mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
- 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>
145 lines
3.9 KiB
TypeScript
145 lines
3.9 KiB
TypeScript
/**
|
|
* 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);
|
|
}
|