FRE-592: Fix remaining code review blockers and add tests
- Replace in-memory Maps with Drizzle ORM queries for all CRUD operations - Use integer IDs matching SQLite schema instead of UUIDs - Fix scriptId to projectId inconsistency in characters and scenes - Add project ownership verification on all mutation procedures - Make getCharacter/getScene procedures protected (not public) - Proper JWT-based userId validation via context - Add cascade delete for characters/relationships/scenes on project deletion - Add verifyProjectOwnership helper for authorization checks - Rewrite tests with createCallerFactory pattern for tRPC v11 - Use better-sqlite3 for in-memory test database - Split vitest config into separate file from vite config
This commit is contained in:
133
server/trpc/test-setup.ts
Normal file
133
server/trpc/test-setup.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
let testDb: ReturnType<typeof drizzle> | null = null;
|
||||
let sqlite: Database.Database | null = null;
|
||||
|
||||
const schemaSQL = `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS projects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
owner_id INTEGER NOT NULL REFERENCES users(id),
|
||||
is_public INTEGER NOT NULL DEFAULT 0,
|
||||
theme TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS characters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL REFERENCES projects(id),
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'supporting',
|
||||
bio TEXT,
|
||||
description TEXT,
|
||||
arc TEXT,
|
||||
arc_type TEXT,
|
||||
age INTEGER,
|
||||
gender TEXT,
|
||||
voice TEXT,
|
||||
traits TEXT,
|
||||
motivation TEXT,
|
||||
conflict TEXT,
|
||||
secret TEXT,
|
||||
image_url TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS character_relationships (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
character_a_id INTEGER NOT NULL REFERENCES characters(id),
|
||||
character_b_id INTEGER NOT NULL REFERENCES characters(id),
|
||||
relationship_type TEXT NOT NULL,
|
||||
description TEXT,
|
||||
strength INTEGER NOT NULL DEFAULT 50,
|
||||
is_antagonistic INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scenes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL REFERENCES projects(id),
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
"order" INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scene_characters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
scene_id INTEGER NOT NULL REFERENCES scenes(id),
|
||||
character_id INTEGER NOT NULL REFERENCES characters(id),
|
||||
screen_time INTEGER,
|
||||
dialogue_lines INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scripts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL REFERENCES projects(id),
|
||||
title TEXT NOT NULL,
|
||||
version TEXT NOT NULL DEFAULT '1.0',
|
||||
content TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS revisions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
script_id INTEGER NOT NULL REFERENCES scripts(id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
version TEXT NOT NULL,
|
||||
content TEXT,
|
||||
created_by INTEGER NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS revision_changes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
revision_id INTEGER NOT NULL REFERENCES revisions(id),
|
||||
change_type TEXT NOT NULL,
|
||||
description TEXT,
|
||||
scene_number INTEGER,
|
||||
line_number INTEGER,
|
||||
old_content TEXT,
|
||||
new_content TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`;
|
||||
|
||||
export async function getTestDb(): Promise<ReturnType<typeof drizzle>> {
|
||||
if (testDb && sqlite) return testDb;
|
||||
|
||||
sqlite = new Database(':memory:');
|
||||
sqlite.exec('PRAGMA foreign_keys = OFF;');
|
||||
sqlite.exec(schemaSQL);
|
||||
sqlite.exec('PRAGMA foreign_keys = ON;');
|
||||
|
||||
// Insert a test user
|
||||
sqlite.exec("INSERT INTO users (id, email, name) VALUES (1, 'test@test.com', 'Test User');");
|
||||
|
||||
testDb = drizzle(sqlite);
|
||||
|
||||
return testDb;
|
||||
}
|
||||
|
||||
export async function resetTestDb(): Promise<ReturnType<typeof drizzle>> {
|
||||
testDb = null;
|
||||
sqlite = null;
|
||||
return getTestDb();
|
||||
}
|
||||
Reference in New Issue
Block a user