feat(#175): Implement E2E test harness
- Create comprehensive E2E test suite for job orchestration - Add test fixtures for Discord, BullMQ, and Prisma mocks - Implement 9 end-to-end test scenarios covering: * Happy path: webhook → job → step execution → completion * Event emission throughout job lifecycle * Step failure and retry handling * Job failure after max retries * Discord command parsing and job creation * WebSocket status updates integration * Job cancellation workflow * Job retry mechanism * Progress percentage tracking - Add helper methods to services for simplified testing: * JobStepsService: start(), complete(), fail(), findByJob() * RunnerJobsService: updateStatus(), updateProgress() * JobEventsService: findByJob() - Configure vitest.e2e.config.ts for E2E test execution - All 9 E2E tests passing - All 1405 unit tests passing - Quality gates: typecheck, lint, build all passing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
83
apps/api/test/fixtures/mock-bullmq.fixture.ts
vendored
Normal file
83
apps/api/test/fixtures/mock-bullmq.fixture.ts
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { vi } from "vitest";
|
||||
import type { Queue, Job } from "bullmq";
|
||||
|
||||
/**
|
||||
* Mock BullMQ job for testing
|
||||
*/
|
||||
export function createMockBullMqJob(overrides?: Partial<Job>): Partial<Job> {
|
||||
return {
|
||||
id: "mock-bull-job-id",
|
||||
name: "runner-job",
|
||||
data: {
|
||||
jobId: "mock-job-id",
|
||||
workspaceId: "mock-workspace-id",
|
||||
type: "code-task",
|
||||
},
|
||||
progress: vi.fn().mockReturnValue(0),
|
||||
updateProgress: vi.fn().mockResolvedValue(undefined),
|
||||
log: vi.fn().mockResolvedValue(undefined),
|
||||
remove: vi.fn().mockResolvedValue(undefined),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock BullMQ queue for testing
|
||||
*/
|
||||
export function createMockBullMqQueue(): Partial<Queue> {
|
||||
const jobs = new Map<string, Partial<Job>>();
|
||||
|
||||
return {
|
||||
add: vi.fn().mockImplementation((name: string, data: unknown) => {
|
||||
const job = createMockBullMqJob({
|
||||
id: `job-${Date.now()}`,
|
||||
name,
|
||||
data: data as never,
|
||||
});
|
||||
jobs.set(job.id as string, job);
|
||||
return Promise.resolve(job);
|
||||
}),
|
||||
getJob: vi.fn().mockImplementation((jobId: string) => {
|
||||
return Promise.resolve(jobs.get(jobId) || null);
|
||||
}),
|
||||
getJobs: vi.fn().mockResolvedValue([]),
|
||||
pause: vi.fn().mockResolvedValue(undefined),
|
||||
resume: vi.fn().mockResolvedValue(undefined),
|
||||
clean: vi.fn().mockResolvedValue([]),
|
||||
close: vi.fn().mockResolvedValue(undefined),
|
||||
on: vi.fn(),
|
||||
once: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock BullMQ service for testing
|
||||
*/
|
||||
export function createMockBullMqService() {
|
||||
const queues = new Map<string, Partial<Queue>>();
|
||||
|
||||
return {
|
||||
addJob: vi
|
||||
.fn()
|
||||
.mockImplementation((queueName: string, jobName: string, data: unknown, opts?: unknown) => {
|
||||
let queue = queues.get(queueName);
|
||||
if (!queue) {
|
||||
queue = createMockBullMqQueue();
|
||||
queues.set(queueName, queue);
|
||||
}
|
||||
return queue.add?.(jobName, data, opts as never);
|
||||
}),
|
||||
getQueue: vi.fn().mockImplementation((queueName: string) => {
|
||||
let queue = queues.get(queueName);
|
||||
if (!queue) {
|
||||
queue = createMockBullMqQueue();
|
||||
queues.set(queueName, queue);
|
||||
}
|
||||
return queue;
|
||||
}),
|
||||
getJob: vi.fn().mockImplementation((queueName: string, jobId: string) => {
|
||||
const queue = queues.get(queueName);
|
||||
return queue?.getJob?.(jobId);
|
||||
}),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user