510d7ca4ec
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
51 lines
2.4 KiB
TypeScript
51 lines
2.4 KiB
TypeScript
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 { applyContentUpdate } from '../src/lib/content-update';
|
|
|
|
let db: Database.Database;
|
|
const textTag = contentSeed.find((e) => e.type === 'text')!.tag;
|
|
const htmlTag = contentSeed.find((e) => e.type === 'html')?.tag;
|
|
beforeEach(() => { db = createDb(':memory:'); });
|
|
|
|
describe('applyContentUpdate', () => {
|
|
it('aggiorna value e styles validi', () => {
|
|
const r = applyContentUpdate(db, textTag, { value: 'Nuovo testo', styles: { size: 'l' } }, 'admin');
|
|
expect(r).toEqual({ ok: true });
|
|
const row = db.prepare('SELECT value, styles, updated_by FROM content_blocks WHERE tag = ?').get(textTag) as any;
|
|
expect(row.value).toBe('Nuovo testo');
|
|
expect(JSON.parse(row.styles)).toEqual({ size: 'l' });
|
|
expect(row.updated_by).toBe('admin');
|
|
});
|
|
|
|
it('404 su tag inesistente, 400 su styles fuori whitelist', () => {
|
|
expect(applyContentUpdate(db, 'no.tag', { value: 'x' }, 'a')).toMatchObject({ ok: false, status: 404 });
|
|
expect(applyContentUpdate(db, textTag, { styles: { size: 'xxxl' } }, 'a')).toMatchObject({ ok: false, status: 400 });
|
|
});
|
|
|
|
it('sanifica i tag html e rifiuta value su tag image', () => {
|
|
if (htmlTag) {
|
|
applyContentUpdate(db, htmlTag, { value: 'ciao <script>alert(1)</script><strong>ok</strong>' }, 'a');
|
|
const row = db.prepare('SELECT value FROM content_blocks WHERE tag = ?').get(htmlTag) as any;
|
|
expect(row.value).not.toContain('<script>');
|
|
expect(row.value).toContain('<strong>ok</strong>');
|
|
}
|
|
const imgTag = contentSeed.find((e) => e.type === 'image')!.tag;
|
|
expect(applyContentUpdate(db, imgTag, { value: 'x' }, 'a')).toMatchObject({ ok: false, status: 400 });
|
|
});
|
|
});
|
|
|
|
describe('GET /api/admin/content/[tag]', () => {
|
|
it('GET /api/admin/content/[tag] ritorna tag, guid, type, value, styleOptions', async () => {
|
|
const { GET } = await import('../src/pages/api/admin/content/[tag]');
|
|
const res = await GET({ params: { tag: 'home.hero.cta' } } as any);
|
|
expect(res.status).toBe(200);
|
|
const body = await res.json();
|
|
expect(body.tag).toBe('home.hero.cta');
|
|
expect(typeof body.guid).toBe('string');
|
|
expect(body.type).toBeDefined();
|
|
expect(Array.isArray(body.styleOptions)).toBe(true);
|
|
});
|
|
});
|