- ローダーがユーザーファイル優先、なければdist/resources/からbuiltinを読む方式に変更 - /ejectコマンドを追加(builtinを~/.takt/にコピーしてカスタマイズ可能に) - /refresh-builtinを簡素化(ejectへの移行案内) - config.yamlにdisabled_builtinsフィールドを追加 - ワークフローYAMLをrules形式に統一
693 lines
20 KiB
TypeScript
693 lines
20 KiB
TypeScript
/**
|
|
* Tests for takt config functions
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { randomUUID } from 'node:crypto';
|
|
import {
|
|
getBuiltinWorkflow,
|
|
loadAllWorkflows,
|
|
loadWorkflow,
|
|
listWorkflows,
|
|
loadAgentPromptFromPath,
|
|
} from '../config/loader.js';
|
|
import {
|
|
getCurrentWorkflow,
|
|
setCurrentWorkflow,
|
|
getProjectConfigDir,
|
|
getBuiltinAgentsDir,
|
|
loadInputHistory,
|
|
saveInputHistory,
|
|
addToInputHistory,
|
|
getInputHistoryPath,
|
|
MAX_INPUT_HISTORY,
|
|
// Worktree session functions
|
|
getWorktreeSessionsDir,
|
|
encodeWorktreePath,
|
|
getWorktreeSessionPath,
|
|
loadWorktreeSessions,
|
|
updateWorktreeSession,
|
|
} from '../config/paths.js';
|
|
import { getLanguage } from '../config/globalConfig.js';
|
|
import { loadProjectConfig } from '../config/projectConfig.js';
|
|
|
|
describe('getBuiltinWorkflow', () => {
|
|
it('should return builtin workflow when it exists in resources', () => {
|
|
const workflow = getBuiltinWorkflow('default');
|
|
expect(workflow).not.toBeNull();
|
|
expect(workflow!.name).toBe('default');
|
|
});
|
|
|
|
it('should return null for non-existent workflow names', () => {
|
|
expect(getBuiltinWorkflow('passthrough')).toBeNull();
|
|
expect(getBuiltinWorkflow('unknown')).toBeNull();
|
|
expect(getBuiltinWorkflow('')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('loadAllWorkflows', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should only load workflows from global ~/.takt/workflows/ (not project-local)', () => {
|
|
// Project-local workflows should NOT be loaded anymore
|
|
const workflowsDir = join(testDir, '.takt', 'workflows');
|
|
mkdirSync(workflowsDir, { recursive: true });
|
|
|
|
const sampleWorkflow = `
|
|
name: test-workflow
|
|
description: Test workflow
|
|
max_iterations: 10
|
|
steps:
|
|
- name: step1
|
|
agent: coder
|
|
instruction: "{task}"
|
|
rules:
|
|
- condition: Task completed
|
|
next: COMPLETE
|
|
`;
|
|
writeFileSync(join(workflowsDir, 'test.yaml'), sampleWorkflow);
|
|
|
|
const workflows = loadAllWorkflows();
|
|
|
|
// Project-local workflow should NOT be loaded
|
|
expect(workflows.has('test')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('loadWorkflow (builtin fallback)', () => {
|
|
it('should load builtin workflow when user workflow does not exist', () => {
|
|
const workflow = loadWorkflow('default');
|
|
expect(workflow).not.toBeNull();
|
|
expect(workflow!.name).toBe('default');
|
|
});
|
|
|
|
it('should return null for non-existent workflow', () => {
|
|
const workflow = loadWorkflow('does-not-exist');
|
|
expect(workflow).toBeNull();
|
|
});
|
|
|
|
it('should load builtin workflows like simple, research', () => {
|
|
const simple = loadWorkflow('simple');
|
|
expect(simple).not.toBeNull();
|
|
expect(simple!.name).toBe('simple');
|
|
|
|
const research = loadWorkflow('research');
|
|
expect(research).not.toBeNull();
|
|
expect(research!.name).toBe('research');
|
|
});
|
|
});
|
|
|
|
describe('listWorkflows (builtin fallback)', () => {
|
|
it('should include builtin workflows', () => {
|
|
const workflows = listWorkflows();
|
|
expect(workflows).toContain('default');
|
|
expect(workflows).toContain('simple');
|
|
});
|
|
|
|
it('should return sorted list', () => {
|
|
const workflows = listWorkflows();
|
|
const sorted = [...workflows].sort();
|
|
expect(workflows).toEqual(sorted);
|
|
});
|
|
});
|
|
|
|
describe('loadAllWorkflows (builtin fallback)', () => {
|
|
it('should include builtin workflows in the map', () => {
|
|
const workflows = loadAllWorkflows();
|
|
expect(workflows.has('default')).toBe(true);
|
|
expect(workflows.has('simple')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('loadAgentPromptFromPath (builtin paths)', () => {
|
|
it('should load agent prompt from builtin resources path', () => {
|
|
const lang = getLanguage();
|
|
const builtinAgentsDir = getBuiltinAgentsDir(lang);
|
|
const agentPath = join(builtinAgentsDir, 'default', 'coder.md');
|
|
|
|
if (existsSync(agentPath)) {
|
|
const prompt = loadAgentPromptFromPath(agentPath);
|
|
expect(prompt).toBeTruthy();
|
|
expect(typeof prompt).toBe('string');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('getCurrentWorkflow', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should return default when no config exists', () => {
|
|
const workflow = getCurrentWorkflow(testDir);
|
|
|
|
expect(workflow).toBe('default');
|
|
});
|
|
|
|
it('should return saved workflow name from config.yaml', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
writeFileSync(join(configDir, 'config.yaml'), 'workflow: default\n');
|
|
|
|
const workflow = getCurrentWorkflow(testDir);
|
|
|
|
expect(workflow).toBe('default');
|
|
});
|
|
|
|
it('should return default for empty config', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
writeFileSync(join(configDir, 'config.yaml'), '');
|
|
|
|
const workflow = getCurrentWorkflow(testDir);
|
|
|
|
expect(workflow).toBe('default');
|
|
});
|
|
});
|
|
|
|
describe('setCurrentWorkflow', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should save workflow name to config.yaml', () => {
|
|
setCurrentWorkflow(testDir, 'my-workflow');
|
|
|
|
const config = loadProjectConfig(testDir);
|
|
|
|
expect(config.workflow).toBe('my-workflow');
|
|
});
|
|
|
|
it('should create config directory if not exists', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
expect(existsSync(configDir)).toBe(false);
|
|
|
|
setCurrentWorkflow(testDir, 'test');
|
|
|
|
expect(existsSync(configDir)).toBe(true);
|
|
});
|
|
|
|
it('should overwrite existing workflow name', () => {
|
|
setCurrentWorkflow(testDir, 'first');
|
|
setCurrentWorkflow(testDir, 'second');
|
|
|
|
const workflow = getCurrentWorkflow(testDir);
|
|
|
|
expect(workflow).toBe('second');
|
|
});
|
|
});
|
|
|
|
describe('loadInputHistory', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should return empty array when no history exists', () => {
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual([]);
|
|
});
|
|
|
|
it('should load saved history entries', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
const entries = ['"first entry"', '"second entry"'];
|
|
writeFileSync(getInputHistoryPath(testDir), entries.join('\n'));
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual(['first entry', 'second entry']);
|
|
});
|
|
|
|
it('should handle multi-line entries', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
const multiLine = 'line1\nline2\nline3';
|
|
writeFileSync(getInputHistoryPath(testDir), JSON.stringify(multiLine));
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toHaveLength(1);
|
|
expect(history[0]).toBe('line1\nline2\nline3');
|
|
});
|
|
});
|
|
|
|
describe('saveInputHistory', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should save history entries', () => {
|
|
saveInputHistory(testDir, ['entry1', 'entry2']);
|
|
|
|
const content = readFileSync(getInputHistoryPath(testDir), 'utf-8');
|
|
expect(content).toBe('"entry1"\n"entry2"');
|
|
});
|
|
|
|
it('should create config directory if not exists', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
expect(existsSync(configDir)).toBe(false);
|
|
|
|
saveInputHistory(testDir, ['test']);
|
|
|
|
expect(existsSync(configDir)).toBe(true);
|
|
});
|
|
|
|
it('should preserve multi-line entries', () => {
|
|
const multiLine = 'line1\nline2';
|
|
saveInputHistory(testDir, [multiLine]);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history[0]).toBe('line1\nline2');
|
|
});
|
|
});
|
|
|
|
describe('addToInputHistory', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should add new entry to history', () => {
|
|
addToInputHistory(testDir, 'first');
|
|
addToInputHistory(testDir, 'second');
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual(['first', 'second']);
|
|
});
|
|
|
|
it('should not add consecutive duplicates', () => {
|
|
addToInputHistory(testDir, 'same');
|
|
addToInputHistory(testDir, 'same');
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual(['same']);
|
|
});
|
|
|
|
it('should allow non-consecutive duplicates', () => {
|
|
addToInputHistory(testDir, 'first');
|
|
addToInputHistory(testDir, 'second');
|
|
addToInputHistory(testDir, 'first');
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual(['first', 'second', 'first']);
|
|
});
|
|
});
|
|
|
|
describe('saveInputHistory - edge cases', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should trim history to MAX_INPUT_HISTORY entries', () => {
|
|
const entries = Array.from({ length: 150 }, (_, i) => `entry${i}`);
|
|
saveInputHistory(testDir, entries);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toHaveLength(MAX_INPUT_HISTORY);
|
|
// First 50 entries should be trimmed, keeping entries 50-149
|
|
expect(history[0]).toBe('entry50');
|
|
expect(history[MAX_INPUT_HISTORY - 1]).toBe('entry149');
|
|
});
|
|
|
|
it('should handle empty history array', () => {
|
|
saveInputHistory(testDir, []);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('loadInputHistory - edge cases', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should skip invalid JSON entries', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
// Mix of valid JSON and invalid entries
|
|
const content = '"valid entry"\ninvalid json\n"another valid"';
|
|
writeFileSync(getInputHistoryPath(testDir), content);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
// Invalid entries should be skipped
|
|
expect(history).toEqual(['valid entry', 'another valid']);
|
|
});
|
|
|
|
it('should handle completely corrupted file', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
// All invalid JSON
|
|
const content = 'not json\nalso not json\nstill not json';
|
|
writeFileSync(getInputHistoryPath(testDir), content);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
// All entries should be skipped
|
|
expect(history).toEqual([]);
|
|
});
|
|
|
|
it('should handle file with only whitespace lines', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
const content = ' \n\n \n';
|
|
writeFileSync(getInputHistoryPath(testDir), content);
|
|
|
|
const history = loadInputHistory(testDir);
|
|
|
|
expect(history).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('saveProjectConfig - gitignore copy', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should copy .gitignore when creating new config', () => {
|
|
setCurrentWorkflow(testDir, 'test');
|
|
|
|
const configDir = getProjectConfigDir(testDir);
|
|
const gitignorePath = join(configDir, '.gitignore');
|
|
|
|
expect(existsSync(gitignorePath)).toBe(true);
|
|
});
|
|
|
|
it('should copy .gitignore to existing config directory without one', () => {
|
|
// Create config directory without .gitignore
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
writeFileSync(join(configDir, 'config.yaml'), 'workflow: existing\n');
|
|
|
|
// Save config should still copy .gitignore
|
|
setCurrentWorkflow(testDir, 'updated');
|
|
|
|
const gitignorePath = join(configDir, '.gitignore');
|
|
expect(existsSync(gitignorePath)).toBe(true);
|
|
});
|
|
|
|
it('should not overwrite existing .gitignore', () => {
|
|
const configDir = getProjectConfigDir(testDir);
|
|
mkdirSync(configDir, { recursive: true });
|
|
const customContent = '# Custom gitignore\nmy-custom-file';
|
|
writeFileSync(join(configDir, '.gitignore'), customContent);
|
|
|
|
setCurrentWorkflow(testDir, 'test');
|
|
|
|
const gitignorePath = join(configDir, '.gitignore');
|
|
const content = readFileSync(gitignorePath, 'utf-8');
|
|
expect(content).toBe(customContent);
|
|
});
|
|
});
|
|
|
|
// ============ Worktree Sessions ============
|
|
|
|
describe('encodeWorktreePath', () => {
|
|
it('should replace slashes with dashes', () => {
|
|
const encoded = encodeWorktreePath('/project/.takt/worktrees/my-task');
|
|
|
|
expect(encoded).not.toContain('/');
|
|
expect(encoded).toContain('-');
|
|
});
|
|
|
|
it('should handle Windows-style paths', () => {
|
|
const encoded = encodeWorktreePath('C:\\project\\worktrees\\task');
|
|
|
|
expect(encoded).not.toContain('\\');
|
|
expect(encoded).not.toContain(':');
|
|
});
|
|
|
|
it('should produce consistent output for same input', () => {
|
|
const path = '/project/.takt/worktrees/feature-x';
|
|
const encoded1 = encodeWorktreePath(path);
|
|
const encoded2 = encodeWorktreePath(path);
|
|
|
|
expect(encoded1).toBe(encoded2);
|
|
});
|
|
});
|
|
|
|
describe('getWorktreeSessionsDir', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should return path inside .takt directory', () => {
|
|
const sessionsDir = getWorktreeSessionsDir(testDir);
|
|
|
|
expect(sessionsDir).toContain('.takt');
|
|
expect(sessionsDir).toContain('worktree-sessions');
|
|
});
|
|
});
|
|
|
|
describe('getWorktreeSessionPath', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should return .json file path', () => {
|
|
const sessionPath = getWorktreeSessionPath(testDir, '/worktree/path');
|
|
|
|
expect(sessionPath).toMatch(/\.json$/);
|
|
});
|
|
|
|
it('should include encoded worktree path in filename', () => {
|
|
const worktreePath = '/project/.takt/worktrees/my-feature';
|
|
const sessionPath = getWorktreeSessionPath(testDir, worktreePath);
|
|
|
|
expect(sessionPath).toContain('worktree-sessions');
|
|
});
|
|
});
|
|
|
|
describe('loadWorktreeSessions', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should return empty object when no session file exists', () => {
|
|
const sessions = loadWorktreeSessions(testDir, '/some/worktree');
|
|
|
|
expect(sessions).toEqual({});
|
|
});
|
|
|
|
it('should load saved sessions from file', () => {
|
|
const worktreePath = '/project/worktree';
|
|
const sessionsDir = getWorktreeSessionsDir(testDir);
|
|
mkdirSync(sessionsDir, { recursive: true });
|
|
|
|
const sessionPath = getWorktreeSessionPath(testDir, worktreePath);
|
|
const data = {
|
|
agentSessions: { coder: 'session-123', reviewer: 'session-456' },
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
writeFileSync(sessionPath, JSON.stringify(data));
|
|
|
|
const sessions = loadWorktreeSessions(testDir, worktreePath);
|
|
|
|
expect(sessions).toEqual({ coder: 'session-123', reviewer: 'session-456' });
|
|
});
|
|
|
|
it('should return empty object for corrupted JSON', () => {
|
|
const worktreePath = '/project/worktree';
|
|
const sessionsDir = getWorktreeSessionsDir(testDir);
|
|
mkdirSync(sessionsDir, { recursive: true });
|
|
|
|
const sessionPath = getWorktreeSessionPath(testDir, worktreePath);
|
|
writeFileSync(sessionPath, 'not valid json');
|
|
|
|
const sessions = loadWorktreeSessions(testDir, worktreePath);
|
|
|
|
expect(sessions).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe('updateWorktreeSession', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `takt-test-${randomUUID()}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('should create session file if not exists', () => {
|
|
const worktreePath = '/project/worktree';
|
|
|
|
updateWorktreeSession(testDir, worktreePath, 'coder', 'session-abc');
|
|
|
|
const sessions = loadWorktreeSessions(testDir, worktreePath);
|
|
expect(sessions).toEqual({ coder: 'session-abc' });
|
|
});
|
|
|
|
it('should update existing session', () => {
|
|
const worktreePath = '/project/worktree';
|
|
|
|
updateWorktreeSession(testDir, worktreePath, 'coder', 'session-1');
|
|
updateWorktreeSession(testDir, worktreePath, 'coder', 'session-2');
|
|
|
|
const sessions = loadWorktreeSessions(testDir, worktreePath);
|
|
expect(sessions.coder).toBe('session-2');
|
|
});
|
|
|
|
it('should preserve other agent sessions when updating one', () => {
|
|
const worktreePath = '/project/worktree';
|
|
|
|
updateWorktreeSession(testDir, worktreePath, 'coder', 'coder-session');
|
|
updateWorktreeSession(testDir, worktreePath, 'reviewer', 'reviewer-session');
|
|
|
|
const sessions = loadWorktreeSessions(testDir, worktreePath);
|
|
expect(sessions).toEqual({
|
|
coder: 'coder-session',
|
|
reviewer: 'reviewer-session',
|
|
});
|
|
});
|
|
|
|
it('should create worktree-sessions directory if not exists', () => {
|
|
const worktreePath = '/project/worktree';
|
|
const sessionsDir = getWorktreeSessionsDir(testDir);
|
|
expect(existsSync(sessionsDir)).toBe(false);
|
|
|
|
updateWorktreeSession(testDir, worktreePath, 'coder', 'session-xyz');
|
|
|
|
expect(existsSync(sessionsDir)).toBe(true);
|
|
});
|
|
|
|
it('should keep sessions isolated between different worktrees', () => {
|
|
const worktree1 = '/project/worktree-1';
|
|
const worktree2 = '/project/worktree-2';
|
|
|
|
updateWorktreeSession(testDir, worktree1, 'coder', 'wt1-session');
|
|
updateWorktreeSession(testDir, worktree2, 'coder', 'wt2-session');
|
|
|
|
const sessions1 = loadWorktreeSessions(testDir, worktree1);
|
|
const sessions2 = loadWorktreeSessions(testDir, worktree2);
|
|
|
|
expect(sessions1.coder).toBe('wt1-session');
|
|
expect(sessions2.coder).toBe('wt2-session');
|
|
});
|
|
});
|