61 lines
4.0 KiB
TypeScript
61 lines
4.0 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, rmSync, writeFileSync } 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('external import preserves content and only runs once', t => {
|
|
const connection = setup(t);
|
|
const directory = mkdtempSync(path.join(tmpdir(), 'portfolio-bundle-'));
|
|
t.after(() => rmSync(directory, { recursive: true, force: true }));
|
|
const content = readContent(connection);
|
|
writeFileSync(path.join(directory, 'bundle.json'), JSON.stringify({ version: 1, content, files: [] }));
|
|
importLegacy(connection, directory, false, directory);
|
|
assert.deepEqual(readContent(connection), content);
|
|
assert.throws(() => importLegacy(connection, directory));
|
|
});
|
|
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/);
|
|
});
|