diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c36ea59..97e17ce 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,8 +41,14 @@ jobs: - name: Build run: npm run build - - name: Check - run: npm run check + - name: Lint + run: npm run lint - - name: Web UI unit tests + - name: Typecheck + run: npm run typecheck + + - name: Root tests + run: npm test + + - name: Web UI tests run: npm --prefix web-ui run test diff --git a/test/integration/shutdown-coordinator.integration.test.ts b/test/integration/shutdown-coordinator.integration.test.ts index 4bfe1f5..fc841ce 100644 --- a/test/integration/shutdown-coordinator.integration.test.ts +++ b/test/integration/shutdown-coordinator.integration.test.ts @@ -94,93 +94,97 @@ function createSession(taskId: string, state: "running" | "awaiting_review" | "i } describe.sequential("shutdown coordinator integration", () => { - it("moves all in-progress and review cards to trash for every indexed project on shutdown", async () => { - await withTemporaryHome(async () => { - const { path: sandboxRoot, cleanup } = createTempDir("kanban-shutdown-scope-"); - try { - const managedProjectPath = join(sandboxRoot, "managed-project"); - const indexedProjectPath = join(sandboxRoot, "indexed-project"); - mkdirSync(managedProjectPath, { recursive: true }); - mkdirSync(indexedProjectPath, { recursive: true }); - initGitRepository(managedProjectPath); - initGitRepository(indexedProjectPath); + it( + "moves all in-progress and review cards to trash for every indexed project on shutdown", + async () => { + await withTemporaryHome(async () => { + const { path: sandboxRoot, cleanup } = createTempDir("kanban-shutdown-scope-"); + try { + const managedProjectPath = join(sandboxRoot, "managed-project"); + const indexedProjectPath = join(sandboxRoot, "indexed-project"); + mkdirSync(managedProjectPath, { recursive: true }); + mkdirSync(indexedProjectPath, { recursive: true }); + initGitRepository(managedProjectPath); + initGitRepository(indexedProjectPath); - const managedInitial = await loadWorkspaceState(managedProjectPath); - await saveWorkspaceState(managedProjectPath, { - board: createBoard({ - inProgress: ["managed-running", "managed-missing-session"], - review: ["managed-idle"], - }), - sessions: { - "managed-running": createSession("managed-running", "running"), - "managed-idle": createSession("managed-idle", "idle"), - }, - expectedRevision: managedInitial.revision, - }); + const managedInitial = await loadWorkspaceState(managedProjectPath); + await saveWorkspaceState(managedProjectPath, { + board: createBoard({ + inProgress: ["managed-running", "managed-missing-session"], + review: ["managed-idle"], + }), + sessions: { + "managed-running": createSession("managed-running", "running"), + "managed-idle": createSession("managed-idle", "idle"), + }, + expectedRevision: managedInitial.revision, + }); - const indexedInitial = await loadWorkspaceState(indexedProjectPath); - await saveWorkspaceState(indexedProjectPath, { - board: createBoard({ - inProgress: ["indexed-missing-session"], - review: ["indexed-awaiting-review"], - }), - sessions: { - "indexed-awaiting-review": createSession("indexed-awaiting-review", "awaiting_review"), - }, - expectedRevision: indexedInitial.revision, - }); + const indexedInitial = await loadWorkspaceState(indexedProjectPath); + await saveWorkspaceState(indexedProjectPath, { + board: createBoard({ + inProgress: ["indexed-missing-session"], + review: ["indexed-awaiting-review"], + }), + sessions: { + "indexed-awaiting-review": createSession("indexed-awaiting-review", "awaiting_review"), + }, + expectedRevision: indexedInitial.revision, + }); - let didCloseRuntimeServer = false; - const managedTerminalManager = { - markInterruptedAndStopAll: () => [createSession("managed-running", "running")], - listSummaries: () => [createSession("managed-running", "running")], - getSummary: (taskId: string) => { - if (taskId === "managed-running") { - return createSession("managed-running", "running"); - } - if (taskId === "managed-idle") { - return createSession("managed-idle", "idle"); - } - return null; - }, - } as unknown as TerminalSessionManager; - await shutdownRuntimeServer({ - workspaceRegistry: { - listManagedWorkspaces: () => [ - { - workspaceId: "managed-project", - workspacePath: managedProjectPath, - terminalManager: managedTerminalManager, - }, - ], - }, - warn: () => {}, - closeRuntimeServer: async () => { - didCloseRuntimeServer = true; - }, - }); + let didCloseRuntimeServer = false; + const managedTerminalManager = { + markInterruptedAndStopAll: () => [createSession("managed-running", "running")], + listSummaries: () => [createSession("managed-running", "running")], + getSummary: (taskId: string) => { + if (taskId === "managed-running") { + return createSession("managed-running", "running"); + } + if (taskId === "managed-idle") { + return createSession("managed-idle", "idle"); + } + return null; + }, + } as unknown as TerminalSessionManager; + await shutdownRuntimeServer({ + workspaceRegistry: { + listManagedWorkspaces: () => [ + { + workspaceId: "managed-project", + workspacePath: managedProjectPath, + terminalManager: managedTerminalManager, + }, + ], + }, + warn: () => {}, + closeRuntimeServer: async () => { + didCloseRuntimeServer = true; + }, + }); - expect(didCloseRuntimeServer).toBe(true); + expect(didCloseRuntimeServer).toBe(true); - const managedAfter = await loadWorkspaceState(managedProjectPath); - const managedTrash = managedAfter.board.columns.find((column) => column.id === "trash")?.cards ?? []; - expect(managedTrash.map((card) => card.id).sort()).toEqual( - ["managed-idle", "managed-missing-session", "managed-running"].sort(), - ); - expect(managedAfter.sessions["managed-running"]?.state).toBe("interrupted"); - expect(managedAfter.sessions["managed-idle"]?.state).toBe("interrupted"); - expect(managedAfter.sessions["managed-missing-session"]).toBeUndefined(); + const managedAfter = await loadWorkspaceState(managedProjectPath); + const managedTrash = managedAfter.board.columns.find((column) => column.id === "trash")?.cards ?? []; + expect(managedTrash.map((card) => card.id).sort()).toEqual( + ["managed-idle", "managed-missing-session", "managed-running"].sort(), + ); + expect(managedAfter.sessions["managed-running"]?.state).toBe("interrupted"); + expect(managedAfter.sessions["managed-idle"]?.state).toBe("interrupted"); + expect(managedAfter.sessions["managed-missing-session"]).toBeUndefined(); - const indexedAfter = await loadWorkspaceState(indexedProjectPath); - const indexedTrash = indexedAfter.board.columns.find((column) => column.id === "trash")?.cards ?? []; - expect(indexedTrash.map((card) => card.id).sort()).toEqual( - ["indexed-awaiting-review", "indexed-missing-session"].sort(), - ); - expect(indexedAfter.sessions["indexed-awaiting-review"]?.state).toBe("interrupted"); - expect(indexedAfter.sessions["indexed-missing-session"]).toBeUndefined(); - } finally { - cleanup(); - } - }); - }); + const indexedAfter = await loadWorkspaceState(indexedProjectPath); + const indexedTrash = indexedAfter.board.columns.find((column) => column.id === "trash")?.cards ?? []; + expect(indexedTrash.map((card) => card.id).sort()).toEqual( + ["indexed-awaiting-review", "indexed-missing-session"].sort(), + ); + expect(indexedAfter.sessions["indexed-awaiting-review"]?.state).toBe("interrupted"); + expect(indexedAfter.sessions["indexed-missing-session"]).toBeUndefined(); + } finally { + cleanup(); + } + }); + }, + 30_000, + ); }); diff --git a/vitest.config.ts b/vitest.config.ts index 964660f..0265a0c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,5 +5,6 @@ export default defineConfig({ globals: true, environment: "node", exclude: ["apps/**", "web-ui/**", "**/node_modules/**", "**/dist/**", ".worktrees/**"], + testTimeout: 15_000, }, });