mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-03-27 10:05:50 +00:00
test(db): add self-healing tests for missing repository prompt table
- Test runtime self-healing when repository_review_prompts table is dropped - Test migration layer self-healing for inconsistent DB state - Verify repository listing remains functional during schema recovery Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { existsSync, mkdirSync, unlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { closeDatabase, getDatabase, initDatabase } from '../database';
|
||||
|
||||
function createInconsistentMigrationState(dbPath: string): void {
|
||||
const db = new Database(dbPath);
|
||||
db.exec('PRAGMA foreign_keys = ON');
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
version INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
`);
|
||||
|
||||
db.query('INSERT INTO _migrations (version, name) VALUES (?, ?)').run(
|
||||
1,
|
||||
'init_llm_provider_schema'
|
||||
);
|
||||
db.query('INSERT INTO _migrations (version, name) VALUES (?, ?)').run(
|
||||
2,
|
||||
'remove_legacy_review_mode'
|
||||
);
|
||||
db.query('INSERT INTO _migrations (version, name) VALUES (?, ?)').run(
|
||||
3,
|
||||
'add_repository_review_prompts'
|
||||
);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
describe('migration self-heal for repository review prompts', () => {
|
||||
let dbPath: string;
|
||||
const savedDbPath = process.env.DATABASE_PATH;
|
||||
|
||||
beforeEach(() => {
|
||||
const tmpDir = join(tmpdir(), `db-migration-heal-${randomUUID()}`);
|
||||
mkdirSync(tmpDir, { recursive: true });
|
||||
dbPath = join(tmpDir, 'test.db');
|
||||
process.env.DATABASE_PATH = dbPath;
|
||||
createInconsistentMigrationState(dbPath);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
closeDatabase();
|
||||
if (savedDbPath === undefined) {
|
||||
Reflect.deleteProperty(process.env, 'DATABASE_PATH');
|
||||
} else {
|
||||
process.env.DATABASE_PATH = savedDbPath;
|
||||
}
|
||||
|
||||
if (existsSync(dbPath)) unlinkSync(dbPath);
|
||||
if (existsSync(`${dbPath}-wal`)) unlinkSync(`${dbPath}-wal`);
|
||||
if (existsSync(`${dbPath}-shm`)) unlinkSync(`${dbPath}-shm`);
|
||||
});
|
||||
|
||||
test('rebuilds missing repository_review_prompts table even when migration 3 is marked applied', () => {
|
||||
initDatabase();
|
||||
const db = getDatabase();
|
||||
|
||||
const tableRow = db
|
||||
.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
|
||||
.get('repository_review_prompts') as { name: string } | null;
|
||||
expect(tableRow?.name).toBe('repository_review_prompts');
|
||||
|
||||
const migrationCountRow = db
|
||||
.query('SELECT COUNT(*) AS count FROM _migrations WHERE version = ?')
|
||||
.get(3) as { count: number } | null;
|
||||
expect(migrationCountRow?.count).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { existsSync, mkdirSync, unlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { closeDatabase, initDatabase } from '../database';
|
||||
import { closeDatabase, getDatabase, initDatabase } from '../database';
|
||||
import { repositoryReviewPromptRepo } from '../repositories/repository-review-prompt-repo';
|
||||
|
||||
describe('repository-review-prompt-repo', () => {
|
||||
@@ -64,4 +64,30 @@ describe('repository-review-prompt-repo', () => {
|
||||
'acme/b': 'prompt-b',
|
||||
});
|
||||
});
|
||||
|
||||
test('self-heals missing prompt table and keeps repository listing readable', () => {
|
||||
const db = getDatabase();
|
||||
db.exec('DROP TABLE repository_review_prompts');
|
||||
|
||||
const map = repositoryReviewPromptRepo.listProjectPrompts(['acme/a']);
|
||||
expect(map).toEqual({});
|
||||
|
||||
repositoryReviewPromptRepo.setProjectPrompt('acme', 'a', 'prompt-a');
|
||||
expect(repositoryReviewPromptRepo.getProjectPrompt('acme', 'a')).toBe('prompt-a');
|
||||
});
|
||||
|
||||
test('self-heals missing prompt table for direct prompt write path', () => {
|
||||
const db = getDatabase();
|
||||
db.exec('DROP TABLE repository_review_prompts');
|
||||
|
||||
const row = repositoryReviewPromptRepo.setProjectPrompt(
|
||||
'acme',
|
||||
'direct-write',
|
||||
'prompt-direct'
|
||||
);
|
||||
expect(row.project_prompt).toBe('prompt-direct');
|
||||
expect(repositoryReviewPromptRepo.getProjectPrompt('acme', 'direct-write')).toBe(
|
||||
'prompt-direct'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user