Pablan, as it stands
Self-hosted knowledge management for SMEs: a split-screen Markdown editor whose sections an LLM refines while you write, and RAG question answering over the documents that result. FastAPI + Postgres/pgvector on the back, SvelteKit on the front, everything OpenAI-compatible and self-hostable. Squashed into a single commit; the development history stays local. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA43ZJda8Rbp2hKXNy8f6b
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// Zero-residue guard: the e2e suite must leave the dev database exactly as
|
||||
// it found it. globalSetup snapshots row counts, globalTeardown re-counts
|
||||
// and fails the run on any difference.
|
||||
//
|
||||
// Documented exception: done/failed rows in `jobs` are execution history
|
||||
// (the queue's audit trail); only unprocessed jobs count as residue.
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const TABLES = [
|
||||
'users',
|
||||
'departments',
|
||||
'templates',
|
||||
'conversations',
|
||||
'messages',
|
||||
'documents',
|
||||
'chunks',
|
||||
'doc_permissions',
|
||||
'auth_sessions',
|
||||
'llm_settings'
|
||||
];
|
||||
|
||||
const SNAPSHOT = path.join(import.meta.dirname, '..', 'test-results', 'row-counts.json');
|
||||
|
||||
function query(sql: string): string {
|
||||
return execSync(`docker exec pablan-dev-postgres-1 psql -tA -U pablan -d pablan -c "${sql}"`)
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function snapshotCounts(): Record<string, number> {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const table of TABLES) {
|
||||
counts[table] = Number(query(`SELECT count(*) FROM ${table}`));
|
||||
}
|
||||
counts['jobs (unprocessed)'] = Number(
|
||||
query(`SELECT count(*) FROM jobs WHERE status IN ('pending','running')`)
|
||||
);
|
||||
return counts;
|
||||
}
|
||||
|
||||
export async function globalSetup(): Promise<void> {
|
||||
fs.mkdirSync(path.dirname(SNAPSHOT), { recursive: true });
|
||||
fs.writeFileSync(SNAPSHOT, JSON.stringify(snapshotCounts(), null, 2));
|
||||
}
|
||||
|
||||
export async function globalTeardown(): Promise<void> {
|
||||
const before = JSON.parse(fs.readFileSync(SNAPSHOT, 'utf-8')) as Record<string, number>;
|
||||
const after = snapshotCounts();
|
||||
const diffs = Object.keys(after)
|
||||
.filter((key) => before[key] !== after[key])
|
||||
.map((key) => ` ${key}: ${before[key]} -> ${after[key]}`);
|
||||
if (diffs.length > 0) {
|
||||
throw new Error(`e2e suite left residue in the database:\n${diffs.join('\n')}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user