Files
stack/apps/api/test/fixtures/mock-bullmq.fixture.ts
Jason Woltje 3cdcbf6774 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>
2026-02-01 21:44:04 -06:00

84 lines
2.3 KiB
TypeScript

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);
}),
};
}