Files
automaker/apps/server/tests/unit/lib/worktree-metadata.test.ts
SuperComboGamer 584f5a3426 Merge main into massive-terminal-upgrade
Resolves merge conflicts:
- apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger
- apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions
- apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling)
- apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes
- apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

392 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();
});
});
});