Files
FrenoCorp/server/trpc/test-setup.ts
Michael Freno 79d153f75a 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
2026-04-24 08:31:42 -04:00

134 lines
4.2 KiB
TypeScript

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();
}