mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +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:
144
apps/server/tests/integration/helpers/git-test-repo.ts
Normal file
144
apps/server/tests/integration/helpers/git-test-repo.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Helper for creating test git repositories for integration tests
|
||||
*/
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
import * as os from "os";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
export interface TestRepo {
|
||||
path: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary git repository for testing
|
||||
*/
|
||||
export async function createTestGitRepo(): Promise<TestRepo> {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "automaker-test-"));
|
||||
|
||||
// Initialize git repo
|
||||
await execAsync("git init", { cwd: tmpDir });
|
||||
await execAsync('git config user.email "test@example.com"', { cwd: tmpDir });
|
||||
await execAsync('git config user.name "Test User"', { cwd: tmpDir });
|
||||
|
||||
// Create initial commit
|
||||
await fs.writeFile(path.join(tmpDir, "README.md"), "# Test Project\n");
|
||||
await execAsync("git add .", { cwd: tmpDir });
|
||||
await execAsync('git commit -m "Initial commit"', { cwd: tmpDir });
|
||||
|
||||
// Create main branch explicitly
|
||||
await execAsync("git branch -M main", { cwd: tmpDir });
|
||||
|
||||
return {
|
||||
path: tmpDir,
|
||||
cleanup: async () => {
|
||||
try {
|
||||
// Remove all worktrees first
|
||||
const { stdout } = await execAsync("git worktree list --porcelain", {
|
||||
cwd: tmpDir,
|
||||
}).catch(() => ({ stdout: "" }));
|
||||
|
||||
const worktrees = stdout
|
||||
.split("\n\n")
|
||||
.slice(1) // Skip main worktree
|
||||
.map((block) => {
|
||||
const pathLine = block.split("\n").find((line) => line.startsWith("worktree "));
|
||||
return pathLine ? pathLine.replace("worktree ", "") : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
for (const worktreePath of worktrees) {
|
||||
try {
|
||||
await execAsync(`git worktree remove "${worktreePath}" --force`, {
|
||||
cwd: tmpDir,
|
||||
});
|
||||
} catch (err) {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the repository
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
console.error("Failed to cleanup test repo:", error);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feature file in the test repo
|
||||
*/
|
||||
export async function createTestFeature(
|
||||
repoPath: string,
|
||||
featureId: string,
|
||||
featureData: any
|
||||
): Promise<void> {
|
||||
const featuresDir = path.join(repoPath, ".automaker", "features");
|
||||
const featureDir = path.join(featuresDir, featureId);
|
||||
|
||||
await fs.mkdir(featureDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(featureDir, "feature.json"),
|
||||
JSON.stringify(featureData, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of git branches
|
||||
*/
|
||||
export async function listBranches(repoPath: string): Promise<string[]> {
|
||||
const { stdout } = await execAsync("git branch --list", { cwd: repoPath });
|
||||
return stdout
|
||||
.split("\n")
|
||||
.map((line) => line.trim().replace(/^[*+]\s*/, ""))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of git worktrees
|
||||
*/
|
||||
export async function listWorktrees(repoPath: string): Promise<string[]> {
|
||||
try {
|
||||
const { stdout } = await execAsync("git worktree list --porcelain", {
|
||||
cwd: repoPath,
|
||||
});
|
||||
|
||||
return stdout
|
||||
.split("\n\n")
|
||||
.slice(1) // Skip main worktree
|
||||
.map((block) => {
|
||||
const pathLine = block.split("\n").find((line) => line.startsWith("worktree "));
|
||||
return pathLine ? pathLine.replace("worktree ", "") : null;
|
||||
})
|
||||
.filter(Boolean) as string[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a branch exists
|
||||
*/
|
||||
export async function branchExists(
|
||||
repoPath: string,
|
||||
branchName: string
|
||||
): Promise<boolean> {
|
||||
const branches = await listBranches(repoPath);
|
||||
return branches.includes(branchName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a worktree exists
|
||||
*/
|
||||
export async function worktreeExists(
|
||||
repoPath: string,
|
||||
worktreePath: string
|
||||
): Promise<boolean> {
|
||||
const worktrees = await listWorktrees(repoPath);
|
||||
return worktrees.some((wt) => wt === worktreePath);
|
||||
}
|
||||
Reference in New Issue
Block a user