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.
183 lines
5.7 KiB
TypeScript
183 lines
5.7 KiB
TypeScript
/**
|
|
* Happy Path Test: Session Initialization
|
|
*
|
|
* Tests that when a user's first tool use occurs, the session is
|
|
* created in the database and observations can be queued.
|
|
*/
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { bashCommandScenario, sessionScenario } from '../helpers/scenarios.js';
|
|
import { getWorkerPort } from '../../src/shared/worker-utils.js';
|
|
|
|
describe('Session Initialization (UserPromptSubmit)', () => {
|
|
const WORKER_PORT = getWorkerPort();
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('creates session when first observation is sent', async () => {
|
|
// This tests the happy path:
|
|
// User types first prompt → Tool runs → Hook sends observation →
|
|
// Worker creates session → Observation queued for SDK processing
|
|
|
|
// Setup: Mock successful response from worker
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued', sessionId: 1 })
|
|
});
|
|
|
|
// Execute: Send first observation (what save-hook does)
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionScenario.claudeSessionId,
|
|
tool_name: bashCommandScenario.tool_name,
|
|
tool_input: bashCommandScenario.tool_input,
|
|
tool_response: bashCommandScenario.tool_response,
|
|
cwd: '/project/claude-mem'
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Session created and observation queued
|
|
expect(response.ok).toBe(true);
|
|
const result = await response.json();
|
|
expect(result.status).toBe('queued');
|
|
expect(result.sessionId).toBeDefined();
|
|
|
|
// Verify: fetch was called with correct endpoint and data
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: expect.stringContaining(sessionScenario.claudeSessionId)
|
|
})
|
|
);
|
|
});
|
|
|
|
it('handles missing claudeSessionId gracefully', async () => {
|
|
// Setup: Mock error response for missing session ID
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: false,
|
|
status: 400,
|
|
json: async () => ({ error: 'Missing claudeSessionId' })
|
|
});
|
|
|
|
// Execute: Send observation without session ID
|
|
const response = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
tool_name: 'Bash',
|
|
tool_input: { command: 'ls' },
|
|
tool_response: { stdout: 'file.txt' }
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Returns 400 error
|
|
expect(response.ok).toBe(false);
|
|
expect(response.status).toBe(400);
|
|
const error = await response.json();
|
|
expect(error.error).toContain('Missing claudeSessionId');
|
|
});
|
|
|
|
it('queues multiple observations for the same session', async () => {
|
|
// Setup: Mock successful responses
|
|
let callCount = 0;
|
|
global.fetch = vi.fn().mockImplementation(async () => {
|
|
const currentId = ++callCount;
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued', observationId: currentId })
|
|
};
|
|
});
|
|
|
|
const sessionId = sessionScenario.claudeSessionId;
|
|
|
|
// Execute: Send multiple observations for the same session
|
|
const obs1 = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionId,
|
|
tool_name: 'Read',
|
|
tool_input: { file_path: '/test.ts' },
|
|
tool_response: { content: 'code...' }
|
|
})
|
|
}
|
|
);
|
|
|
|
const obs2 = await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionId,
|
|
tool_name: 'Edit',
|
|
tool_input: { file_path: '/test.ts', old_string: 'old', new_string: 'new' },
|
|
tool_response: { success: true }
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Both observations were queued successfully
|
|
expect(obs1.ok).toBe(true);
|
|
expect(obs2.ok).toBe(true);
|
|
|
|
const result1 = await obs1.json();
|
|
const result2 = await obs2.json();
|
|
|
|
expect(result1.status).toBe('queued');
|
|
expect(result2.status).toBe('queued');
|
|
expect(result1.observationId).toBe(1);
|
|
expect(result2.observationId).toBe(2);
|
|
});
|
|
|
|
it('includes project context from cwd', async () => {
|
|
// Setup: Mock successful response
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ status: 'queued' })
|
|
});
|
|
|
|
const projectPath = '/Users/alice/projects/my-app';
|
|
|
|
// Execute: Send observation with cwd
|
|
await fetch(
|
|
`http://127.0.0.1:${WORKER_PORT}/api/sessions/observations`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
claudeSessionId: sessionScenario.claudeSessionId,
|
|
tool_name: 'Bash',
|
|
tool_input: { command: 'npm test' },
|
|
tool_response: { stdout: 'PASS', exit_code: 0 },
|
|
cwd: projectPath
|
|
})
|
|
}
|
|
);
|
|
|
|
// Verify: Request includes cwd
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
expect.any(String),
|
|
expect.objectContaining({
|
|
body: expect.stringContaining(projectPath)
|
|
})
|
|
);
|
|
});
|
|
});
|