mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
feat: add comprehensive integration tests for auto-mode-service
- Created git-test-repo helper for managing test git repositories - Added 13 integration tests covering: - Worktree operations (create, error handling, non-worktree mode) - Feature execution (status updates, model selection, duplicate prevention) - Auto loop (start/stop, pending features, max concurrency, events) - Error handling (provider errors, continue after failures) - Integration tests use real git operations with temporary repos - All 416 tests passing with 72.65% overall coverage - Service coverage improved: agent-service 58%, auto-mode-service 44%, feature-loader 66% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
38
apps/server/tests/utils/helpers.ts
Normal file
38
apps/server/tests/utils/helpers.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Test helper functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collect all values from an async generator
|
||||
*/
|
||||
export async function collectAsyncGenerator<T>(gen: AsyncGenerator<T>): Promise<T[]> {
|
||||
const results: T[] = [];
|
||||
for await (const item of gen) {
|
||||
results.push(item);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a condition to be true
|
||||
*/
|
||||
export async function waitFor(
|
||||
condition: () => boolean,
|
||||
timeout = 1000,
|
||||
interval = 10
|
||||
): Promise<void> {
|
||||
const start = Date.now();
|
||||
while (!condition()) {
|
||||
if (Date.now() - start > timeout) {
|
||||
throw new Error("Timeout waiting for condition");
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, interval));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary directory for tests
|
||||
*/
|
||||
export function createTempDir(): string {
|
||||
return `/tmp/test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
107
apps/server/tests/utils/mocks.ts
Normal file
107
apps/server/tests/utils/mocks.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Mock utilities for testing
|
||||
* Provides reusable mocks for common dependencies
|
||||
*/
|
||||
|
||||
import { vi } from "vitest";
|
||||
import type { ChildProcess } from "child_process";
|
||||
import { EventEmitter } from "events";
|
||||
import type { Readable } from "stream";
|
||||
|
||||
/**
|
||||
* Mock child_process.spawn for subprocess tests
|
||||
*/
|
||||
export function createMockChildProcess(options: {
|
||||
stdout?: string[];
|
||||
stderr?: string[];
|
||||
exitCode?: number | null;
|
||||
shouldError?: boolean;
|
||||
}): ChildProcess {
|
||||
const { stdout = [], stderr = [], exitCode = 0, shouldError = false } = options;
|
||||
|
||||
const mockProcess = new EventEmitter() as any;
|
||||
|
||||
// Create mock stdout stream
|
||||
mockProcess.stdout = new EventEmitter() as Readable;
|
||||
mockProcess.stderr = new EventEmitter() as Readable;
|
||||
|
||||
mockProcess.kill = vi.fn();
|
||||
|
||||
// Simulate async output
|
||||
process.nextTick(() => {
|
||||
// Emit stdout lines
|
||||
for (const line of stdout) {
|
||||
mockProcess.stdout.emit("data", Buffer.from(line + "\n"));
|
||||
}
|
||||
|
||||
// Emit stderr lines
|
||||
for (const line of stderr) {
|
||||
mockProcess.stderr.emit("data", Buffer.from(line + "\n"));
|
||||
}
|
||||
|
||||
// Emit exit or error
|
||||
if (shouldError) {
|
||||
mockProcess.emit("error", new Error("Process error"));
|
||||
} else {
|
||||
mockProcess.emit("exit", exitCode);
|
||||
}
|
||||
});
|
||||
|
||||
return mockProcess as ChildProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock fs/promises for file system tests
|
||||
*/
|
||||
export function createMockFs() {
|
||||
return {
|
||||
readFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
mkdir: vi.fn(),
|
||||
access: vi.fn(),
|
||||
stat: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock Express request/response/next for middleware tests
|
||||
*/
|
||||
export function createMockExpressContext() {
|
||||
const req = {
|
||||
headers: {},
|
||||
body: {},
|
||||
params: {},
|
||||
query: {},
|
||||
} as any;
|
||||
|
||||
const res = {
|
||||
status: vi.fn().mockReturnThis(),
|
||||
json: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
} as any;
|
||||
|
||||
const next = vi.fn();
|
||||
|
||||
return { req, res, next };
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock AbortController for async operation tests
|
||||
*/
|
||||
export function createMockAbortController() {
|
||||
const controller = new AbortController();
|
||||
const originalAbort = controller.abort.bind(controller);
|
||||
controller.abort = vi.fn(originalAbort);
|
||||
return controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock Claude SDK query function
|
||||
*/
|
||||
export function createMockClaudeQuery(messages: any[] = []) {
|
||||
return vi.fn(async function* ({ prompt, options }: any) {
|
||||
for (const msg of messages) {
|
||||
yield msg;
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user