import { beforeEach, describe, expect, test } from 'bun:test'; import { useSessionWorktreeStore } from './session-worktree-store'; import { useSessionUIStore } from './session-ui-store'; /** * Unit tests for session worktree routing through the authoritative store. * * These tests verify that session-worktree-store is properly integrated as the * authoritative holder of session↔worktree attachments, and that session-ui-store * routes through it for switching and creation flows. * * Note: Full integration tests for setCurrentSession require runtime mocking. * These tests focus on the contract layer: that setAttachment/getAttachment work * correctly and that the contract helpers produce correct results. */ describe('session-worktree-store worktree routing', () => { beforeEach(() => { // Clear all attachments before each test const store = useSessionWorktreeStore.getState(); const attachments = store.attachments; for (const sessionId of attachments.keys()) { store.clearAttachment(sessionId); } useSessionUIStore.setState({ currentSessionId: null, worktreeMetadata: new Map() }); }); test('getDirectoryForSession prefers authoritative attachment cwd over sync fallback', () => { useSessionWorktreeStore.getState().setAttachment('session-dir', { worktreeRoot: '/repo/worktrees/feat-a', cwd: '/repo/worktrees/feat-a/src', branch: 'feat-a', headState: 'branch', worktreeStatus: 'ready', worktreeSource: 'existing', legacy: false, degraded: false, }); expect(useSessionUIStore.getState().getDirectoryForSession('session-dir')).toBe('/repo/worktrees/feat-a/src'); }); test('getDirectoryForSession falls back to authoritative worktreeRoot when attachment is degraded', () => { useSessionWorktreeStore.getState().setAttachment('session-dir', { worktreeRoot: '/repo/worktrees/feat-a', cwd: '/tmp/outside', branch: 'feat-a', headState: 'branch', worktreeStatus: 'invalid', worktreeSource: 'existing', legacy: false, degraded: true, }); expect(useSessionUIStore.getState().getDirectoryForSession('session-dir')).toBe('/repo/worktrees/feat-a'); }); test('setCurrentSession uses canonical cwd when valid', () => { const store = useSessionWorktreeStore.getState(); // Simulate: session has valid worktree metadata with cwd inside worktreeRoot store.setAttachment('session-1', { worktreeRoot: '/repo/worktrees/feat-a', cwd: '/repo/worktrees/feat-a/src', branch: 'feat-a', headState: 'branch', worktreeStatus: 'ready', worktreeSource: 'existing', legacy: false, degraded: false, }); const attachment = store.getAttachment('session-1'); expect(attachment).toBeDefined(); expect(attachment.cwd).toBe('/repo/worktrees/feat-a/src'); expect(attachment.worktreeRoot).toBe('/repo/worktrees/feat-a'); expect(attachment.degraded).toBe(false); expect(attachment.worktreeStatus).toBe('ready'); }); test('setCurrentSession falls back to worktreeRoot when cwd is degraded', () => { const store = useSessionWorktreeStore.getState(); // Simulate: cwd is outside worktreeRoot (degraded) store.setAttachment('session-2', { worktreeRoot: '/repo/worktrees/feat-a', cwd: '/repo/worktrees/feat-a', // same as worktreeRoot means not degraded for this case branch: 'feat-a', headState: 'branch', worktreeStatus: 'ready', worktreeSource: 'existing', legacy: false, degraded: true, // marked degraded because cwd was resolved from invalid state }); const attachment = store.getAttachment('session-2'); expect(attachment).toBeDefined(); expect(attachment.degraded).toBe(true); // cwd should equal worktreeRoot when degraded (fallback) expect(attachment.cwd).toBe(attachment.worktreeRoot); }); test('isolated session initializes created-for-session attachment', () => { const store = useSessionWorktreeStore.getState(); // Simulate: isolated worktree session created for a specific branch store.setAttachment('session-isolated', { worktreeRoot: '/repo/worktrees/feature-xyz', cwd: '/repo/worktrees/feature-xyz', branch: 'feature-xyz', headState: 'branch', worktreeStatus: 'ready', worktreeSource: 'created-for-session', legacy: false, degraded: false, }); const attachment = store.getAttachment('session-isolated'); expect(attachment).toBeDefined(); expect(attachment.worktreeSource).toBe('created-for-session'); expect(attachment.worktreeStatus).toBe('ready'); expect(attachment.legacy).toBe(false); }); test('legacy session upgrades when runtime canonicalization recovers a worktree', () => { const store = useSessionWorktreeStore.getState(); // Simulate: session without metadata (legacy) gets upgraded via runtime resolution // Initially no attachment let attachment = store.getAttachment('session-legacy'); expect(attachment).toBeUndefined(); // Runtime canonicalization resolves it to a worktree store.setAttachment('session-legacy', { worktreeRoot: '/repo/worktrees/recovered', cwd: '/repo/worktrees/recovered', branch: 'recovered', headState: 'branch', worktreeStatus: 'ready', worktreeSource: 'existing', legacy: false, // upgraded from legacy=true to false degraded: false, }); attachment = store.getAttachment('session-legacy'); expect(attachment).toBeDefined(); expect(attachment.legacy).toBe(false); expect(attachment.worktreeRoot).toBe('/repo/worktrees/recovered'); }); test('missing worktree session has missing status', () => { const store = useSessionWorktreeStore.getState(); // Simulate: session whose worktree was deleted store.setAttachment('session-missing', { worktreeRoot: null, cwd: null, branch: null, headState: 'branch', worktreeStatus: 'missing', worktreeSource: null, legacy: false, degraded: true, }); const attachment = store.getAttachment('session-missing'); expect(attachment).toBeDefined(); expect(attachment.worktreeStatus).toBe('missing'); expect(attachment.degraded).toBe(true); }); test('not-a-repo session has correct status', () => { const store = useSessionWorktreeStore.getState(); // Simulate: session opened in a directory that is not a git repo store.setAttachment('session-not-repo', { worktreeRoot: null, cwd: '/tmp/not-a-repo', branch: null, headState: 'detached', worktreeStatus: 'not-a-repo', worktreeSource: null, legacy: false, degraded: true, }); const attachment = store.getAttachment('session-not-repo'); expect(attachment).toBeDefined(); expect(attachment.worktreeStatus).toBe('not-a-repo'); }); });