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:
AdrianoDev
2026-09-03 11:05:54 +00:00
parent c03edccd8e
commit 510d7ca4ec
551 changed files with 4 additions and 4 deletions
+78
View File
@@ -0,0 +1,78 @@
import { describe, it, expect, beforeEach } from 'vitest';
import type Database from 'better-sqlite3';
import { createDb } from '../src/lib/db';
import {
createPost, trashPost, restorePost, deletePost, getPostById, listAllPosts, listByAuthor,
listTrashed, listPublished, listRecent, listArchiveMonths, getPublishedBySlug,
countPublishedByCategory,
} from '../src/lib/posts';
let db: Database.Database;
let id: number;
const articolo = (slug: string, authorId: number | null = 1) => ({
title: `Titolo ${slug}`, slug, category: 'training', excerpt: 'x',
body_html: '<p>x</p>', draft: false, author_id: authorId,
});
beforeEach(() => {
db = createDb(':memory:');
id = createPost(db, articolo('uno'));
createPost(db, articolo('due', 2));
});
describe('cestino', () => {
it('cestinare non cancella: l\'articolo resta leggibile per id', () => {
trashPost(db, id);
const p = getPostById(db, id);
expect(p).toBeDefined();
expect(p!.deleted_at).not.toBeNull();
});
it('lo toglie da tutti gli elenchi del pannello', () => {
trashPost(db, id);
expect(listAllPosts(db).map((p) => p.slug)).toEqual(['due']);
expect(listByAuthor(db, 1)).toEqual([]);
expect(listTrashed(db).map((p) => p.slug)).toEqual(['uno']);
});
it('lo toglie dal sito pubblico: elenco, recenti, archivio e pagina singola', () => {
trashPost(db, id);
expect(listPublished(db).items.map((p) => p.slug)).toEqual(['due']);
expect(listPublished(db).total).toBe(1);
expect(listPublished(db, { category: 'training' }).total).toBe(1);
expect(listRecent(db).map((p) => p.slug)).toEqual(['due']);
expect(listArchiveMonths(db).reduce((n, m) => n + m.count, 0)).toBe(1);
expect(getPublishedBySlug(db, 'uno')).toBeUndefined();
});
it('il cestino di un autore contiene solo i suoi articoli', () => {
trashPost(db, id);
expect(listTrashed(db, 1).map((p) => p.slug)).toEqual(['uno']);
expect(listTrashed(db, 2)).toEqual([]);
});
it('il ripristino lo rimette dov\'era', () => {
trashPost(db, id);
restorePost(db, id);
expect(getPostById(db, id)!.deleted_at).toBeNull();
expect(listTrashed(db)).toEqual([]);
expect(listAllPosts(db).map((p) => p.slug)).toContain('uno');
expect(getPublishedBySlug(db, 'uno')).toBeDefined();
});
it('i conteggi per categoria escludono cestino e bozze', () => {
expect(countPublishedByCategory(db)).toEqual({ training: 2 });
trashPost(db, id);
expect(countPublishedByCategory(db)).toEqual({ training: 1 });
restorePost(db, id);
expect(countPublishedByCategory(db)).toEqual({ training: 2 });
});
it('la cancellazione definitiva rimuove davvero la riga', () => {
trashPost(db, id);
deletePost(db, id);
expect(getPostById(db, id)).toBeUndefined();
expect(listTrashed(db)).toEqual([]);
});
});