Files
claude-mem/tests/hooks-database-integration.test.ts
T
Alex Newman 01b477da26 feat: Add build and publish scripts for claude-mem
- Implemented build.js to bundle TypeScript source into a minified executable using Bun.
- Created publish.js to handle version bumping, building, and publishing to npm with user prompts.
- Added tests for database schema and hook functions in database-schema.test.ts.
- Introduced integration tests for hooks database in hooks-database-integration.test.ts.
- Developed end-to-end tests for SDK prompts and parser in sdk-prompts-parser.test.ts.
- Created session lifecycle tests to simulate complete Claude Code session in session-lifecycle.test.ts.
2025-10-15 20:23:32 -04:00

254 lines
7.9 KiB
TypeScript

#!/usr/bin/env bun
/**
* Phase 3 Integration Tests
* Tests the complete hook lifecycle and end-to-end integration
*
* Note: These tests verify database integration rather than calling hooks directly
* since hooks call process.exit() which would terminate the test process
*/
import { describe, it, expect, beforeAll, afterAll } from 'bun:test';
import { HooksDatabase } from './src/services/sqlite/HooksDatabase.js';
import { DatabaseManager } from './src/services/sqlite/Database.js';
import { migrations } from './src/services/sqlite/migrations.js';
import fs from 'fs';
import path from 'path';
// Test database path
const TEST_DB_DIR = '/tmp/claude-mem-phase3-test';
const TEST_DB_PATH = path.join(TEST_DB_DIR, 'claude-mem.db');
describe('Phase 3: Hook Database Integration', () => {
beforeAll(async () => {
// Create test directory
fs.mkdirSync(TEST_DB_DIR, { recursive: true });
// Set test environment
process.env.CLAUDE_MEM_DATA_DIR = TEST_DB_DIR;
// Initialize database with migrations
const dbManager = DatabaseManager.getInstance();
migrations.forEach(m => dbManager.registerMigration(m));
await dbManager.initialize();
dbManager.close();
});
afterAll(() => {
// Clean up test database and all files
if (fs.existsSync(TEST_DB_DIR)) {
const files = fs.readdirSync(TEST_DB_DIR);
files.forEach(file => {
fs.unlinkSync(path.join(TEST_DB_DIR, file));
});
fs.rmdirSync(TEST_DB_DIR);
}
});
describe('HooksDatabase - Session Management', () => {
it('should create and find SDK sessions', () => {
const db = new HooksDatabase();
const sessionId = db.createSDKSession(
'test-claude-session-1',
'my-project',
'Implement authentication'
);
expect(sessionId).toBeGreaterThan(0);
const found = db.findActiveSDKSession('test-claude-session-1');
expect(found).not.toBeNull();
expect(found!.project).toBe('my-project');
expect(found!.id).toBe(sessionId);
db.close();
});
it('should update SDK session ID', () => {
const db = new HooksDatabase();
const sessionId = db.createSDKSession(
'test-claude-session-2',
'my-project',
'Test prompt'
);
db.updateSDKSessionId(sessionId, 'sdk-session-abc');
const found = db.findActiveSDKSession('test-claude-session-2');
expect(found!.sdk_session_id).toBe('sdk-session-abc');
db.close();
});
it('should mark session as completed', () => {
const db = new HooksDatabase();
const sessionId = db.createSDKSession(
'test-claude-session-3',
'my-project',
'Test prompt'
);
db.markSessionCompleted(sessionId);
const found = db.findActiveSDKSession('test-claude-session-3');
expect(found).toBeNull(); // Should not find active session
db.close();
});
});
describe('HooksDatabase - Observation Queue', () => {
it('should queue and retrieve observations', () => {
const db = new HooksDatabase();
// Create session first (FK constraint requirement)
const sessionId = db.createSDKSession('claude-queue-1', 'test-project', 'Test');
db.updateSDKSessionId(sessionId, 'sdk-queue-test-1');
db.queueObservation(
'sdk-queue-test-1',
'Read',
JSON.stringify({ file_path: 'src/app.ts' }),
JSON.stringify({ content: 'test content' })
);
const pending = db.getPendingObservations('sdk-queue-test-1', 10);
expect(pending).toHaveLength(1);
expect(pending[0].tool_name).toBe('Read');
db.close();
});
it('should mark observations as processed', () => {
const db = new HooksDatabase();
// Create session first (FK constraint requirement)
const sessionId = db.createSDKSession('claude-queue-2', 'test-project', 'Test');
db.updateSDKSessionId(sessionId, 'sdk-queue-test-2');
db.queueObservation(
'sdk-queue-test-2',
'Edit',
JSON.stringify({ file_path: 'src/app.ts' }),
JSON.stringify({ success: true })
);
const pending = db.getPendingObservations('sdk-queue-test-2', 10);
expect(pending).toHaveLength(1);
db.markObservationProcessed(pending[0].id);
const stillPending = db.getPendingObservations('sdk-queue-test-2', 10);
expect(stillPending).toHaveLength(0);
db.close();
});
it('should queue FINALIZE messages', () => {
const db = new HooksDatabase();
// Create session first (FK constraint requirement)
const sessionId = db.createSDKSession('claude-finalize', 'test-project', 'Test');
db.updateSDKSessionId(sessionId, 'sdk-finalize-test');
db.queueObservation('sdk-finalize-test', 'FINALIZE', '{}', '{}');
const pending = db.getPendingObservations('sdk-finalize-test', 10);
expect(pending).toHaveLength(1);
expect(pending[0].tool_name).toBe('FINALIZE');
db.close();
});
});
describe('HooksDatabase - Observations Storage', () => {
it('should store observations from SDK', () => {
const db = new HooksDatabase();
// Create session first (FK constraint requirement)
const sessionId = db.createSDKSession('claude-obs-1', 'my-project', 'Test');
db.updateSDKSessionId(sessionId, 'sdk-obs-test-1');
db.storeObservation(
'sdk-obs-test-1',
'my-project',
'feature',
'Implemented JWT authentication'
);
const dbInstance = (db as any).db;
const query = dbInstance.query('SELECT * FROM observations WHERE sdk_session_id = ?');
const observations = query.all('sdk-obs-test-1');
expect(observations).toHaveLength(1);
expect(observations[0].type).toBe('feature');
expect(observations[0].text).toBe('Implemented JWT authentication');
db.close();
});
});
describe('HooksDatabase - Summaries', () => {
it('should store and retrieve summaries', () => {
const db = new HooksDatabase();
// Create session first (FK constraint requirement)
const sessionId = db.createSDKSession('claude-summary-1', 'my-project', 'Test');
db.updateSDKSessionId(sessionId, 'sdk-summary-test-1');
db.storeSummary('sdk-summary-test-1', 'my-project', {
request: 'Implement authentication',
investigated: 'Existing patterns',
learned: 'No JWT support',
completed: 'Implemented JWT',
next_steps: 'Add tests',
files_read: JSON.stringify(['src/auth.ts']),
files_edited: JSON.stringify(['src/auth.ts']),
notes: 'Used bcrypt'
});
const summaries = db.getRecentSummaries('my-project', 10);
expect(summaries.length).toBeGreaterThan(0);
const summary = summaries.find(s => s.request === 'Implement authentication');
expect(summary).not.toBeUndefined();
expect(summary!.completed).toBe('Implemented JWT');
db.close();
});
it('should return recent summaries only for specific project', () => {
const db = new HooksDatabase();
// Create sessions first (FK constraint requirement)
const session1Id = db.createSDKSession('claude-proj-1', 'project-1', 'Test');
db.updateSDKSessionId(session1Id, 'sdk-proj1');
const session2Id = db.createSDKSession('claude-proj-2', 'project-2', 'Test');
db.updateSDKSessionId(session2Id, 'sdk-proj2');
db.storeSummary('sdk-proj1', 'project-1', {
request: 'Feature for project 1',
completed: 'Done'
});
db.storeSummary('sdk-proj2', 'project-2', {
request: 'Feature for project 2',
completed: 'Done'
});
const proj1Summaries = db.getRecentSummaries('project-1', 10);
const proj2Summaries = db.getRecentSummaries('project-2', 10);
expect(proj1Summaries.every(s => s.request?.includes('project 1'))).toBe(true);
expect(proj2Summaries.every(s => s.request?.includes('project 2'))).toBe(true);
db.close();
});
});
});
console.log('Running Phase 3 Integration Tests...');