Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"dev": "bun run --watch src/index.ts",
"build": "tsc --noEmit",
"start": "bun run src/index.ts",
"test": "LOG_LEVEL=silent bun test src/__tests__/websocket-handler.test.ts && LOG_LEVEL=silent bun test src/__tests__/sync-integration.test.ts && LOG_LEVEL=silent bun test src/__tests__/session-manager.test.ts && LOG_LEVEL=silent bun test src/__tests__/file-browser.test.ts src/__tests__/file-upload.test.ts src/__tests__/fuzzy-matcher.test.ts src/__tests__/health-collector.test.ts src/__tests__/inspiration-manager.test.ts src/__tests__/meeting-capture.test.ts src/__tests__/note-capture.test.ts src/__tests__/search-index.perf.test.ts src/__tests__/search-index.test.ts src/__tests__/search-integration.test.ts src/__tests__/server.test.ts src/__tests__/task-manager.test.ts src/__tests__/vault-config.test.ts src/__tests__/vault-manager.test.ts src/__tests__/vault-setup.test.ts src/__tests__/vault-transfer.test.ts src/__tests__/widget-integration.test.ts src/__tests__/widget-performance.test.ts && LOG_LEVEL=silent bun test src/sync/__tests__/sync-pipeline.test.ts && LOG_LEVEL=silent bun test src/sync/__tests__/api-response-cache.test.ts src/sync/__tests__/bgg-connector.test.ts src/sync/__tests__/config-loader.test.ts src/sync/__tests__/connector-interface.test.ts src/sync/__tests__/frontmatter-updater.test.ts src/sync/__tests__/schemas.test.ts src/sync/__tests__/vocabulary-normalizer.test.ts && LOG_LEVEL=silent bun test src/widgets/__tests__/file-watcher.test.ts && LOG_LEVEL=silent bun test src/widgets/__tests__/aggregators.test.ts src/widgets/__tests__/comparators.test.ts src/widgets/__tests__/context-aggregators.test.ts src/widgets/__tests__/dag-integration.test.ts src/widgets/__tests__/dependency-graph.test.ts src/widgets/__tests__/expression-eval.test.ts src/widgets/__tests__/frontmatter.test.ts src/widgets/__tests__/schemas.test.ts src/widgets/__tests__/task002-validation.test.ts src/widgets/__tests__/widget-cache.test.ts src/widgets/__tests__/widget-engine.test.ts src/widgets/__tests__/widget-includes.test.ts src/widgets/__tests__/widget-loader.test.ts && LOG_LEVEL=silent bun test src/handlers/__tests__/sync-handlers.test.ts",
"test": "LOG_LEVEL=silent bun test",
"test:unit": "bun run test",
"test:coverage": "LOG_LEVEL=silent bun test src/__tests__/websocket-handler.test.ts --coverage && LOG_LEVEL=silent bun test src/__tests__/sync-integration.test.ts --coverage && LOG_LEVEL=silent bun test src/__tests__/session-manager.test.ts --coverage && LOG_LEVEL=silent bun test src/__tests__/file-browser.test.ts src/__tests__/file-upload.test.ts src/__tests__/fuzzy-matcher.test.ts src/__tests__/health-collector.test.ts src/__tests__/inspiration-manager.test.ts src/__tests__/meeting-capture.test.ts src/__tests__/note-capture.test.ts src/__tests__/search-index.perf.test.ts src/__tests__/search-index.test.ts src/__tests__/search-integration.test.ts src/__tests__/server.test.ts src/__tests__/task-manager.test.ts src/__tests__/vault-config.test.ts src/__tests__/vault-manager.test.ts src/__tests__/vault-setup.test.ts src/__tests__/vault-transfer.test.ts src/__tests__/widget-integration.test.ts src/__tests__/widget-performance.test.ts --coverage && LOG_LEVEL=silent bun test src/sync/__tests__/sync-pipeline.test.ts --coverage && LOG_LEVEL=silent bun test src/sync/__tests__/api-response-cache.test.ts src/sync/__tests__/bgg-connector.test.ts src/sync/__tests__/config-loader.test.ts src/sync/__tests__/connector-interface.test.ts src/sync/__tests__/frontmatter-updater.test.ts src/sync/__tests__/schemas.test.ts src/sync/__tests__/vocabulary-normalizer.test.ts --coverage && LOG_LEVEL=silent bun test src/widgets/__tests__/file-watcher.test.ts --coverage && LOG_LEVEL=silent bun test src/widgets/__tests__/aggregators.test.ts src/widgets/__tests__/comparators.test.ts src/widgets/__tests__/context-aggregators.test.ts src/widgets/__tests__/dag-integration.test.ts src/widgets/__tests__/dependency-graph.test.ts src/widgets/__tests__/expression-eval.test.ts src/widgets/__tests__/frontmatter.test.ts src/widgets/__tests__/schemas.test.ts src/widgets/__tests__/task002-validation.test.ts src/widgets/__tests__/widget-cache.test.ts src/widgets/__tests__/widget-engine.test.ts src/widgets/__tests__/widget-includes.test.ts src/widgets/__tests__/widget-loader.test.ts --coverage && LOG_LEVEL=silent bun test src/handlers/__tests__/sync-handlers.test.ts --coverage",
"test:coverage": "LOG_LEVEL=silent bun test --coverage",
"typecheck": "tsc --noEmit",
"lint": "eslint ."
},
Expand Down
59 changes: 35 additions & 24 deletions backend/src/__tests__/session-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Session Manager Tests
*
* Unit tests for session lifecycle management.
* Uses mocking for the SDK to avoid real API calls.
* Uses dependency injection for the SDK to avoid real API calls.
*/

