Files
automaker/apps/server/tests/integration/helpers/git-test-repo.ts
SuperComboGamer 8d578558ff style: fix formatting with Prettier
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:31:57 -05:00

136 lines
3.8 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);
}