Struttura: il sito esistente si sposta sotto apps/sito
Primo passo della separazione sito / piattaforme decisa da Adriano il 03/09: due applicazioni autosufficienti, senza workspace di root, cosi' che ognuna resti un progetto Astro completo ed estraibile. Qui non cambia niente del codice: solo la radice. I pattern del .gitignore che avevano una barra erano ancorati alla radice e dopo lo spostamento non avrebbero piu' coperto niente (data/*.db*, uploads/*): resi validi a qualunque profondita'. Suite: 45 file, 347 test verdi prima e dopo lo spostamento. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import type Database from 'better-sqlite3';
|
||||
import { createDb } from '../src/lib/db';
|
||||
import { contentSeed } from '../src/data/content-seed';
|
||||
import { makeContentStore, syncSeed, resolveContent } from '../src/lib/content';
|
||||
|
||||
let db: Database.Database;
|
||||
beforeEach(() => { db = createDb(':memory:'); });
|
||||
|
||||
describe('seed', () => {
|
||||
it('tag univoci, validi e valori non vuoti', () => {
|
||||
const tags = contentSeed.map((e) => e.tag);
|
||||
expect(new Set(tags).size).toBe(tags.length);
|
||||
for (const e of contentSeed) {
|
||||
expect(e.tag).toMatch(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/);
|
||||
if (e.type !== 'image') expect(e.value.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('createDb sincronizza il seed senza sovrascrivere modifiche', () => {
|
||||
const n = (db.prepare('SELECT COUNT(*) AS n FROM content_blocks').get() as { n: number }).n;
|
||||
expect(n).toBe(contentSeed.length);
|
||||
db.prepare(`UPDATE content_blocks SET value = 'MODIFICATO' WHERE tag = ?`).run(contentSeed[0].tag);
|
||||
// ri-sync (come a un nuovo avvio) non deve toccare la modifica
|
||||
syncSeed(db);
|
||||
const v = db.prepare('SELECT value FROM content_blocks WHERE tag = ?').get(contentSeed[0].tag) as { value: string };
|
||||
expect(v.value).toBe('MODIFICATO');
|
||||
});
|
||||
});
|
||||
|
||||
describe('content store', () => {
|
||||
it('legge dal DB con classi preset e cache invalidabile', () => {
|
||||
const store = makeContentStore(() => db);
|
||||
const tag = contentSeed[0].tag;
|
||||
db.prepare(`UPDATE content_blocks SET value = 'Nuovo', styles = '{"size":"l"}' WHERE tag = ?`).run(tag);
|
||||
store.invalidate();
|
||||
const r = store.resolve(tag);
|
||||
expect(r.value).toBe('Nuovo');
|
||||
expect(r.classes).toBe('cs-size-l');
|
||||
// senza invalidate la cache resta
|
||||
db.prepare(`UPDATE content_blocks SET value = 'Altro' WHERE tag = ?`).run(tag);
|
||||
expect(store.resolve(tag).value).toBe('Nuovo');
|
||||
store.invalidate();
|
||||
expect(store.resolve(tag).value).toBe('Altro');
|
||||
});
|
||||
|
||||
it('fallback: tag assente nel DB usa il seed, sconosciuto stringa vuota', () => {
|
||||
const store = makeContentStore(() => db);
|
||||
db.prepare('DELETE FROM content_blocks WHERE tag = ?').run(contentSeed[0].tag);
|
||||
store.invalidate();
|
||||
expect(store.resolve(contentSeed[0].tag).value).toBe(contentSeed[0].value);
|
||||
expect(store.resolve('tag.inesistente').value).toBe('');
|
||||
});
|
||||
|
||||
it('styles corrotto nel DB viene ignorato', () => {
|
||||
const store = makeContentStore(() => db);
|
||||
db.prepare(`UPDATE content_blocks SET styles = 'non-json' WHERE tag = ?`).run(contentSeed[0].tag);
|
||||
store.invalidate();
|
||||
expect(store.resolve(contentSeed[0].tag).classes).toBe('');
|
||||
});
|
||||
|
||||
it('resolveContent restituisce il guid dal DB', () => {
|
||||
const r = resolveContent('home.hero.cta');
|
||||
expect(typeof r.guid).toBe('string');
|
||||
expect(r.guid.length).toBeGreaterThan(10);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user