import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { openDatabase, migrateDatabase } from '../src/server/db'; import { seedDatabase } from '../src/server/seed'; import { readContent, saveContent, resolvePublicRoute, deleteContent } from '../src/server/content'; import { importLegacy } from '../src/server/legacy-import'; function setup(t: { after: (fn: () => void) => void }) { const directory = mkdtempSync(path.join(tmpdir(), 'portfolio-test-')); const connection = openDatabase(directory); migrateDatabase(connection); seedDatabase(connection); t.after(() => { connection.sqlite.close(); rmSync(directory, { recursive: true, force: true }); }); return connection; } test('seed is idempotent and content saves are validated and immediately readable', t => { const connection = setup(t); const original = readContent(connection); const hero = original.sections.find(section => section.key === 'hero')!; hero.translations.en.name = 'Test owner'; saveContent(connection, 'sections', hero); seedDatabase(connection); assert.equal(resolvePublicRoute(readContent(connection)).portfolio.messages.hero.name, 'Test owner'); assert.throws(() => saveContent(connection, 'sections', { ...hero, translations: { en: { name: '' } } })); assert.equal(resolvePublicRoute(readContent(connection)).portfolio.messages.hero.name, 'Test owner'); assert.throws(() => importLegacy(connection), /before content has been edited/); saveContent(connection, 'sections', { ...hero, visible: false }); assert.ok(!JSON.stringify(resolvePublicRoute(readContent(connection)).portfolio).includes('Test owner')); }); test('runtime projects and arbitrary locales resolve, hidden and missing URLs do not', t => { const connection = setup(t); saveContent(connection, 'languages', { code: 'ar', name: 'العربية', direction: 'rtl', enabled: true, messages: { 'projects.viewCaseStudy': 'دراسة الحالة' } }); assert.equal(resolvePublicRoute(readContent(connection), ['ar']).portfolio.direction, 'rtl'); assert.equal(resolvePublicRoute(readContent(connection), ['ar']).portfolio.messages.projects.viewCaseStudy, 'دراسة الحالة'); const project = readContent(connection).entries.find(entry => entry.kind === 'projects')!; saveContent(connection, 'entries', { ...project, id: 'new-project' }); assert.equal(resolvePublicRoute(readContent(connection), ['ar','projects','new-project']).project?.id, 'new-project'); saveContent(connection, 'entries', { ...project, id: 'new-project', visible: false }); assert.throws(() => resolvePublicRoute(readContent(connection), ['projects','new-project']), /not found/); deleteContent(connection, 'entries', project.id); assert.throws(() => resolvePublicRoute(readContent(connection), ['projects',project.id]), /not found/); for (const parts of [['en'], ['unknown'], ['ar','bad'], ['admin']]) assert.throws(() => resolvePublicRoute(readContent(connection), parts)); assert.throws(() => saveContent(connection, 'languages', { code: 'en', name: 'English', direction: 'ltr', enabled: false, messages: {} })); }); test('legacy import preserves English and Persian projects and only runs once', t => { const connection = setup(t); importLegacy(connection); const snapshot = readContent(connection); assert.equal(resolvePublicRoute(snapshot, ['projects','qlockify']).project?.title, 'Qlockify'); assert.equal(resolvePublicRoute(snapshot, ['fa']).portfolio.direction, 'rtl'); assert.ok(snapshot.entries.length > 5); assert.throws(() => importLegacy(connection)); }); test('startup rejects a database migrated by an incompatible application image', t => { const connection = setup(t); connection.sqlite.prepare('INSERT INTO __drizzle_migrations (hash, created_at) VALUES (?, ?)').run('future-migration', Date.now() + 100000); assert.throws(() => migrateDatabase(connection), /do not match/); });