feat: rewrite database schema synchronization with automatic recovery (#379)
Complete rewrite of the Hermes SQLite database schema synchronization mechanism with comprehensive error handling, automatic recovery, and full test coverage. ## Database Schema Synchronization - **Unified sync mechanism**: Single `syncTable()` function handles all schema changes - **Automatic column sync**: Adds missing columns and removes extra columns - **Table rebuilding**: Automatically rebuilds tables when primary keys or types change - **Data preservation**: Preserves data during schema changes when compatible - **Index management**: Creates and removes indexes as needed ## Error Recovery & Reliability - **Automatic backup**: Backs up corrupted database before recovery - **Retry limiting**: Prevents infinite loops with retry limit - **Duplicate prevention**: Avoids multiple backup files - **Safe file operations**: Uses copy+delete instead of rename for safety ## Composite Primary Keys - Fixed GC_ROOM_AGENTS and GC_ROOM_MEMBERS with proper composite primary keys - Prevents duplicate entries while allowing same roomId with different agentId/userId ## Test Coverage - **10 new integration tests** for schema synchronization (tests/server/schema-sync.test.ts) - **3 updated tests** for Hermes schemas (tests/server/hermes-schemas.test.ts) - All 327 tests passing (47 test files, 325 passed, 2 skipped) ## Bug Fixes - Fixed module import issues (unified ES6 imports, removed mixed require()) - Fixed mock issues in sessions routes tests - Fixed i18n coverage test to handle newly added keys - Fixed profiles store test to match current implementation Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ const listConversationsMock = vi.fn(async (ctx: any) => { ctx.body = { sessions:
|
||||
const getConversationMessagesMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id, messages: [] } })
|
||||
const getConversationMessagesPaginatedMock = vi.fn(async (ctx: any) => { ctx.body = { session_id: ctx.params.id, messages: [], pagination: {} } })
|
||||
const listMock = vi.fn(async (ctx: any) => { ctx.body = { sessions: [{ id: 's1' }] } })
|
||||
const listHermesSessionsMock = vi.fn(async (ctx: any) => { ctx.body = { sessions: [{ id: 'hermes-1' }] } })
|
||||
const getHermesSessionMock = vi.fn(async (ctx: any) => { ctx.body = { session: { id: ctx.params.id } } })
|
||||
const searchMock = vi.fn(async (ctx: any) => { ctx.body = { results: [{ id: 'search-1' }] } })
|
||||
const getMock = vi.fn(async (ctx: any) => { ctx.body = { session: { id: ctx.params.id } } })
|
||||
const removeMock = vi.fn(async (ctx: any) => { ctx.body = { ok: true } })
|
||||
@@ -20,6 +22,8 @@ vi.mock('../../packages/server/src/controllers/hermes/sessions', () => ({
|
||||
getConversationMessages: getConversationMessagesMock,
|
||||
getConversationMessagesPaginated: getConversationMessagesPaginatedMock,
|
||||
list: listMock,
|
||||
listHermesSessions: listHermesSessionsMock,
|
||||
getHermesSession: getHermesSessionMock,
|
||||
search: searchMock,
|
||||
get: getMock,
|
||||
remove: removeMock,
|
||||
|
||||
Reference in New Issue
Block a user