Skip to main content
Glama
build-config-manager.test.ts7.39 kB
import type { Logger } from 'winston'; import { BuildConfigManager, type ManagedBuildConfiguration, } from '@/teamcity/build-config-manager'; import { type MockTeamCityClient, createMockTeamCityClient, } from '../../test-utils/mock-teamcity-client'; describe('BuildConfigManager', () => { let manager: BuildConfigManager; let mockClient: MockTeamCityClient; let logger: Logger; const createBuildType = (overrides: Record<string, unknown> = {}) => ({ id: 'cfg-id', name: 'API Build', projectId: 'Proj_Main', projectName: 'Main Project', description: 'Builds the API', webUrl: 'https://example.com', paused: false, templateFlag: false, template: { id: 'Template_1' }, parameters: { property: [ { name: 'env', value: 'dev' }, { name: 'branch', value: 'main' }, ], }, ['vcs-root-entries']: { 'vcs-root-entry': [{ id: 'VCS_MAIN' }], }, steps: { count: 2 }, triggers: { count: 1 }, ['snapshot-dependencies']: { 'snapshot-dependency': [{ id: 'snap-1' }], }, ['artifact-dependencies']: { 'artifact-dependency': [{ id: 'artifact-1' }], }, ...overrides, }); beforeEach(() => { mockClient = createMockTeamCityClient(); mockClient.resetAllMocks(); logger = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), log: jest.fn(), add: jest.fn(), remove: jest.fn(), child: jest.fn(() => logger), close: jest.fn(), clear: jest.fn(), configure: jest.fn(), level: 'info', levels: {}, format: undefined as never, transports: [], profile: jest.fn(), startTimer: jest.fn(), query: jest.fn(), } as unknown as Logger; manager = new BuildConfigManager(mockClient, logger); }); it('lists configurations with filtering, sorting, and pagination', async () => { const buildTypes = [ createBuildType(), createBuildType({ id: 'cfg-2', name: 'UI Build', projectId: 'Proj_Main', triggers: { count: 0 }, }), createBuildType({ id: 'child-1', name: 'Child API', projectId: 'Proj_Child', ['vcs-root-entries']: { 'vcs-root-entry': [] }, }), ]; mockClient.buildTypes.getAllBuildTypes.mockResolvedValue({ data: { buildType: buildTypes } }); const result = await manager.listConfigurations({ filters: { projectId: 'Proj_Main', templateFlag: false, paused: false, tags: ['release', 'hotfix'], namePattern: 'API*', hasVcsRoot: true, hasTriggers: true, }, sort: { by: 'projectName', order: 'desc' }, pagination: { page: 1, pageSize: 1 }, includeDetails: true, }); expect(mockClient.buildTypes.getAllBuildTypes).toHaveBeenCalledWith( 'affectedProject:(id:Proj_Main),templateFlag:false,paused:false,tag:(release,hotfix)', expect.stringContaining('parameters') ); expect(result.configurations).toHaveLength(1); const config = result.configurations[0]; expect(config).toBeDefined(); if (!config) { throw new Error('expected configuration result'); } expect(config.id).toBe('cfg-id'); expect(config.parameters?.['env']).toBe('dev'); expect(config.dependencies?.snapshot).toEqual(['snap-1']); expect(result.pagination.hasNext).toBe(false); expect(result.pagination.totalCount).toBe(1); }); it('supports wildcard patterns and negative filters', async () => { const buildTypes = [ createBuildType({ name: 'agent-service', triggers: { count: 1 }, ['vcs-root-entries']: { 'vcs-root-entry': [{ id: 'vcs-1' }] }, }), createBuildType({ id: 'cfg-no-vcs', name: 'agent-helper', ['vcs-root-entries']: { 'vcs-root-entry': [] }, triggers: { count: 0 }, }), ]; mockClient.buildTypes.getAllBuildTypes.mockResolvedValue({ data: { buildType: buildTypes } }); const result = await manager.listConfigurations({ filters: { projectId: 'Proj_Main', namePattern: 'agent-*', hasVcsRoot: false, hasTriggers: false, }, sort: { by: 'name', order: 'asc' }, pagination: { page: 1, pageSize: 5 }, }); expect(result.configurations).toHaveLength(1); const config = result.configurations[0]; expect(config).toBeDefined(); if (!config) { throw new Error('expected configuration result'); } expect(config.id).toBe('cfg-no-vcs'); expect(result.pagination.hasNext).toBe(false); expect(result.pagination.totalPages).toBe(1); }); it('fetches project configurations including subprojects', async () => { mockClient.projects.getAllSubprojectsOrdered.mockResolvedValue({ data: { project: [{ id: 'Sub_1' }, { id: 'Sub_2' }] }, }); mockClient.buildTypes.getAllBuildTypes.mockResolvedValue({ data: { buildType: [createBuildType({ projectId: 'Sub_1' })] }, }); const configs = await manager.getProjectConfigurations('Proj_Main', true); expect(mockClient.buildTypes.getAllBuildTypes).toHaveBeenCalledWith( 'affectedProject:(id:Proj_Main,id:Sub_1,id:Sub_2)', expect.any(String) ); expect(configs).toHaveLength(1); }); it('returns empty subproject list on API errors', async () => { mockClient.projects.getAllSubprojectsOrdered.mockRejectedValue(new Error('boom')); const ids = await ( manager as unknown as { getSubprojectIds(projectId: string): Promise<string[]>; } ).getSubprojectIds('root'); expect(ids).toEqual([]); expect(logger.warn).toHaveBeenCalledWith('Failed to get subprojects', { error: expect.any(Error), projectId: 'root', }); }); it('builds template hierarchy including inheritors', async () => { mockClient.buildTypes.getBuildType.mockResolvedValue({ data: createBuildType({ id: 'Template_1', templateFlag: true }), }); const listSpy = jest.spyOn(manager, 'listConfigurations').mockResolvedValue({ configurations: [ { id: 'child-uses-template', name: 'Child API', projectId: 'Proj_Main', projectName: 'Main Project', description: 'child build', webUrl: 'https://example.com', paused: false, templateFlag: false, templateId: 'Template_1', }, { id: 'other', name: 'Another', projectId: 'Proj_Main', projectName: 'Main Project', description: 'another build', webUrl: 'https://example.com', paused: false, templateFlag: false, templateId: 'Template_X', }, ] as ManagedBuildConfiguration[], pagination: { page: 1, pageSize: 50, totalCount: 2, totalPages: 1, hasNext: false, hasPrevious: false, }, }); const result = await manager.getTemplateHierarchy('Template_1'); expect(mockClient.buildTypes.getBuildType).toHaveBeenCalledWith( 'Template_1', expect.any(String) ); expect(result.template.id).toBe('Template_1'); expect(result.inheritors.map((cfg) => cfg.id)).toEqual(['child-uses-template']); listSpy.mockRestore(); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Daghis/teamcity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server