import { describe, test, expect, beforeEach, afterEach, mock } from "bun:test";
Expand All @@ -11,16 +11,6 @@ import { join } from "node:path";
import { tmpdir } from "node:os";
import type { SessionMetadata, VaultInfo } from "@memory-loop/shared";

// Mock the SDK before importing session-manager
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockQuery = mock<(...args: any[]) => any>(() => undefined);
const mockInterrupt = mock(() => Promise.resolve());

void mock.module("@anthropic-ai/claude-agent-sdk", () => ({
query: mockQuery,
}));

// Now import session-manager (it will use the mocked SDK)
import {
getSessionsDir,
getSessionFilePath,
Expand All @@ -36,8 +26,26 @@ import {
resumeSession,
querySession,
SESSIONS_DIR,
type QueryFunction,
} from "../session-manager";

// =============================================================================
// Mock SDK Query Function (injected via DI)
// =============================================================================

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockQuery = mock<(...args: any[]) => any>(() => undefined);
const mockInterrupt = mock(() => Promise.resolve());
const mockSupportedCommands = mock(() => Promise.resolve([]));

/**
* Creates the mock query function that can be injected.
*/
function createMockQueryFn(): QueryFunction {
// Return the mock's current implementation
return mockQuery as unknown as QueryFunction;
}

// =============================================================================
// Test Fixtures
// =============================================================================
Expand Down Expand Up @@ -110,6 +118,7 @@ function createMockQueryGenerator(
return this;
},
interrupt: mockInterrupt,
supportedCommands: mockSupportedCommands,
};

return generator;
Expand Down Expand Up @@ -698,7 +707,8 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("new-session-id");
mockQuery.mockReturnValue(mockGenerator);

await createSession(vault, "Hello");
// Pass mock query function via DI (last parameter)
await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

expect(mockQuery).toHaveBeenCalledTimes(1);
const calls = mockQuery.mock.calls;
Expand All @@ -716,7 +726,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("saved-session-id");
mockQuery.mockReturnValue(mockGenerator);

const result = await createSession(vault, "Hello");
const result = await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

expect(result.sessionId).toBe("saved-session-id");

