mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
- Add comprehensive unit tests for SettingsService, covering global and project settings management, including creation, updates, and merging with defaults. - Implement tests for handling credentials, ensuring proper masking and merging of API keys. - Introduce tests for migration from localStorage, validating successful data transfer and error handling. - Enhance error handling in subprocess management tests, ensuring robust timeout and output reading scenarios.
395 lines
13 KiB
TypeScript
395 lines
13 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import {
|
|
readWorktreeMetadata,
|
|
writeWorktreeMetadata,
|
|
updateWorktreePRInfo,
|
|
getWorktreePRInfo,
|
|
readAllWorktreeMetadata,
|
|
deleteWorktreeMetadata,
|
|
type WorktreeMetadata,
|
|
type WorktreePRInfo,
|
|
} from "@/lib/worktree-metadata.js";
|
|
import fs from "fs/promises";
|
|
import path from "path";
|
|
import os from "os";
|
|
|
|
describe("worktree-metadata.ts", () => {
|
|
let testProjectPath: string;
|
|
|
|
beforeEach(async () => {
|
|
testProjectPath = path.join(os.tmpdir(), `worktree-metadata-test-${Date.now()}`);
|
|
await fs.mkdir(testProjectPath, { recursive: true });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
try {
|
|
await fs.rm(testProjectPath, { recursive: true, force: true });
|
|
} catch {
|
|
// Ignore cleanup errors
|
|
}
|
|
});
|
|
|
|
describe("sanitizeBranchName", () => {
|
|
// Test through readWorktreeMetadata and writeWorktreeMetadata
|
|
it("should sanitize branch names with invalid characters", async () => {
|
|
const branch = "feature/test-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should sanitize branch names with Windows invalid characters", async () => {
|
|
const branch = "feature:test*branch?";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should sanitize Windows reserved names", async () => {
|
|
const branch = "CON";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should handle empty branch name", async () => {
|
|
const branch = "";
|
|
const metadata: WorktreeMetadata = {
|
|
branch: "branch",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
// Empty branch name should be sanitized to "_branch"
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should handle branch name that becomes empty after sanitization", async () => {
|
|
// Test branch that would become empty after removing invalid chars
|
|
const branch = "///";
|
|
const metadata: WorktreeMetadata = {
|
|
branch: "branch",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
});
|
|
|
|
describe("readWorktreeMetadata", () => {
|
|
it("should return null when metadata file doesn't exist", async () => {
|
|
const result = await readWorktreeMetadata(testProjectPath, "nonexistent-branch");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should read existing metadata", async () => {
|
|
const branch = "test-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should read metadata with PR info", async () => {
|
|
const branch = "pr-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
pr: {
|
|
number: 123,
|
|
url: "https://github.com/owner/repo/pull/123",
|
|
title: "Test PR",
|
|
state: "open",
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
});
|
|
|
|
describe("writeWorktreeMetadata", () => {
|
|
it("should create metadata directory if it doesn't exist", async () => {
|
|
const branch = "new-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata);
|
|
});
|
|
|
|
it("should overwrite existing metadata", async () => {
|
|
const branch = "existing-branch";
|
|
const metadata1: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
const metadata2: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
pr: {
|
|
number: 456,
|
|
url: "https://github.com/owner/repo/pull/456",
|
|
title: "Updated PR",
|
|
state: "closed",
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata1);
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata2);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toEqual(metadata2);
|
|
});
|
|
});
|
|
|
|
describe("updateWorktreePRInfo", () => {
|
|
it("should create new metadata if it doesn't exist", async () => {
|
|
const branch = "new-pr-branch";
|
|
const prInfo: WorktreePRInfo = {
|
|
number: 789,
|
|
url: "https://github.com/owner/repo/pull/789",
|
|
title: "New PR",
|
|
state: "open",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await updateWorktreePRInfo(testProjectPath, branch, prInfo);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).not.toBeNull();
|
|
expect(result?.branch).toBe(branch);
|
|
expect(result?.pr).toEqual(prInfo);
|
|
});
|
|
|
|
it("should update existing metadata with PR info", async () => {
|
|
const branch = "existing-pr-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
|
|
const prInfo: WorktreePRInfo = {
|
|
number: 999,
|
|
url: "https://github.com/owner/repo/pull/999",
|
|
title: "Updated PR",
|
|
state: "merged",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await updateWorktreePRInfo(testProjectPath, branch, prInfo);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result?.pr).toEqual(prInfo);
|
|
});
|
|
|
|
it("should preserve existing metadata when updating PR info", async () => {
|
|
const branch = "preserve-branch";
|
|
const originalCreatedAt = new Date().toISOString();
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: originalCreatedAt,
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
|
|
const prInfo: WorktreePRInfo = {
|
|
number: 111,
|
|
url: "https://github.com/owner/repo/pull/111",
|
|
title: "PR",
|
|
state: "open",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await updateWorktreePRInfo(testProjectPath, branch, prInfo);
|
|
const result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result?.createdAt).toBe(originalCreatedAt);
|
|
expect(result?.pr).toEqual(prInfo);
|
|
});
|
|
});
|
|
|
|
describe("getWorktreePRInfo", () => {
|
|
it("should return null when metadata doesn't exist", async () => {
|
|
const result = await getWorktreePRInfo(testProjectPath, "nonexistent");
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null when metadata exists but has no PR info", async () => {
|
|
const branch = "no-pr-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
const result = await getWorktreePRInfo(testProjectPath, branch);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return PR info when it exists", async () => {
|
|
const branch = "has-pr-branch";
|
|
const prInfo: WorktreePRInfo = {
|
|
number: 222,
|
|
url: "https://github.com/owner/repo/pull/222",
|
|
title: "Has PR",
|
|
state: "open",
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await updateWorktreePRInfo(testProjectPath, branch, prInfo);
|
|
const result = await getWorktreePRInfo(testProjectPath, branch);
|
|
expect(result).toEqual(prInfo);
|
|
});
|
|
});
|
|
|
|
describe("readAllWorktreeMetadata", () => {
|
|
it("should return empty map when worktrees directory doesn't exist", async () => {
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(0);
|
|
});
|
|
|
|
it("should return empty map when worktrees directory is empty", async () => {
|
|
const worktreesDir = path.join(testProjectPath, ".automaker", "worktrees");
|
|
await fs.mkdir(worktreesDir, { recursive: true });
|
|
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(0);
|
|
});
|
|
|
|
it("should read all worktree metadata", async () => {
|
|
const branch1 = "branch-1";
|
|
const branch2 = "branch-2";
|
|
const metadata1: WorktreeMetadata = {
|
|
branch: branch1,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
const metadata2: WorktreeMetadata = {
|
|
branch: branch2,
|
|
createdAt: new Date().toISOString(),
|
|
pr: {
|
|
number: 333,
|
|
url: "https://github.com/owner/repo/pull/333",
|
|
title: "PR 3",
|
|
state: "open",
|
|
createdAt: new Date().toISOString(),
|
|
},
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch1, metadata1);
|
|
await writeWorktreeMetadata(testProjectPath, branch2, metadata2);
|
|
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(2);
|
|
expect(result.get(branch1)).toEqual(metadata1);
|
|
expect(result.get(branch2)).toEqual(metadata2);
|
|
});
|
|
|
|
it("should skip directories without worktree.json", async () => {
|
|
const worktreesDir = path.join(testProjectPath, ".automaker", "worktrees");
|
|
const emptyDir = path.join(worktreesDir, "empty-dir");
|
|
await fs.mkdir(emptyDir, { recursive: true });
|
|
|
|
const branch = "valid-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(1);
|
|
expect(result.get(branch)).toEqual(metadata);
|
|
});
|
|
|
|
it("should skip files in worktrees directory", async () => {
|
|
const worktreesDir = path.join(testProjectPath, ".automaker", "worktrees");
|
|
await fs.mkdir(worktreesDir, { recursive: true });
|
|
const filePath = path.join(worktreesDir, "not-a-dir.txt");
|
|
await fs.writeFile(filePath, "content");
|
|
|
|
const branch = "valid-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(1);
|
|
expect(result.get(branch)).toEqual(metadata);
|
|
});
|
|
|
|
it("should skip directories with malformed JSON", async () => {
|
|
const worktreesDir = path.join(testProjectPath, ".automaker", "worktrees");
|
|
const badDir = path.join(worktreesDir, "bad-dir");
|
|
await fs.mkdir(badDir, { recursive: true });
|
|
const badJsonPath = path.join(badDir, "worktree.json");
|
|
await fs.writeFile(badJsonPath, "not valid json");
|
|
|
|
const branch = "valid-branch";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
|
|
const result = await readAllWorktreeMetadata(testProjectPath);
|
|
expect(result.size).toBe(1);
|
|
expect(result.get(branch)).toEqual(metadata);
|
|
});
|
|
});
|
|
|
|
describe("deleteWorktreeMetadata", () => {
|
|
it("should delete worktree metadata directory", async () => {
|
|
const branch = "to-delete";
|
|
const metadata: WorktreeMetadata = {
|
|
branch,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await writeWorktreeMetadata(testProjectPath, branch, metadata);
|
|
let result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).not.toBeNull();
|
|
|
|
await deleteWorktreeMetadata(testProjectPath, branch);
|
|
result = await readWorktreeMetadata(testProjectPath, branch);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should handle deletion when metadata doesn't exist", async () => {
|
|
// Should not throw
|
|
await expect(
|
|
deleteWorktreeMetadata(testProjectPath, "nonexistent")
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
|