ded9671a82
- Replaced hardcoded migration port with dynamic port retrieval using `getWorkerPort()` in worker-cli.ts. - Updated context generator to clarify error handling comments. - Introduced timeout constants in ProcessManager for better maintainability. - Configured SQLite settings using constants for mmap size and cache size in DatabaseManager. - Added timeout constants for Git and NPM commands in BranchManager. - Enhanced error logging in FormattingService and SearchManager to provide more context on failures. - Removed deprecated silentDebug function and replaced its usage with logger.debug. - Updated tests to use dynamic worker port retrieval instead of hardcoded values.
249 lines
8.2 KiB
TypeScript
249 lines
8.2 KiB
TypeScript
/**
|
|
* Happy Path Test: Session Summary (Stop)
|
|
*
|
|
* Tests that when a user pauses or stops a session, the SDK
|
|
* generates a summary from the conversation context.
|
|
*/
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { sessionSummaryScenario, sessionScenario } from '../helpers/scenarios.js';
|
|
import { getWorkerPort } from '../../src/shared/worker-utils.js';
|
|
|
|
describe('Session Summary (Stop)', () => {
|
|
const WORKER_PORT = getWorkerPort();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('generates summary from last messages', async () => {
|
|
// This tests the happy path:
|
|
// User stops/pauses → Hook sends last messages → Worker queues for SDK →
|
|
// SDK generates summary → Summary saved to database
|
|
|
|
// Setup: Mock successful response from worker
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued' })
|
|
});
|
|
|
|
// Execute: Send summarize request (what summary-hook does)
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionSummaryScenario.claudeSessionId,
|
|
last_user_message: sessionSummaryScenario.last_user_message,
|
|
last_assistant_message: sessionSummaryScenario.last_assistant_message,
|
|
cwd: '/project/claude-mem'
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Summary queued successfully
|
|
expect(response.ok).toBe(true);
|
|
const result = await response.json();
|
|
expect(result.status).toBe('queued');
|
|
|
|
// Verify: Correct data sent to worker
|
|
const fetchCall = (global.fetch as any).mock.calls[0];
|
|
const requestBody = JSON.parse(fetchCall[1].body);
|
|
expect(requestBody.last_user_message).toBe('Thanks, that fixed it!');
|
|
expect(requestBody.last_assistant_message).toContain('parser');
|
|
});
|
|
|
|
it('handles missing session ID gracefully', async () => {
|
|
// Setup: Mock error response
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: false,
|
|
status: 400,
|
|
json: async () => ({ error: 'Missing claudeSessionId' })
|
|
});
|
|
|
|
// Execute: Send summarize without session ID
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
last_user_message: 'Some message',
|
|
last_assistant_message: 'Some response'
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Returns error
|
|
expect(response.ok).toBe(false);
|
|
expect(response.status).toBe(400);
|
|
const error = await response.json();
|
|
expect(error.error).toContain('Missing claudeSessionId');
|
|
});
|
|
|
|
it('generates summary for different conversation types', async () => {
|
|
// Setup: Mock worker responses
|
|
const summaries: any[] = [];
|
|
global.fetch = vi.fn().mockImplementation(async (url, options) => {
|
|
const body = JSON.parse(options.body);
|
|
summaries.push(body);
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued', summaryId: summaries.length })
|
|
};
|
|
});
|
|
|
|
// Test different conversation scenarios
|
|
const scenarios = [
|
|
{
|
|
type: 'bug_fix',
|
|
user: 'Thanks for fixing the parser bug!',
|
|
assistant: 'I fixed the XML parser to handle self-closing tags in src/parser.ts:42.'
|
|
},
|
|
{
|
|
type: 'feature_addition',
|
|
user: 'Perfect! The search feature works great.',
|
|
assistant: 'I added FTS5 full-text search in src/services/search.ts.'
|
|
},
|
|
{
|
|
type: 'exploration',
|
|
user: 'That helps me understand the codebase better.',
|
|
assistant: 'The authentication flow uses JWT tokens stored in localStorage.'
|
|
},
|
|
{
|
|
type: 'refactoring',
|
|
user: 'Much cleaner now!',
|
|
assistant: 'I refactored the duplicate code into a shared utility function in src/utils/helpers.ts.'
|
|
}
|
|
];
|
|
|
|
// Execute: Send summary for each scenario
|
|
for (const scenario of scenarios) {
|
|
await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: `session-${scenario.type}`,
|
|
last_user_message: scenario.user,
|
|
last_assistant_message: scenario.assistant,
|
|
cwd: '/project'
|
|
})
|
|
}
|
|
);
|
|
}
|
|
|
|
// Verify: All summaries queued
|
|
expect(summaries.length).toBe(4);
|
|
expect(summaries[0].last_user_message).toContain('parser bug');
|
|
expect(summaries[1].last_user_message).toContain('search');
|
|
expect(summaries[2].last_user_message).toContain('understand');
|
|
expect(summaries[3].last_user_message).toContain('cleaner');
|
|
});
|
|
|
|
it('preserves long conversation context', async () => {
|
|
// Setup: Mock worker response
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued' })
|
|
});
|
|
|
|
// Execute: Send summary with long messages (realistic scenario)
|
|
const longAssistantMessage = `I've fixed the bug in the parser. Here's what I did:
|
|
|
|
1. Added null check for empty tags in src/parser.ts:42
|
|
2. Updated the regex pattern to handle self-closing tags
|
|
3. Added unit tests to verify the fix works
|
|
4. Ran the test suite and confirmed all tests pass
|
|
|
|
The issue was that the parser wasn't handling XML tags like <tag/> correctly.
|
|
It was only expecting <tag></tag> format. Now it handles both formats.`;
|
|
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionScenario.claudeSessionId,
|
|
last_user_message: 'Thanks for the detailed explanation!',
|
|
last_assistant_message: longAssistantMessage,
|
|
cwd: '/project'
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Long message preserved
|
|
expect(response.ok).toBe(true);
|
|
const fetchCall = (global.fetch as any).mock.calls[0];
|
|
const requestBody = JSON.parse(fetchCall[1].body);
|
|
expect(requestBody.last_assistant_message.length).toBeGreaterThan(200);
|
|
expect(requestBody.last_assistant_message).toContain('parser.ts:42');
|
|
expect(requestBody.last_assistant_message).toContain('self-closing tags');
|
|
});
|
|
|
|
it('handles empty or minimal messages gracefully', async () => {
|
|
// Setup: Mock worker response
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued' })
|
|
});
|
|
|
|
// Execute: Send summary with minimal messages
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionScenario.claudeSessionId,
|
|
last_user_message: 'Thanks!',
|
|
last_assistant_message: 'Done.',
|
|
cwd: '/project'
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Still processes minimal messages
|
|
expect(response.ok).toBe(true);
|
|
const result = await response.json();
|
|
expect(result.status).toBe('queued');
|
|
});
|
|
|
|
it('includes project context from cwd', async () => {
|
|
// Setup: Mock worker response
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued' })
|
|
});
|
|
|
|
const projectPath = '/Users/alice/projects/my-app';
|
|
|
|
// Execute: Send summary with project context
|
|
await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionScenario.claudeSessionId,
|
|
last_user_message: 'Great!',
|
|
last_assistant_message: 'Fixed the bug.',
|
|
cwd: projectPath
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Project context included
|
|
const fetchCall = (global.fetch as any).mock.calls[0];
|
|
const requestBody = JSON.parse(fetchCall[1].body);
|
|
expect(requestBody.cwd).toBe(projectPath);
|
|
});
|
|
});
|