Skip to main content
Glama
index.ts16.5 kB
/** * Type-safe fixture builders for TeamCity API testing * * Provides properly typed factory functions that create valid API response * objects without requiring `as unknown as` casts. * * @example * ```typescript * const project = createProjectFixture({ name: 'My Project' }); * const build = createBuildFixture({ status: 'FAILURE' }); * ``` */ import type { TeamCityBuildResponse, TeamCityBuildTypeResponse, TeamCityBuildTypesResponse, TeamCityBuildsResponse, TeamCityProjectResponse, TeamCityProjectsResponse, TeamCityProperties, TeamCityProperty, TeamCityStepResponse, TeamCityStepsResponse, TeamCityTriggerResponse, TeamCityTriggersResponse, TeamCityVcsRootEntriesResponse, TeamCityVcsRootEntry, } from '@/teamcity/api-types'; /** * Deep partial type for nested object overrides */ export type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]>; } : T; // ============================================================================ // Property Fixtures // ============================================================================ /** * Create a TeamCity property */ export function createPropertyFixture(overrides: Partial<TeamCityProperty> = {}): TeamCityProperty { return { name: 'property-name', value: 'property-value', ...overrides, }; } /** * Create a TeamCity properties collection */ export function createPropertiesFixture(properties: TeamCityProperty[] = []): TeamCityProperties { return { count: properties.length, property: properties.length === 0 ? undefined : properties, }; } // ============================================================================ // Project Fixtures // ============================================================================ let projectCounter = 0; /** * Create a TeamCity project response * * @example * ```typescript * const project = createProjectFixture(); * // { id: 'TestProject_1', name: 'Test Project 1', ... } * * const customProject = createProjectFixture({ * id: 'MyProject', * name: 'My Custom Project', * archived: true, * }); * ``` */ export function createProjectFixture( overrides: DeepPartial<TeamCityProjectResponse> = {} ): TeamCityProjectResponse { projectCounter++; const id = overrides.id ?? `TestProject_${projectCounter}`; const name = overrides.name ?? `Test Project ${projectCounter}`; return { id, name, parentProjectId: '_Root', href: `/app/rest/projects/id:${id}`, webUrl: `https://teamcity.example.com/project/${id}`, ...overrides, } as TeamCityProjectResponse; } /** * Create a TeamCity projects collection response */ export function createProjectsFixture( projects: TeamCityProjectResponse[] = [] ): TeamCityProjectsResponse { return { count: projects.length, project: projects.length === 0 ? undefined : projects, }; } // ============================================================================ // Build Type Fixtures // ============================================================================ let buildTypeCounter = 0; /** * Create a TeamCity build type (configuration) response * * @example * ```typescript * const buildType = createBuildTypeFixture(); * // { id: 'TestProject_Build_1', name: 'Build 1', projectId: 'TestProject', ... } * * const customBuildType = createBuildTypeFixture({ * id: 'MyProject_Deploy', * name: 'Deploy to Production', * paused: true, * }); * ``` */ export function createBuildTypeFixture( overrides: DeepPartial<TeamCityBuildTypeResponse> = {} ): TeamCityBuildTypeResponse { buildTypeCounter++; const projectId = overrides.projectId ?? 'TestProject'; const id = overrides.id ?? `${projectId}_Build_${buildTypeCounter}`; const name = overrides.name ?? `Build ${buildTypeCounter}`; return { id, name, projectId, projectName: overrides.projectName ?? 'Test Project', webUrl: `https://teamcity.example.com/buildConfiguration/${id}`, paused: false, ...overrides, } as TeamCityBuildTypeResponse; } /** * Create a TeamCity build types collection response */ export function createBuildTypesFixture( buildTypes: TeamCityBuildTypeResponse[] = [] ): TeamCityBuildTypesResponse { return { count: buildTypes.length, buildType: buildTypes.length === 0 ? undefined : buildTypes, }; } // ============================================================================ // Build Fixtures // ============================================================================ let buildCounter = 0; /** * Create a TeamCity build response * * @example * ```typescript * const build = createBuildFixture(); * // { id: 1, buildTypeId: 'TestProject_Build', status: 'SUCCESS', state: 'finished', ... } * * const failedBuild = createBuildFixture({ * status: 'FAILURE', * statusText: 'Tests failed', * }); * * const runningBuild = createBuildFixture({ * state: 'running', * percentageComplete: 45, * }); * ``` */ export function createBuildFixture( overrides: DeepPartial<TeamCityBuildResponse> = {} ): TeamCityBuildResponse { buildCounter++; const id = overrides.id ?? buildCounter; const buildTypeId = overrides.buildTypeId ?? 'TestProject_Build'; return { id, buildTypeId, number: overrides.number ?? `${id}`, status: 'SUCCESS', state: 'finished', branchName: 'main', defaultBranch: true, href: `/app/rest/builds/id:${id}`, webUrl: `https://teamcity.example.com/viewLog.html?buildId=${id}`, ...overrides, } as TeamCityBuildResponse; } /** * Create a TeamCity builds collection response */ export function createBuildsFixture(builds: TeamCityBuildResponse[] = []): TeamCityBuildsResponse { return { count: builds.length, build: builds.length === 0 ? undefined : builds, }; } /** * Create a running build fixture */ export function createRunningBuildFixture( overrides: DeepPartial<TeamCityBuildResponse> = {} ): TeamCityBuildResponse { return createBuildFixture({ state: 'running', running: true, percentageComplete: overrides.percentageComplete ?? 50, status: undefined, // Running builds don't have final status yet ...overrides, }); } /** * Create a queued build fixture */ export function createQueuedBuildFixture( overrides: DeepPartial<TeamCityBuildResponse> = {} ): TeamCityBuildResponse { return createBuildFixture({ state: 'queued', status: undefined, queuedDate: new Date().toISOString(), ...overrides, }); } /** * Create a failed build fixture */ export function createFailedBuildFixture( overrides: DeepPartial<TeamCityBuildResponse> = {} ): TeamCityBuildResponse { return createBuildFixture({ status: 'FAILURE', statusText: overrides.statusText ?? 'Tests failed: 3 tests', ...overrides, }); } // ============================================================================ // Trigger Fixtures // ============================================================================ let triggerCounter = 0; /** * Create a TeamCity trigger response */ export function createTriggerFixture( overrides: DeepPartial<TeamCityTriggerResponse> = {} ): TeamCityTriggerResponse { triggerCounter++; return { id: overrides.id ?? `TRIGGER_${triggerCounter}`, type: overrides.type ?? 'vcsTrigger', disabled: false, inherited: false, ...overrides, } as TeamCityTriggerResponse; } /** * Create a TeamCity triggers collection response */ export function createTriggersFixture( triggers: TeamCityTriggerResponse[] = [] ): TeamCityTriggersResponse { return { count: triggers.length, trigger: triggers.length === 0 ? undefined : triggers, }; } /** * Create a VCS trigger fixture */ export function createVcsTriggerFixture( overrides: DeepPartial<TeamCityTriggerResponse> = {} ): TeamCityTriggerResponse { return createTriggerFixture({ type: 'vcsTrigger', properties: createPropertiesFixture([ createPropertyFixture({ name: 'quietPeriodMode', value: 'DO_NOT_USE' }), ]), ...overrides, }); } /** * Create a scheduled trigger fixture */ export function createScheduledTriggerFixture( overrides: DeepPartial<TeamCityTriggerResponse> = {} ): TeamCityTriggerResponse { return createTriggerFixture({ type: 'schedulingTrigger', properties: createPropertiesFixture([ createPropertyFixture({ name: 'schedulingPolicy', value: 'daily' }), createPropertyFixture({ name: 'hour', value: '3' }), createPropertyFixture({ name: 'minute', value: '0' }), ]), ...overrides, }); } // ============================================================================ // Step Fixtures // ============================================================================ let stepCounter = 0; /** * Create a TeamCity step response */ export function createStepFixture( overrides: DeepPartial<TeamCityStepResponse> = {} ): TeamCityStepResponse { stepCounter++; return { id: overrides.id ?? `STEP_${stepCounter}`, name: overrides.name ?? `Step ${stepCounter}`, type: overrides.type ?? 'simpleRunner', disabled: false, inherited: false, ...overrides, } as TeamCityStepResponse; } /** * Create a TeamCity steps collection response */ export function createStepsFixture(steps: TeamCityStepResponse[] = []): TeamCityStepsResponse { return { count: steps.length, step: steps.length === 0 ? undefined : steps, }; } /** * Create a command line step fixture */ export function createCommandLineStepFixture( command: string, overrides: DeepPartial<TeamCityStepResponse> = {} ): TeamCityStepResponse { return createStepFixture({ type: 'simpleRunner', properties: createPropertiesFixture([ createPropertyFixture({ name: 'script.content', value: command }), createPropertyFixture({ name: 'use.custom.script', value: 'true' }), ]), ...overrides, }); } /** * Create a Gradle step fixture */ export function createGradleStepFixture( tasks: string = 'build', overrides: DeepPartial<TeamCityStepResponse> = {} ): TeamCityStepResponse { return createStepFixture({ type: 'gradle-runner', properties: createPropertiesFixture([ createPropertyFixture({ name: 'ui.gradleRunner.gradle.tasks.names', value: tasks }), ]), ...overrides, }); } // ============================================================================ // VCS Root Fixtures // ============================================================================ let vcsRootCounter = 0; /** * Create a TeamCity VCS root entry */ export function createVcsRootEntryFixture( overrides: DeepPartial<TeamCityVcsRootEntry> = {} ): TeamCityVcsRootEntry { vcsRootCounter++; const id = overrides.id ?? `VcsRoot_${vcsRootCounter}`; return { id, inherited: false, 'checkout-rules': '+:.', 'vcs-root': { id, name: `VCS Root ${vcsRootCounter}`, href: `/app/rest/vcs-roots/id:${id}`, ...(overrides['vcs-root'] ?? {}), }, ...overrides, } as TeamCityVcsRootEntry; } /** * Create a TeamCity VCS root entries collection response */ export function createVcsRootEntriesFixture( entries: TeamCityVcsRootEntry[] = [] ): TeamCityVcsRootEntriesResponse { return { count: entries.length, 'vcs-root-entry': entries.length === 0 ? undefined : entries, }; } // ============================================================================ // Agent Fixtures // ============================================================================ /** * TeamCity agent response (simplified for testing) */ export interface TeamCityAgentFixture { id: number; name: string; typeId?: number; connected?: boolean; enabled?: boolean; authorized?: boolean; uptodate?: boolean; ip?: string; pool?: { id: number; name: string; href?: string; }; } let agentCounter = 0; /** * Create a TeamCity agent fixture */ export function createAgentFixture( overrides: Partial<TeamCityAgentFixture> = {} ): TeamCityAgentFixture { agentCounter++; return { id: overrides.id ?? agentCounter, name: overrides.name ?? `Agent ${agentCounter}`, typeId: overrides.typeId ?? 1, connected: overrides.connected ?? true, enabled: overrides.enabled ?? true, authorized: overrides.authorized ?? true, uptodate: overrides.uptodate ?? true, ip: overrides.ip ?? '192.168.1.100', pool: overrides.pool ?? { id: 0, name: 'Default', href: '/app/rest/agentPools/id:0', }, ...overrides, }; } /** * Create an agents collection fixture */ export function createAgentsFixture(agents: TeamCityAgentFixture[] = []): { count: number; agent?: TeamCityAgentFixture[]; } { return { count: agents.length, agent: agents.length === 0 ? undefined : agents, }; } // ============================================================================ // Test Occurrence Fixtures // ============================================================================ /** * TeamCity test occurrence response (simplified for testing) */ export interface TeamCityTestOccurrenceFixture { id: string; name: string; status: 'SUCCESS' | 'FAILURE' | 'IGNORED' | 'UNKNOWN'; duration?: number; details?: string; currentlyMuted?: boolean; currentlyInvestigated?: boolean; test?: { id: string; name: string; }; build?: { id: number; buildTypeId: string; }; } let testCounter = 0; /** * Create a TeamCity test occurrence fixture */ export function createTestOccurrenceFixture( overrides: Partial<TeamCityTestOccurrenceFixture> = {} ): TeamCityTestOccurrenceFixture { testCounter++; const id = overrides.id ?? `test_${testCounter}`; return { id, name: overrides.name ?? `com.example.Test${testCounter}.testMethod`, status: overrides.status ?? 'SUCCESS', duration: overrides.duration ?? 100, ...overrides, }; } /** * Create a failed test occurrence fixture */ export function createFailedTestFixture( overrides: Partial<TeamCityTestOccurrenceFixture> = {} ): TeamCityTestOccurrenceFixture { return createTestOccurrenceFixture({ status: 'FAILURE', details: overrides.details ?? 'Expected true but was false', ...overrides, }); } /** * Create a test occurrences collection fixture */ export function createTestOccurrencesFixture(tests: TeamCityTestOccurrenceFixture[] = []): { count: number; testOccurrence?: TeamCityTestOccurrenceFixture[]; } { return { count: tests.length, testOccurrence: tests.length === 0 ? undefined : tests, }; } // ============================================================================ // Problem Occurrence Fixtures // ============================================================================ /** * TeamCity problem occurrence response (simplified for testing) */ export interface TeamCityProblemOccurrenceFixture { id: string; type: string; identity: string; details?: string; additionalData?: string; build?: { id: number; buildTypeId: string; }; problem?: { id: string; type: string; identity: string; }; } let problemCounter = 0; /** * Create a TeamCity problem occurrence fixture */ export function createProblemOccurrenceFixture( overrides: Partial<TeamCityProblemOccurrenceFixture> = {} ): TeamCityProblemOccurrenceFixture { problemCounter++; const id = overrides.id ?? `problem_${problemCounter}`; return { id, type: overrides.type ?? 'TC_COMPILATION_ERROR', identity: overrides.identity ?? `Compilation error in Module${problemCounter}`, details: overrides.details ?? 'Syntax error at line 42', ...overrides, }; } /** * Create a problem occurrences collection fixture */ export function createProblemOccurrencesFixture( problems: TeamCityProblemOccurrenceFixture[] = [] ): { count: number; problemOccurrence?: TeamCityProblemOccurrenceFixture[] } { return { count: problems.length, problemOccurrence: problems.length === 0 ? undefined : problems, }; } // ============================================================================ // Reset Counters // ============================================================================ /** * Reset all fixture counters (useful between tests) */ export function resetFixtureCounters(): void { projectCounter = 0; buildTypeCounter = 0; buildCounter = 0; triggerCounter = 0; stepCounter = 0; vcsRootCounter = 0; agentCounter = 0; testCounter = 0; problemCounter = 0; }

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