Expand All @@ -733,7 +743,7 @@ describe("Session Manager", () => {
]);
mockQuery.mockReturnValue(mockGenerator);

const result = await createSession(vault, "Hello");
const result = await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

// Consume events
const events: unknown[] = [];
Expand All @@ -752,7 +762,7 @@ describe("Session Manager", () => {
});

try {
await createSession(vault, "Hello");
await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());
expect.unreachable("Should have thrown");
} catch (error) {
expect(error).toBeInstanceOf(SessionError);
Expand All @@ -765,7 +775,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("interruptible-session");
mockQuery.mockReturnValue(mockGenerator);

const result = await createSession(vault, "Hello");
const result = await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

expect(typeof result.interrupt).toBe("function");
await result.interrupt();
Expand All @@ -777,7 +787,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("custom-options-session");
mockQuery.mockReturnValue(mockGenerator);

await createSession(vault, "Hello", { maxTurns: 5 });
await createSession(vault, "Hello", { maxTurns: 5 }, undefined, undefined, createMockQueryFn());

const calls = mockQuery.mock.calls;
expect(calls.length).toBeGreaterThan(0);
Expand All @@ -790,7 +800,8 @@ describe("Session Manager", () => {
describe("resumeSession", () => {
test("throws for non-existent session", async () => {
try {
await resumeSession(vaultPath, "non-existent", "Continue");
// No queryFn needed since it throws before calling the SDK
await resumeSession(vaultPath, "non-existent", "Continue", undefined, undefined, undefined, createMockQueryFn());
expect.unreachable("Should have thrown");
} catch (error) {
expect(error).toBeInstanceOf(SessionError);
Expand All @@ -805,7 +816,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("existing-session");
mockQuery.mockReturnValue(mockGenerator);

await resumeSession(vaultPath, "existing-session", "Continue");
await resumeSession(vaultPath, "existing-session", "Continue", undefined, undefined, undefined, createMockQueryFn());

expect(mockQuery).toHaveBeenCalledTimes(1);
const calls = mockQuery.mock.calls;
Expand All @@ -828,7 +839,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("resume-session");
mockQuery.mockReturnValue(mockGenerator);

await resumeSession(vaultPath, "resume-session", "Continue");
await resumeSession(vaultPath, "resume-session", "Continue", undefined, undefined, undefined, createMockQueryFn());

const updated = await loadSession(vaultPath, "resume-session");
expect(updated!.lastActiveAt).not.toBe("2025-01-01T00:00:00.000Z");
Expand All @@ -841,7 +852,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("auto-created-session");
mockQuery.mockReturnValue(mockGenerator);

const result = await querySession(vault, "Hello");
const result = await querySession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

expect(result.sessionId).toBe("auto-created-session");

Expand All @@ -864,7 +875,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("existing-for-query");
mockQuery.mockReturnValue(mockGenerator);

const result = await querySession(vault, "Continue", "existing-for-query");
const result = await querySession(vault, "Continue", "existing-for-query", undefined, undefined, createMockQueryFn());

expect(result.sessionId).toBe("existing-for-query");

Expand Down Expand Up @@ -906,7 +917,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("empty-prompt-session");
mockQuery.mockReturnValue(mockGenerator);

const result = await createSession(vault, "");
const result = await createSession(vault, "", undefined, undefined, undefined, createMockQueryFn());

expect(result.sessionId).toBe("empty-prompt-session");
const calls = mockQuery.mock.calls;
Expand All @@ -925,7 +936,7 @@ describe("Session Manager", () => {
const mockGenerator = createMockQueryGenerator("spaces-session");
mockQuery.mockReturnValue(mockGenerator);

await createSession(vault, "Hello");
await createSession(vault, "Hello", undefined, undefined, undefined, createMockQueryFn());

const calls = mockQuery.mock.calls;
expect(calls.length).toBeGreaterThan(0);
Expand Down
Loading