Merge branch 'main' into refactor/frontend

This commit is contained in:
Cody Seibert
2025-12-19 14:42:31 -05:00
24 changed files with 1205 additions and 879 deletions

View File

@@ -0,0 +1,69 @@
import { describe, it, expect, vi, afterEach } from "vitest";
import { createCreateHandler } from "@/routes/worktree/routes/create.js";
import { AUTOMAKER_INITIAL_COMMIT_MESSAGE } from "@/routes/worktree/common.js";
import { exec } from "child_process";
import { promisify } from "util";
import * as fs from "fs/promises";
import * as os from "os";
import * as path from "path";
const execAsync = promisify(exec);
describe("worktree create route - repositories without commits", () => {
let repoPath: string | null = null;
async function initRepoWithoutCommit() {
repoPath = await fs.mkdtemp(
path.join(os.tmpdir(), "automaker-no-commit-")
);
await execAsync("git init", { cwd: repoPath });
await execAsync('git config user.email "test@example.com"', {
cwd: repoPath,
});
await execAsync('git config user.name "Test User"', { cwd: repoPath });
// Intentionally skip creating an initial commit
}
afterEach(async () => {
if (!repoPath) {
return;
}
await fs.rm(repoPath, { recursive: true, force: true });
repoPath = null;
});
it("creates an initial commit before adding a worktree when HEAD is missing", async () => {
await initRepoWithoutCommit();
const handler = createCreateHandler();
const json = vi.fn();
const status = vi.fn().mockReturnThis();
const req = {
body: { projectPath: repoPath, branchName: "feature/no-head" },
} as any;
const res = {
json,
status,
} as any;
await handler(req, res);
expect(status).not.toHaveBeenCalled();
expect(json).toHaveBeenCalled();
const payload = json.mock.calls[0][0];
expect(payload.success).toBe(true);
const { stdout: commitCount } = await execAsync(
"git rev-list --count HEAD",
{ cwd: repoPath! }
);
expect(Number(commitCount.trim())).toBeGreaterThan(0);
const { stdout: latestMessage } = await execAsync(
"git log -1 --pretty=%B",
{ cwd: repoPath! }
);
expect(latestMessage.trim()).toBe(AUTOMAKER_INITIAL_COMMIT_MESSAGE);
});
});

View File

@@ -13,6 +13,10 @@ import {
} from "../helpers/git-test-repo.js";
import * as fs from "fs/promises";
import * as path from "path";
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
vi.mock("@/providers/provider-factory.js");
@@ -43,13 +47,24 @@ describe("auto-mode-service.ts (integration)", () => {
});
describe("worktree operations", () => {
it("should create git worktree for feature", async () => {
// Create a test feature
it("should use existing git worktree for feature", async () => {
const branchName = "feature/test-feature-1";
// Create a test feature with branchName set
await createTestFeature(testRepo.path, "test-feature-1", {
id: "test-feature-1",
category: "test",
description: "Test feature",
status: "pending",
branchName: branchName,
});
// Create worktree before executing (worktrees are now created when features are added/edited)
const worktreesDir = path.join(testRepo.path, ".worktrees");
const worktreePath = path.join(worktreesDir, "test-feature-1");
await fs.mkdir(worktreesDir, { recursive: true });
await execAsync(`git worktree add -b ${branchName} "${worktreePath}" HEAD`, {
cwd: testRepo.path,
});
// Mock provider to complete quickly
@@ -82,9 +97,20 @@ describe("auto-mode-service.ts (integration)", () => {
false // isAutoMode
);
// Verify branch was created
// Verify branch exists (was created when worktree was created)
const branches = await listBranches(testRepo.path);
expect(branches).toContain("feature/test-feature-1");
expect(branches).toContain(branchName);
// Verify worktree exists and is being used
// The service should have found and used the worktree (check via logs)
// We can verify the worktree exists by checking git worktree list
const worktrees = await listWorktrees(testRepo.path);
expect(worktrees.length).toBeGreaterThan(0);
// Verify that at least one worktree path contains our feature ID
const worktreePathsMatch = worktrees.some(wt =>
wt.includes("test-feature-1") || wt.includes(".worktrees")
);
expect(worktreePathsMatch).toBe(true);
// Note: Worktrees are not automatically cleaned up by the service
// This is expected behavior - manual cleanup is required