feat: 支持取消 Jenkins 发布执行
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { AppError } from '../common/errors/app-error';
|
||||
import { DeployNotificationService } from '../notifications/wecom/deploy-notification.service';
|
||||
import { JenkinsClient } from '../integrations/jenkins/jenkins.client';
|
||||
import { ProjectSummary } from '../projects/project.types';
|
||||
import { ProjectsService } from '../projects/projects.service';
|
||||
import { DeployExecutionService } from './deploy-execution.service';
|
||||
@@ -66,17 +67,28 @@ describe('DeployRunsService', () => {
|
||||
operator: input.operator,
|
||||
trigger: input.trigger ?? 'manual',
|
||||
})),
|
||||
updateMemoryStatus: jest.fn((id: string, status: DeployRunStatus) =>
|
||||
id === baseRun.id ? { ...baseRun, status } : null,
|
||||
updateRun: jest.fn((id: string, patch: { status?: DeployRunStatus }) =>
|
||||
runs.some((run) => run.id === id)
|
||||
? {
|
||||
...(runs.find((run) => run.id === id) ?? baseRun),
|
||||
...patch,
|
||||
}
|
||||
: null,
|
||||
),
|
||||
updateStep: jest.fn((id: string) =>
|
||||
runs.some((run) => run.id === id)
|
||||
? {
|
||||
...(runs.find((run) => run.id === id) ?? baseRun),
|
||||
status: 'canceled' as const,
|
||||
}
|
||||
: null,
|
||||
),
|
||||
} satisfies Pick<
|
||||
DeployRunRepository,
|
||||
'findMany' | 'findById' | 'create' | 'updateMemoryStatus'
|
||||
'findMany' | 'findById' | 'create' | 'updateRun' | 'updateStep'
|
||||
>;
|
||||
const projects = {
|
||||
getProject: jest.fn(() =>
|
||||
Promise.resolve(resolvedProject),
|
||||
),
|
||||
getProject: jest.fn(() => Promise.resolve(resolvedProject)),
|
||||
} satisfies Pick<ProjectsService, 'getProject'>;
|
||||
const audit = {
|
||||
record: jest.fn(),
|
||||
@@ -98,6 +110,26 @@ describe('DeployRunsService', () => {
|
||||
Promise.resolve({ ...run, status: 'success' as const }),
|
||||
),
|
||||
} satisfies Pick<DeployExecutionService, 'execute' | 'syncJenkinsRun'>;
|
||||
const jenkins = {
|
||||
healthSummary: jest.fn(() => ({ status: 'ok' as const })),
|
||||
cancelQueueItem: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
target: 'queue' as const,
|
||||
queueId: 'queue_001',
|
||||
requested: true as const,
|
||||
}),
|
||||
),
|
||||
stopBuild: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
target: 'build' as const,
|
||||
buildNumber: 7,
|
||||
requested: true as const,
|
||||
}),
|
||||
),
|
||||
} satisfies Pick<
|
||||
JenkinsClient,
|
||||
'healthSummary' | 'cancelQueueItem' | 'stopBuild'
|
||||
>;
|
||||
|
||||
return {
|
||||
audit,
|
||||
@@ -111,6 +143,7 @@ describe('DeployRunsService', () => {
|
||||
audit as unknown as AuditService,
|
||||
notification as unknown as DeployNotificationService,
|
||||
execution as unknown as DeployExecutionService,
|
||||
jenkins as unknown as JenkinsClient,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -262,17 +295,63 @@ describe('DeployRunsService', () => {
|
||||
expect(execution.execute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cancels an existing deploy run', async () => {
|
||||
const { audit, service } = createService();
|
||||
it('cancels a queued Jenkins execution before marking the run canceled', async () => {
|
||||
const queuedRun: DeployRunSummary = {
|
||||
...baseRun,
|
||||
status: 'queued',
|
||||
jenkinsQueueId: 'queue_001',
|
||||
};
|
||||
const { audit, jenkins, service } = createService({
|
||||
runs: [queuedRun],
|
||||
});
|
||||
|
||||
const run = await service.cancelRun(baseRun.id);
|
||||
|
||||
expect(run.status).toBe('canceled');
|
||||
expect(jenkins.cancelQueueItem).toHaveBeenCalledWith('queue_001');
|
||||
expect(jenkins.stopBuild).not.toHaveBeenCalled();
|
||||
expect(audit.record).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ action: 'DEPLOY_CANCELED' }),
|
||||
);
|
||||
});
|
||||
|
||||
it('cancels a running Jenkins build before marking the run canceled', async () => {
|
||||
const runningRun: DeployRunSummary = {
|
||||
...baseRun,
|
||||
status: 'running',
|
||||
jenkinsBuildNumber: 7,
|
||||
};
|
||||
const { jenkins, service } = createService({
|
||||
runs: [runningRun],
|
||||
});
|
||||
|
||||
const run = await service.cancelRun(baseRun.id);
|
||||
|
||||
expect(run.status).toBe('canceled');
|
||||
expect(jenkins.stopBuild).toHaveBeenCalledWith(
|
||||
'my-project-dev/test-access-manage-develop',
|
||||
7,
|
||||
);
|
||||
expect(jenkins.cancelQueueItem).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects already terminal deploy runs before calling Jenkins', async () => {
|
||||
const canceledRun: DeployRunSummary = {
|
||||
...baseRun,
|
||||
status: 'canceled',
|
||||
jenkinsBuildNumber: 7,
|
||||
};
|
||||
const { jenkins, service } = createService({
|
||||
runs: [canceledRun],
|
||||
});
|
||||
|
||||
await expect(service.cancelRun(baseRun.id)).rejects.toBeInstanceOf(
|
||||
AppError,
|
||||
);
|
||||
expect(jenkins.stopBuild).not.toHaveBeenCalled();
|
||||
expect(jenkins.cancelQueueItem).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('retries a deploy run with retry trigger', async () => {
|
||||
const { repository, service } = createService();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user