Andrea Grossi
← Torna ai progetti

Lavoro · 2026

Questo sito: un portfolio trilingue i cui test girano sul build

Astro 5, tre lingue con URL localizzati, e una suite di test che legge dist/ invece del sorgente. Compreso il bug che quella suite non poteva vedere, e che ha mandato lorem ipsum in produzione.

Il sito che stai leggendo. È un sito Astro 5, prerenderizzato in HTML statico, pubblicato in inglese, italiano e francese. Lo racconto qui soprattutto per una decisione e un fallimento.

URL che appartengono alla loro lingua

L’inglese sta alla radice senza prefisso; l’italiano vive sotto /it/, il francese sotto /fr/. La parte che costa lavoro vero è che anche i segmenti di percorso sono tradotti, non solo i contenuti:

IngleseItalianoFrancese
/projects//it/progetti//fr/projets/
/about//it/chi-sono//fr/a-propos/
/hobbies//it/hobby//fr/loisirs/

Un progetto è un file Markdown per lingua che condivide uno slug, ed è quello a permettere alle tre versioni di puntarsi a vicenda. Una pagina deve esistere solo nelle lingue per cui c’è contenuto: l’hreflang viene emesso verso le traduzioni che esistono davvero, mai verso un URL che darebbe 404. Ogni pagina referenzia anche se stessa, cosa che la specifica richiede e che è la regola che più spesso viene saltata.

I test leggono il build, non il sorgente

La suite — quindici file, 64 test sul test runner di Node — lancia prima npm run build e poi legge dist/. Non c’è niente di simulato, perché la cosa che vale la pena verificare è l’HTML che un browser riceverà davvero.

Controlla che nessun link interno sia rotto, che ogni pagina abbia title e description non vuoti e che quelle di primo livello siano distinte, che ogni immagine referenziata localmente esista su disco, che i set di hreflang siano completi e auto-referenzianti, che i draft non finiscano mai nel build, e che il tema sia applicato prima del primo paint perché la pagina non lampeggi bianca.

Verificare sul build intercetta un’intera categoria di bug che i test sul sorgente si perdono: un link corretto dentro un componente e sbagliato dopo il routing, un percorso immagine che si risolveva in sviluppo e non nell’output.

Il bug che i test non potevano vedere

Ecco la parte che vale la pena raccontare. Per settimane questo sito ha servito cinque progetti chiamati cose tipo “Lorem ipsum ingestion pipeline”, e il footer di ogni singola pagina diceva “Lorem ipsum dolor sit amet”. La suite di test è rimasta verde per tutto il tempo.

Era verde perché ogni asserzione era strutturale. I fixture che i test usavano per verificare il comportamento — un progetto pubblicato, uno non in evidenza, uno in bozza — erano gli stessi file che venivano pubblicati come contenuto. La suite chiedeva c’è un progetto elencato in questa pagina, e un progetto c’era. Non aveva nessuna opinione su cosa quel progetto dicesse.

È la trappola dell’usare i propri fixture come contenuto. I test continuano a passare, con precisione e onestà, mentre il sito non dice niente di vero. Una suite verde ti dice che la struttura regge; non può dirti che la struttura stia reggendo qualcosa di reale.

Quello che l’ha risolto non è stata un’asserzione più furba, è stato cancellare i fixture e puntare i test su progetti che esistono. La copertura sul comportamento è invariata — qualcosa deve comunque essere pubblicato-ma-non-in-evidenza perché quel test voglia dire qualcosa, e quel qualcosa adesso è questa pagina.

La pagina progetti quando i fixture erano ancora online Quello che /projects/ ha servito per settimane, ricostruito dal commit precedente alla pulizia. Tutti i test erano verdi su questa pagina.

Pubblicare apposta

Il build viene sincronizzato via rsync verso il suo host, e npm run deploy gira in --dry-run di default. Per pubblicare davvero serve un -- --live esplicito, e a quel punto lo script chiede all’operatore di digitare il nome del dominio prima di procedere.

Quell’attrito è voluto, perché rsync gira con --delete: tutto quello che sta sul server e non sta in dist/ sparisce. Un flag così distruttivo non dovrebbe stare a un refuso di distanza da una sera stanca.

Copertine senza una chiamata di rete

Ogni progetto, articolo e pagina disegna una copertina. Se il campo cover: non è impostato, si ricade su un SVG piatto generato in modo deterministico dallo slug da uno script — stesso slug, stessa immagine, ogni volta, senza scaricare font o immagini da nessuna parte. Un test fa fallire il build se una pagina referenzia una copertina che non esiste, ed è così che una copertina mancante diventa un test rosso invece che un’immagine rotta.