Documentazione: strategia di test e regola "nuova funzione = nuovi test"
Aggiunge ADR-0003 (xUnit, Avalonia.Headless, coverlet, FakeClaude come doppio di claude, struttura src/tests) e docs/TESTING.md con la regola, le soglie di copertura e la matrice requisito-test. Ogni criterio di accettazione dell'MVP è collegato alle righe della matrice. La roadmap ha la Fase 6 e CLAUDE.md rende obbligatori i test nello stesso commit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
941d02110d
commit
84fefda219
6 files changed
+256
-4
No files matched your search
@@ -9,6 +9,7 @@ agent-wiki-ui (già `LlmRun`; il nome ha un'unica sorgente, vedi [docs/README.md
|
||||
## Workflow doc-first (obbligatorio)
|
||||
|
||||
- Specifica e criteri di accettazione: [docs/MVP.md](docs/MVP.md). Fasi e stato: [docs/ROADMAP.md](docs/ROADMAP.md). Indice dei doc e regole (SSOT, ADR): [docs/README.md](docs/README.md); una decisione architetturale si scrive solo nel suo ADR, gli altri doc la richiamano con un link. Uso: [README.md](README.md).
|
||||
- **Test obbligatori** ([docs/TESTING.md](docs/TESTING.md), [ADR-0003](docs/adr/0003-strategia-di-test.md)): ogni nuova funzione, correzione o cambio di comportamento va coperta da test nello stesso commit; prima si aggiunge la riga (con ID) alla matrice in TESTING.md, poi i test, poi il codice. Un bug si corregge partendo da un test che fallisce.
|
||||
- Ad ogni richiesta di modifica: aggiornare prima i doc interessati (MVP.md, ROADMAP.md con le spunte, README.md se cambia l'uso; un nuovo ADR in `docs/adr/` per ogni scelta architetturale), poi il codice, poi questo file se cambiano comandi, design o vincoli. Nessuna modifica deve lasciare i doc disallineati.
|
||||
|
||||
## Comandi
|
||||
@@ -16,7 +17,7 @@ agent-wiki-ui (già `LlmRun`; il nome ha un'unica sorgente, vedi [docs/README.md
|
||||
- Build: `dotnet build` (agent-wiki-ui.csproj, `net10.0`, Avalonia)
|
||||
- GUI: `dotnet run`; CLI: `dotnet run -- "prompt"`
|
||||
- Publish single-file (uno per sistema): `dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true` e `-r linux-x64`
|
||||
- Test: non esistono ancora. Per verificare la GUI senza schermo si può usare `Avalonia.Headless` con `CaptureRenderedFrame()` in un progetto di prova fuori dal repo.
|
||||
- Test: non esistono ancora (pianificati, Fase 6: `dotnet test`; il piano è in docs/TESTING.md). Per verificare la GUI senza schermo si può usare `Avalonia.Headless` con `CaptureRenderedFrame()` in un progetto di prova fuori dal repo.
|
||||
|
||||
## Architettura
|
||||
|
||||
|
||||
+3
-1
@@ -51,9 +51,11 @@ Le scelte architetturali sono negli [ADR](README.md#indice-adr). Sintesi:
|
||||
- L'avvio di `claude` è in un solo punto ([Core/Claude.cs](../Core/Claude.cs)), usato da CLI e GUI.
|
||||
|
||||
## Fuori scope (MVP)
|
||||
Streaming token-per-token nella GUI, annullamento di una risposta in corso, rinomina/ricerca delle chat, config file, altri motori oltre a Claude Code (Codex, Antigravity: vedi [ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md)), logging su file, test automatici, supporto macOS.
|
||||
Streaming token-per-token nella GUI, annullamento di una risposta in corso, rinomina/ricerca delle chat, config file, altri motori oltre a Claude Code (Codex, Antigravity: vedi [ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md)), logging su file, supporto macOS. I test automatici sono pianificati: [TESTING.md](TESTING.md).
|
||||
|
||||
## Criteri di accettazione
|
||||
Ogni criterio ha test automatici nella matrice di [TESTING.md](TESTING.md) (CL-04, CL-08, CL-06, CL-01).
|
||||
|
||||
- [ ] Eseguito da un'altra cartella, `claude` parte nella cartella dell'exe (verificabile chiedendo "pwd").
|
||||
- [ ] L'output appare in streaming, non solo alla fine.
|
||||
- [ ] L'exit code è propagato.
|
||||
|
||||
+4
-1
@@ -6,6 +6,7 @@ Indice e regole. Ogni informazione vive in **un solo documento**; gli altri la r
|
||||
|---|---|
|
||||
| [../README.md](../README.md) | Uso, requisiti, build e publish |
|
||||
| [MVP.md](MVP.md) | Specifica funzionale, design UI/UX, criteri di accettazione |
|
||||
| [TESTING.md](TESTING.md) | Piano dei test: regola "nuova funzione = nuovi test", soglie di copertura, matrice requisito → test |
|
||||
| [ROADMAP.md](ROADMAP.md) | Fasi e stato (spunte); solo passi, mai motivazioni |
|
||||
| `adr/NNNN-*.md` | Decisioni architetturali e loro motivazioni (indice sotto) |
|
||||
| [../CLAUDE.md](../CLAUDE.md) | Guida per Claude Code: comandi, architettura del codice, vincoli |
|
||||
@@ -20,7 +21,8 @@ Se il nome cambia di nuovo, aggiornare solo questi punti:
|
||||
- [ ] Titolo di [../README.md](../README.md) e riga iniziale di [../CLAUDE.md](../CLAUDE.md) (nome del csproj nei comandi)
|
||||
|
||||
## Flusso (doc-first, doc-as-code)
|
||||
1. Prima i doc, poi il codice: ADR (se c'è una scelta architetturale) → MVP → ROADMAP → README → codice → CLAUDE.md.
|
||||
1. Prima i doc, poi i test, poi il codice: ADR (se c'è una scelta architetturale) → MVP → matrice in [TESTING.md](TESTING.md) → test → codice → ROADMAP → README → CLAUDE.md.
|
||||
Nessuna funzione è finita senza test che la coprono, nello stesso commit.
|
||||
2. I doc stanno nel repo e cambiano con lo stesso commit del codice a cui si riferiscono.
|
||||
3. Una modifica non deve lasciare i doc disallineati.
|
||||
|
||||
@@ -35,3 +37,4 @@ Se il nome cambia di nuovo, aggiornare solo questi punti:
|
||||
|---|---|---|
|
||||
| [0001](adr/0001-gui-multipiattaforma-avalonia.md) | GUI multipiattaforma in solo C# con Avalonia | Accettata |
|
||||
| [0002](adr/0002-motori-cli-claude-codex-antigravity.md) | Più motori CLI (Claude Code, Codex, Antigravity) su Windows e Linux | Proposta |
|
||||
| [0003](adr/0003-strategia-di-test.md) | Strategia di test e regola "nuova funzione = nuovi test" | Proposta |
|
||||
+15
-1
@@ -23,7 +23,7 @@ Dettagli dell'MVP in [MVP.md](MVP.md).
|
||||
- [x] GUI: cronologia chat (salvataggio JSON, barra laterale, riapertura con `--resume`, eliminazione)
|
||||
- [ ] GUI: streaming della risposta (`--output-format stream-json`), stop, rinomina/ricerca chat
|
||||
- [ ] Config file accanto all'exe
|
||||
- [ ] Test automatici e CI
|
||||
- [ ] Test automatici e CI (vedi Fase 6)
|
||||
|
||||
## Fase 4 — Più motori (Claude Code, Codex, Antigravity)
|
||||
Decisione, tabella dei comandi e rischi: [ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md). Qui solo i passi. Va fatta dopo (o insieme a) la Fase 5, che estrae il punto unico di avvio processo.
|
||||
@@ -53,3 +53,17 @@ Decisione e motivazioni: [ADR-0001](adr/0001-gui-multipiattaforma-avalonia.md).
|
||||
- [x] Eliminare `ui.html` e il protocollo `WebMessage`
|
||||
- [ ] Publish single-file self-contained `win-x64` e `linux-x64`: compilano e partono (Linux: CLI e GUI su WSLg; Windows: solo CLI, via WSL); da provare la GUI su Windows nativo e su una distribuzione Linux pulita
|
||||
- [x] Aggiornare README.md e CLAUDE.md (comandi, architettura, vincoli) a migrazione conclusa
|
||||
|
||||
## Fase 6 — Test e copertura completa
|
||||
Decisione: [ADR-0003](adr/0003-strategia-di-test.md). Piano, soglie e matrice: [TESTING.md](TESTING.md). Da questa fase in poi vale la regola: ogni nuova funzione arriva con i suoi test.
|
||||
|
||||
- [ ] Verificare le compatibilità dell'ADR-0003 (xUnit, Avalonia.Headless, coverlet, `dotnet test`) e portarlo ad "Accettata"
|
||||
- [ ] Ristrutturare in `src/` e `tests/` con soluzione unica e `InternalsVisibleTo`
|
||||
- [ ] Predisposizioni di testabilità: `ChatStore` a istanza, `IClaudeRunner`
|
||||
- [ ] `FakeClaude`: eseguibile finto per i test di processo
|
||||
- [ ] Test `Core/` (CL, ST) e `MainViewModel` (VM)
|
||||
- [ ] Test del renderer markdown (MD) e della GUI headless (UI)
|
||||
- [ ] Test end-to-end e smoke di publish per `win-x64` e `linux-x64` (PUB)
|
||||
- [ ] Soglie di copertura che fanno fallire la build
|
||||
- [ ] CI con matrice Windows + Linux (fornitore da scegliere)
|
||||
- [ ] Batteria di contratto per ogni motore (EN), insieme alla Fase 4
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
# Piano dei test
|
||||
|
||||
Decisioni (strumenti, livelli, struttura): [ADR-0003](adr/0003-strategia-di-test.md). Qui: regola, flusso, soglie e **matrice requisito → test**, sorgente di verità per la copertura. Le spunte `[ ]` passano a `[x]` quando il test esiste e passa.
|
||||
|
||||
## Regola
|
||||
**Nuova funzione, correzione o cambio di comportamento ⇒ test nello stesso commit.** Niente test, funzione non finita.
|
||||
- Una nuova funzione aggiunge righe alla matrice qui sotto (con ID) prima di scrivere il codice.
|
||||
- Una correzione di bug parte da un test che fallisce e lo lascia come regressione.
|
||||
- Una modifica che cambia un requisito aggiorna prima il doc, poi la riga della matrice, poi i test, poi il codice.
|
||||
- Una riga senza test non è copertura: va lasciata `[ ]` e conta come debito visibile.
|
||||
|
||||
## Flusso (estende il doc-first di [README.md](README.md))
|
||||
ADR (se serve) → MVP.md (requisito e criterio) → riga in questa matrice → test che falliscono → codice → test verdi → ROADMAP/README/CLAUDE.md.
|
||||
|
||||
## Livelli
|
||||
| Livello | Cosa prova | Come |
|
||||
|---|---|---|
|
||||
| **U** — unitario | `Core/` e `MainViewModel` | xUnit, finto runner, cartella dati temporanea |
|
||||
| **R** — renderer | `MarkdownView` | xUnit + Headless, asserzioni sull'albero visuale |
|
||||
| **G** — GUI headless | `MainWindow`, template, tastiera, finestre di dialogo | xUnit + Headless, senza schermo |
|
||||
| **P** — processo | avvio reale di un processo (`Claude`) | `FakeClaude` nel PATH |
|
||||
| **E** — end-to-end | l'eseguibile pubblicato (CLI, 127, exit code) | publish + processo figlio + `FakeClaude` |
|
||||
|
||||
## Struttura prevista
|
||||
```
|
||||
agent-wiki-ui.slnx
|
||||
src/agent-wiki-ui/ app (Core/, Gui/, Program.cs)
|
||||
tests/agent-wiki-ui.Tests/ U, R, G, P, E (Trait "Category": Unit | Gui | Process | E2E)
|
||||
tests/FakeClaude/ eseguibile finto: registra argv/cwd/stdin; testo, stderr, exit code e ritardo da variabili d'ambiente
|
||||
```
|
||||
|
||||
## Predisposizioni di testabilità (prerequisito dei test)
|
||||
- [ ] `ChatStore` come istanza con cartella iniettata (oggi statico su `LocalApplicationData`)
|
||||
- [ ] `IClaudeRunner` dietro `Core/Claude.cs`, iniettato nel `MainViewModel`
|
||||
- [ ] `InternalsVisibleTo` verso il progetto di test
|
||||
- [ ] Spostamento in `src/` e `tests/` con soluzione unica
|
||||
|
||||
## Soglie di copertura (la build fallisce sotto soglia)
|
||||
| Area | Righe | Rami |
|
||||
|---|---|---|
|
||||
| `Core/` | ≥ 95% | ≥ 90% |
|
||||
| `Gui/MainViewModel` | ≥ 95% | ≥ 90% |
|
||||
| `Gui/MarkdownView` | ≥ 90% | ≥ 85% |
|
||||
| `Gui/MainWindow*`, `ConfirmDialog` (code-behind) | ≥ 85% | — |
|
||||
| Totale | ≥ 90% | — |
|
||||
|
||||
Esclusi dal calcolo: XAML generato, `Program.cs` (tre righe, coperte da E2E), `App.axaml.cs`. La soglia non sostituisce la matrice: copertura di righe alta con requisiti scoperti non basta.
|
||||
|
||||
## Matrice requisito → test
|
||||
Fonte dei requisiti: [MVP.md](MVP.md) e gli ADR. Livello: vedi sopra. Il nome del test è indicativo.
|
||||
|
||||
### Avvio di `claude` — `Core/Claude.cs`
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| CL-01 | Il prompt è un solo argomento, anche con virgolette e spazi (criterio MVP 4) | U/P | `Prompt_con_virgolette_arriva_intatto` | [ ] |
|
||||
| CL-02 | Primo messaggio: `--session-id <guid>`; successivi: `--resume <guid>` | U | `Sessione_prima_e_ripresa` | [ ] |
|
||||
| CL-03 | `--model`/`--effort` omessi se vuoti, presenti se valorizzati | U | `Flag_opzionali` | [ ] |
|
||||
| CL-04 | Cartella di lavoro = cartella dell'exe, non la cwd del chiamante (criterio MVP 1) | U/P | `Cartella_di_lavoro_e_quella_dell_exe` | [ ] |
|
||||
| CL-05 | Stdin chiuso subito: nessuna attesa di 3 s | P | `Stdin_chiuso_non_blocca` | [ ] |
|
||||
| CL-06 | CLI: l'exit code di `claude` è propagato (criterio MVP 3) | P/E | `ExitCode_propagato` | [ ] |
|
||||
| CL-07 | CLI: `claude` assente ⇒ messaggio chiaro ed exit code 127 | P/E | `Claude_assente_127` | [ ] |
|
||||
| CL-08 | CLI: stdout/stderr ereditati e ritrasmessi (criterio MVP 2: streaming) | E | `Output_ereditato_in_tempo_reale` | [ ] |
|
||||
| CL-09 | GUI: output catturato e ripulito agli estremi; stderr separato | P | `Output_catturato` | [ ] |
|
||||
| CL-10 | GUI: `claude` assente ⇒ eccezione riconoscibile, non crash | P | `Claude_assente_in_GUI` | [ ] |
|
||||
| CL-11 | Il catalogo modelli contiene gli ID attesi e gli effort previsti | U | `Catalogo_modelli_ed_effort` | [ ] |
|
||||
| CL-12 | Senza argomenti non parte la CLI; con argomenti sì (tutti uniti in un prompt) | E | `Argomenti_uniti_in_un_prompt` | [ ] |
|
||||
|
||||
### Cronologia — `Core/ChatStore.cs`, `Core/AppInfo.cs`
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| ST-01 | Salvataggio e rilettura: id, titolo, data, `started`, messaggi con ruolo e testo | U | `Salva_e_rileggi` | [ ] |
|
||||
| ST-02 | Una chat senza messaggi non viene salvata | U | `Chat_vuota_non_salvata` | [ ] |
|
||||
| ST-03 | Titolo = inizio del primo messaggio utente: 50 caratteri + "…", a capo sostituiti da spazi | U | `Titolo_troncato` | [ ] |
|
||||
| ST-04 | L'id deve essere un Guid: un id con `..` o separatori non tocca il disco fuori dalla cartella | U | `Id_non_guid_rifiutato` | [ ] |
|
||||
| ST-05 | L'elenco è ordinato dalla più recente | U | `Elenco_ordinato` | [ ] |
|
||||
| ST-06 | File JSON corrotto ignorato senza eccezioni | U | `File_corrotto_ignorato` | [ ] |
|
||||
| ST-07 | Eliminazione: rimuove solo il file di quella chat; id inesistente senza errori | U | `Elimina_chat` | [ ] |
|
||||
| ST-08 | Compatibilità del formato: un JSON scritto dalla versione WinForms si legge | U | `Formato_storico_compatibile` | [ ] |
|
||||
| ST-09 | Migrazione: la cartella di un nome precedente (`LlmRun`) si sposta nella nuova al primo avvio; se la nuova esiste non si tocca nulla | U | `Migrazione_da_nome_precedente` | [ ] |
|
||||
| ST-10 | `AppInfo.Name` = `AssemblyName` del csproj | U | `Nome_da_assembly` | [ ] |
|
||||
| ST-11 | Errore di I/O nel salvataggio non perde la chat a schermo | U | vedi VM-12 | [ ] |
|
||||
|
||||
### Logica della chat — `Gui/MainViewModel.cs`
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| VM-01 | Senza chat salvate: schermata iniziale (`ShowEmpty`), elenco vuoto | U | `Avvio_senza_chat` | [ ] |
|
||||
| VM-02 | All'avvio si riapre la chat più recente | U | `Avvio_riapre_ultima` | [ ] |
|
||||
| VM-03 | `CanSend` falso se il testo è vuoto o solo spazi, o se occupato | U | `CanSend` | [ ] |
|
||||
| VM-04 | Invio: il messaggio utente compare e viene **salvato prima** della risposta | U | `Messaggio_salvato_prima_della_risposta` | [ ] |
|
||||
| VM-05 | Risposta riuscita: aggiunta come "ai", `started` diventa vero, il secondo invio usa `--resume` | U | `Risposta_e_ripresa` | [ ] |
|
||||
| VM-06 | Risposta fallita (exit ≠ 0): messaggio "err" con codice e stderr; `started` resta falso e il tentativo successivo usa ancora `--session-id` | U | `Errore_non_avvia_la_sessione` | [ ] |
|
||||
| VM-07 | `claude` assente: messaggio "'claude' non trovato nel PATH." | U | `Claude_assente_messaggio` | [ ] |
|
||||
| VM-08 | Durante l'attesa: Invia e Nuova chat disabilitati; aprire/eliminare è ignorato | U | `Occupato_blocca_azioni` | [ ] |
|
||||
| VM-09 | Modello: "Predefinito" ⇒ nessun flag; modello scelto ⇒ ID; "Altro ID…" ⇒ testo libero ripulito | U | `Scelta_modello` | [ ] |
|
||||
| VM-10 | Effort: "Predefinito" ⇒ nessun flag; altrimenti il valore | U | `Scelta_effort` | [ ] |
|
||||
| VM-11 | Nuova chat: svuota la vista, nuovo guid, `started` falso, elenco aggiornato | U | `Nuova_chat` | [ ] |
|
||||
| VM-12 | Errore di I/O nel salvataggio non interrompe l'invio né cancella i messaggi | U | `Salvataggio_fallito_non_perde_chat` | [ ] |
|
||||
| VM-13 | Apertura chat: carica messaggi, `started` e id; chat inesistente ⇒ elenco ricaricato | U | `Apri_chat` | [ ] |
|
||||
| VM-14 | Eliminazione: con conferma rimuove; senza conferma non fa nulla; se è la chat aperta parte una nuova | U | `Elimina_con_e_senza_conferma` | [ ] |
|
||||
| VM-15 | Suggerimento: precompila il campo e chiede il focus | U | `Suggerimento_precompila` | [ ] |
|
||||
| VM-16 | Barra laterale: il comando commuta `SidebarOpen` | U | `Barra_laterale` | [ ] |
|
||||
| VM-17 | Copia: il comando passa il testo alla funzione degli appunti | U | `Copia_negli_appunti` | [ ] |
|
||||
| VM-18 | Notifiche di proprietà: `HasMessages`, `ShowEmpty`, `CanSend`, `IsCustomModel` cambiano quando devono | U | `Notifiche_proprieta` | [ ] |
|
||||
| VM-19 | Il titolo e la data dell'elenco riflettono l'ultimo scambio; la chat aperta è marcata attiva | U | `Elenco_chat_attiva` | [ ] |
|
||||
|
||||
### Renderer markdown — `Gui/MarkdownView.cs`
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| MD-01 | Sorgente vuota o nulla ⇒ nessun contenuto, senza errori | R | `Sorgente_vuota` | [ ] |
|
||||
| MD-02 | Titoli h1–h6 con dimensioni decrescenti e peso semi-grassetto | R | `Titoli` | [ ] |
|
||||
| MD-03 | Paragrafi; a capo morbido = spazio, a capo forzato = riga nuova | R | `A_capo` | [ ] |
|
||||
| MD-04 | Grassetto, corsivo, barrato, anche annidati | R | `Enfasi` | [ ] |
|
||||
| MD-05 | Codice inline in carattere monospazio con sfondo | R | `Codice_inline` | [ ] |
|
||||
| MD-06 | Blocco di codice: monospazio, scorrimento orizzontale, pulsante Copia che copia solo il codice | R/G | `Blocco_codice_e_copia` | [ ] |
|
||||
| MD-07 | Liste puntate e numerate (rispetta il numero iniziale), annidate | R | `Liste` | [ ] |
|
||||
| MD-08 | Citazioni con barra laterale, anche annidate | R | `Citazioni` | [ ] |
|
||||
| MD-09 | Tabelle: intestazione in evidenza, allineamenti sinistra/centro/destra, righe irregolari | R | `Tabelle` | [ ] |
|
||||
| MD-10 | Linea orizzontale | R | `Linea_orizzontale` | [ ] |
|
||||
| MD-11 | Link: colore d'accento e sottolineatura; il clic apre solo `http(s)` | R | `Link_http_https` | [ ] |
|
||||
| MD-12 | Link: nessuna apertura se c'è una selezione in corso; cursore a mano sopra il link | R | `Link_con_selezione` | [ ] |
|
||||
| MD-13 | Posizioni dei link corrette con enfasi, codice inline e a capo prima del link | R | `Intervalli_link` | [ ] |
|
||||
| MD-14 | Immagini: si mostra il testo alternativo, non si scarica nulla | R | `Immagini_come_testo` | [ ] |
|
||||
| MD-15 | HTML grezzo disabilitato: i tag compaiono come testo, non come elementi | R | `Html_disabilitato` | [ ] |
|
||||
| MD-16 | Cambio di tema (chiaro/scuro): i colori si aggiornano ricostruendo la vista | R | `Cambio_tema` | [ ] |
|
||||
| MD-17 | Markdown malformato o blocchi sconosciuti non fanno crashare | R | `Markdown_malformato` | [ ] |
|
||||
| MD-18 | Testo selezionabile | R | `Testo_selezionabile` | [ ] |
|
||||
|
||||
### Finestra e interazione — `Gui/MainWindow*`, `Gui/App.axaml`, `Gui/ConfirmDialog.cs`
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| UI-01 | La finestra si apre e mostra la schermata iniziale con quattro suggerimenti | G | `Apertura_schermata_iniziale` | [ ] |
|
||||
| UI-02 | Proporzioni 16:10, 75% dell'area utile (max 1440), minimo 900x600; senza schermo primario 1280x800 | G | `Dimensioni_finestra` | [ ] |
|
||||
| UI-03 | `Invio` invia, `Maiusc+Invio` va a capo, nessun invio a campo vuoto | G | `Tasti_invio` | [ ] |
|
||||
| UI-04 | Il pulsante Invia è disabilitato con campo vuoto o in attesa | G | `Pulsante_invia` | [ ] |
|
||||
| UI-05 | I messaggi hanno aspetto per ruolo: utente, Claude (con Copia), errore | G | `Template_per_ruolo` | [ ] |
|
||||
| UI-06 | La conversazione scorre in fondo a ogni nuovo messaggio | G | `Scorrimento_in_fondo` | [ ] |
|
||||
| UI-07 | Indicatore di attesa visibile solo durante l'elaborazione | G | `Indicatore_attesa` | [ ] |
|
||||
| UI-08 | Elenco chat: voce attiva evidenziata, clic apre, ✕ elimina; "Nessuna chat salvata" se vuoto | G | `Elenco_chat` | [ ] |
|
||||
| UI-09 | Il campo "Altro ID…" compare solo con quella voce del modello | G | `Campo_modello_personalizzato` | [ ] |
|
||||
| UI-10 | Il pulsante a tre linee mostra/nasconde la barra laterale | G | `Mostra_nascondi_barra` | [ ] |
|
||||
| UI-11 | Dialogo di conferma: restituisce vero con Elimina, falso con Annulla o chiusura | G | `Dialogo_conferma` | [ ] |
|
||||
| UI-12 | Accessibilità: ogni pulsante a sola icona e il campo messaggio hanno `AutomationProperties.Name` | G | `Nomi_accessibili` | [ ] |
|
||||
| UI-13 | Tema: sia il chiaro sia lo scuro risolvono tutti i token usati (nessuna risorsa mancante) | G | `Token_tema_completi` | [ ] |
|
||||
| UI-14 | Focus sul campo messaggio all'apertura, dopo la risposta e dopo "Nuova chat" | G | `Focus_campo` | [ ] |
|
||||
| UI-15 | Il nome dell'app compare nel titolo della finestra e nella barra laterale | G | `Nome_app_visibile` | [ ] |
|
||||
|
||||
### Eseguibile pubblicato
|
||||
| ID | Requisito | Liv. | Test | |
|
||||
|---|---|---|---|---|
|
||||
| PUB-01 | Il publish single-file `linux-x64` si compila e la CLI risponde con `FakeClaude` | E | `Publish_linux_smoke` | [ ] |
|
||||
| PUB-02 | Il publish single-file `win-x64` si compila e la CLI risponde con `FakeClaude` | E | `Publish_windows_smoke` | [ ] |
|
||||
| PUB-03 | Le librerie native (Skia/HarfBuzz) vengono estratte e la GUI si avvia (provata con timeout) | E | `Gui_si_avvia` | [ ] |
|
||||
|
||||
### Più motori (ADR-0002) — da compilare quando si implementa la Fase 4
|
||||
Per **ogni** motore, la stessa batteria di contratto:
|
||||
| ID | Requisito | Liv. | |
|
||||
|---|---|---|---|
|
||||
| EN-01 | Argomenti del primo messaggio e della ripresa | U | [ ] |
|
||||
| EN-02 | Rilevamento nel PATH; assente ⇒ disabilitato e messaggio chiaro | U/P | [ ] |
|
||||
| EN-03 | Lettura dell'id di sessione dall'output (Codex) | U | [ ] |
|
||||
| EN-04 | Parser dell'output → markdown, con output di esempio reali salvati nei test | U | [ ] |
|
||||
| EN-05 | Modelli ed effort mostrati secondo il motore | U/G | [ ] |
|
||||
| EN-06 | Il JSON della chat memorizza il motore; cambiare motore apre una nuova chat | U | [ ] |
|
||||
| EN-07 | Politica dei permessi: nessun flag che salta i controlli senza scelta esplicita | U | [ ] |
|
||||
| EN-08 | Impostazioni persistite e rilette | U | [ ] |
|
||||
| EN-09 | Gestione del blocco di `agy -p` con output rediretto (timeout, errore chiaro) | P | [ ] |
|
||||
|
||||
## Come si eseguono (a implementazione avvenuta)
|
||||
Comandi previsti, da confermare con ADR-0003 → "Da verificare":
|
||||
- Tutti i test: `dotnet test`
|
||||
- Con copertura e soglie: `dotnet test --collect:"XPlat Code Coverage"` e controllo delle soglie della tabella sopra
|
||||
- Solo una categoria: `dotnet test --filter "Category=Unit"` (o `Gui`, `Process`, `E2E`)
|
||||
Su Linux i test GUI girano senza display (Headless).
|
||||
|
||||
## Definition of Done di una modifica
|
||||
- [ ] Doc aggiornati (MVP, ROADMAP, ADR se serve)
|
||||
- [ ] Righe nuove o modificate nella matrice, con ID
|
||||
- [ ] Test scritti e verdi su Windows e Linux
|
||||
- [ ] Soglie di copertura rispettate
|
||||
- [ ] Nessun requisito della modifica resta con la casella `[ ]` senza motivo scritto
|
||||
@@ -0,0 +1,52 @@
|
||||
# ADR-0003 — Strategia di test e regola "nuova funzione = nuovi test"
|
||||
|
||||
- **Stato**: Proposta
|
||||
- **Data**: 2026-10-15
|
||||
- **Collegato a**: [ADR-0001](0001-gui-multipiattaforma-avalonia.md), [ADR-0002](0002-motori-cli-claude-codex-antigravity.md)
|
||||
|
||||
## Contesto
|
||||
Oggi non esistono test. L'app gira su due sistemi operativi, ha una CLI, una GUI e una dipendenza esterna (`claude`) che non si può chiamare nei test: costa, è lenta e non è deterministica. Con l'arrivo di altri motori (ADR-0002) la superficie da proteggere cresce. Serve una strategia che garantisca **copertura completa** e che renda i test parte del lavoro di ogni nuova funzione.
|
||||
|
||||
## Criteri di decisione
|
||||
1. Nessun test dipende da `claude`, `codex`, `agy`, dalla rete o da un account.
|
||||
2. Gli stessi test girano su Windows e Linux, senza schermo (CI senza display).
|
||||
3. Copertura misurata e con soglia che fa fallire la build, non solo un numero da guardare.
|
||||
4. Ogni requisito scritto nei doc è tracciabile a uno o più test.
|
||||
5. Costo basso per aggiungere un test: il codice deve essere progettato per essere testabile.
|
||||
|
||||
## Decisione
|
||||
- **Regola**: una funzione, correzione o modifica di comportamento non è finita finché non ha test che la coprono, **nello stesso commit**. Una correzione di bug parte da un test che fallisce. La regola e il flusso sono in [TESTING.md](../TESTING.md).
|
||||
- **Piano dei test come documento**: [TESTING.md](../TESTING.md) è l'unica sorgente della matrice requisito → test e della politica di copertura. Gli altri doc la richiamano con un link.
|
||||
- **Strumenti**: xUnit come framework; `Avalonia.Headless` (con l'integrazione xUnit) per i test della GUI senza schermo; `coverlet` per la copertura. Le versioni compatibili con Avalonia 12 e .NET 10 vanno verificate (vedi sotto).
|
||||
- **Doppio di `claude`**: un piccolo eseguibile di test, `FakeClaude`, scritto in C# e compilato insieme ai test. Registra argomenti, cartella di lavoro e stdin, e risponde in base a variabili d'ambiente (testo, stderr, exit code, ritardo). Essendo un eseguibile nativo funziona uguale su Windows e Linux (uno script `.sh` o `.cmd` non sarebbe avviabile direttamente come `claude`).
|
||||
- **Livelli**: unitari (Core, ViewModel, renderer markdown), componenti GUI headless (finestra, template, tastiera), end-to-end sull'eseguibile pubblicato (CLI e codice 127) e smoke di publish per `win-x64` e `linux-x64`.
|
||||
- **Predisposizioni di testabilità** nel codice (da fare insieme ai primi test):
|
||||
- `ChatStore` diventa un'istanza con cartella iniettata, non uno statico legato a `LocalApplicationData`.
|
||||
- L'esecuzione di `claude` sta dietro un'interfaccia (`IClaudeRunner`) iniettata nel ViewModel; l'implementazione reale resta in `Core/Claude.cs`. I test del ViewModel usano un finto runner; quelli di `Claude` usano `FakeClaude`.
|
||||
- La conferma di eliminazione, gli appunti e il focus sono già delegati (`ConfirmDelete`, `CopyText`, `FocusInput`) e restano sostituibili.
|
||||
- Gli ADR-0002 (motori) richiedono che ogni motore abbia la propria batteria di test sugli argomenti e sul parser dell'output.
|
||||
- **Struttura del repo**: codice dell'app in `src/agent-wiki-ui/`, test in `tests/`, soluzione unica alla radice. Così il globbing del csproj dell'app non include i sorgenti dei test. Il progetto dell'app espone gli `internal` ai test con `InternalsVisibleTo`.
|
||||
- **Porte di qualità**: copertura minima per area (in TESTING.md); `dotnet test` con copertura deve passare su Windows e Linux. Il fornitore di CI non è ancora scelto: i comandi sono indipendenti dal fornitore e vivono nel repo.
|
||||
|
||||
## Opzioni considerate
|
||||
| Opzione | Esito | Motivo |
|
||||
|---|---|---|
|
||||
| Test manuali con checklist | Scartata | Non scala, non gira in CI, non protegge dalle regressioni. |
|
||||
| Test solo unitari sulla logica | Scartata | La GUI e la CLI reale (exit code, 127, cartella di lavoro, stdin) resterebbero scoperte. |
|
||||
| Test GUI con screenshot di riferimento | Rinviata | Font e rendering cambiano tra Windows e Linux: i confronti pixel sono instabili. Si usano asserzioni sull'albero visuale; gli screenshot restano come artefatti da guardare. |
|
||||
| `claude` finto come script | Scartata | Su Windows `claude` si avvia direttamente (senza `cmd /c`): serve un eseguibile. |
|
||||
| **xUnit + Headless + FakeClaude + coverlet** | **Scelta** | Copre tutti i livelli, senza dipendenze esterne, su entrambi i sistemi. |
|
||||
|
||||
## Conseguenze
|
||||
**Positive**: regressioni intercettate su entrambi i sistemi; requisiti tracciabili; i motori aggiuntivi si aggiungono con test di contratto uniformi.
|
||||
**Negative / costi**: un refactoring iniziale per iniettare cartella dati e runner; la struttura `src/` + `tests/` sposta file; la soglia di copertura obbliga a mantenere i test anche per codice di interfaccia; i test headless sono più lenti degli unitari.
|
||||
|
||||
## Da verificare
|
||||
- [ ] Versioni di xUnit, `Avalonia.Headless` (integrazione xUnit) e `coverlet` compatibili con Avalonia 12 e .NET 10, e `dotnet test` funzionante con la soluzione.
|
||||
- [ ] `Avalonia.Headless` con rendering Skia disponibile su Windows e Linux senza display (in questa macchina il rendering headless è già stato provato fuori dal repo).
|
||||
- [ ] Nessun test di GUI instabile per i tempi (usare attese su condizioni, non pause fisse).
|
||||
- [ ] Scelta del fornitore di CI e traduzione dei comandi in una pipeline con matrice Windows + Linux.
|
||||
|
||||
## Riferimenti
|
||||
- Piano, matrice e regole: [TESTING.md](../TESTING.md).
|
||||
- Passi: [ROADMAP.md](../ROADMAP.md), Fase 6.
|
||||
Reference in new issue
Block a user