111 lines
3.0 KiB
TypeScript
111 lines
3.0 KiB
TypeScript
import { Database } from 'bun:sqlite';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
|
|
import { applyBaseSchema } from './base-schema.js';
|
|
import { initializeDatabaseSchema } from './bootstrap.js';
|
|
import { applyLegacySchemaMigrations } from './schema.js';
|
|
|
|
function getAppliedSchemaMigrations(
|
|
database: Database,
|
|
): Array<{ version: number; name: string }> {
|
|
return database
|
|
.prepare(
|
|
`SELECT version, name
|
|
FROM schema_migrations
|
|
ORDER BY version ASC`,
|
|
)
|
|
.all() as Array<{ version: number; name: string }>;
|
|
}
|
|
|
|
function getExpectedSchemaMigrations(): Array<{
|
|
version: number;
|
|
name: string;
|
|
}> {
|
|
return [
|
|
{ version: 1, name: 'legacy_schema_bundle' },
|
|
{ version: 2, name: 'scheduled_task_columns' },
|
|
{ version: 3, name: 'room_registration_canonical_columns' },
|
|
{ version: 4, name: 'message_seq_and_work_item_indexes' },
|
|
{ version: 5, name: 'sessions_composite_key' },
|
|
{ version: 6, name: 'chat_channel_metadata' },
|
|
{ version: 7, name: 'runtime_service_metadata' },
|
|
{ version: 8, name: 'paired_task_schema_cleanup' },
|
|
{ version: 9, name: 'paired_workspace_project_schema_cleanup' },
|
|
{ version: 10, name: 'paired_turn_provenance_upgrade' },
|
|
{ version: 11, name: 'owner_failure_count' },
|
|
{ version: 12, name: 'paired_verdict_and_step_telemetry' },
|
|
];
|
|
}
|
|
|
|
describe('initializeDatabaseSchema', () => {
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const tempDir of tempDirs.splice(0)) {
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('records the legacy schema bundle as version 1 on a fresh database', () => {
|
|
const database = new Database(':memory:');
|
|
|
|
try {
|
|
initializeDatabaseSchema(database);
|
|
|
|
expect(getAppliedSchemaMigrations(database)).toEqual(
|
|
getExpectedSchemaMigrations(),
|
|
);
|
|
} finally {
|
|
database.close();
|
|
}
|
|
});
|
|
|
|
it('does not duplicate schema migration rows on repeated initialization', () => {
|
|
const database = new Database(':memory:');
|
|
|
|
try {
|
|
initializeDatabaseSchema(database);
|
|
initializeDatabaseSchema(database);
|
|
|
|
expect(getAppliedSchemaMigrations(database)).toEqual(
|
|
getExpectedSchemaMigrations(),
|
|
);
|
|
} finally {
|
|
database.close();
|
|
}
|
|
});
|
|
|
|
it('backfills schema migration tracking for an existing pre-versioned database', () => {
|
|
const tempDir = fs.mkdtempSync(
|
|
path.join('/tmp', 'ejclaw-schema-migrations-'),
|
|
);
|
|
tempDirs.push(tempDir);
|
|
const dbPath = path.join(tempDir, 'messages.db');
|
|
const legacyDb = new Database(dbPath);
|
|
|
|
try {
|
|
applyBaseSchema(legacyDb);
|
|
applyLegacySchemaMigrations(legacyDb, {
|
|
assistantName: 'Andy',
|
|
});
|
|
} finally {
|
|
legacyDb.close();
|
|
}
|
|
|
|
const reopened = new Database(dbPath);
|
|
|
|
try {
|
|
initializeDatabaseSchema(reopened);
|
|
|
|
expect(getAppliedSchemaMigrations(reopened)).toEqual(
|
|
getExpectedSchemaMigrations(),
|
|
);
|
|
} finally {
|
|
reopened.close();
|
|
}
|
|
});
|
|
});
|