import { describe, expect, it } from 'vitest'; import type { RepoSummary, SessionSummary, WorktreeSummary } from '@arboretum/shared'; import { Store } from '../src/state/store.js'; function repo(id: string): RepoSummary { return { id, path: `/code/${id}`, label: id, defaultBranch: 'main', postCreateHooks: [], preTrust: false, createdAt: '2026-01-01T00:00:00Z', valid: true, hidden: false, }; } function worktree(repoId: string, path: string, branch: string): WorktreeSummary { return { repoId, path, branch, head: 'abcdef0', detached: false, locked: false, prunable: false, isMain: branch === 'main', git: { ahead: 0, behind: 0, dirtyCount: 0, upstream: null }, sessions: [], }; } function session(id: string, cwd: string, over: Partial = {}): SessionSummary { return { id, cwd, command: 'claude', title: null, status: 'running', live: true, createdAt: '2026-01-01T00:00:00Z', endedAt: null, exitCode: null, clients: 0, source: 'managed', claudeSessionId: null, pid: 123, resumable: false, attachable: true, registryStatus: null, ...over, }; } describe('Store', () => { it('corrèle les sessions au worktree par cwd et filtre les externes', () => { const store = new Store(); store.applyWorktree({ type: 'repo_update', repo: repo('app') }); store.applyWorktree({ type: 'worktree_update', repoId: 'app', worktree: worktree('app', '/code/app', 'main') }); store.applySession({ type: 'session_update', session: session('s1', '/code/app') }); store.applySession({ type: 'session_update', session: session('s2', '/code/app', { source: 'discovered' }) }); // externes masquées par défaut expect(store.sessionsForWorktree('/code/app').map((s) => s.id)).toEqual(['s1']); store.setShowExternalSessions(true); expect(store.sessionsForWorktree('/code/app').map((s) => s.id).sort()).toEqual(['s1', 's2']); }); it('liste les repos visibles, triés', () => { const store = new Store(); store.applyWorktree({ type: 'repo_update', repo: { ...repo('b'), label: 'beta' } }); store.applyWorktree({ type: 'repo_update', repo: { ...repo('a'), label: 'alpha' } }); store.applyWorktree({ type: 'repo_update', repo: { ...repo('h'), label: 'hidden', hidden: true } }); expect(store.listRepos().map((r) => r.label)).toEqual(['alpha', 'beta']); }); it('session_exit marque la session morte', () => { const store = new Store(); store.applySession({ type: 'session_update', session: session('s1', '/x') }); store.applySession({ type: 'session_exit', sessionId: 's1', exitCode: 0, signal: null }); expect(store.getSession('s1')?.live).toBe(false); expect(store.getSession('s1')?.status).toBe('exited'); }); it('waitingSessions ne retient que les sessions live en attente', () => { const store = new Store(); store.applySession({ type: 'session_update', session: session('s1', '/x', { activity: 'waiting' }) }); store.applySession({ type: 'session_update', session: session('s2', '/y', { activity: 'busy' }) }); store.applySession({ type: 'session_update', session: session('s3', '/z', { activity: 'waiting', live: false, status: 'exited' }) }); expect(store.waitingSessions().map((s) => s.id)).toEqual(['s1']); }); it('worktree_removed retire le worktree', () => { const store = new Store(); store.applyWorktree({ type: 'repo_update', repo: repo('app') }); store.applyWorktree({ type: 'worktree_update', repoId: 'app', worktree: worktree('app', '/code/app-wt', 'feat') }); expect(store.listWorktrees('app')).toHaveLength(1); store.applyWorktree({ type: 'worktree_removed', repoId: 'app', path: '/code/app-wt' }); expect(store.listWorktrees('app')).toHaveLength(0); }); });