diff --git a/CLAUDE.md b/CLAUDE.md index 7fb2d05..3cb8245 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,7 @@ Context for AI agents (Claude Code) working in this repo. Read this first, then - **Local-first**: everything works offline, no account, no central server. Online only enriches (degrades gracefully). - **Progressive disclosure**: only `Variety.label` is mandatory; every other field optional. Audience is everyone 10–80; simple by default, depth on demand. Never build "two modes". - **License: AGPL-3.0.** Keep new deps compatible. +- **Tests, near-TDD.** Every behavior is covered by automated tests (unit / widget / integration); **do NOT rely on manual testing.** Write tests first for domain logic (`commons_core` is pure Dart — ideal for TDD). Nothing merges without tests covering the new behavior; CI gates it. See [`docs/design/testing.md`](docs/design/testing.md). - Discussion/design docs may be in Spanish; code and specs in English. ## Stack & structure diff --git a/docs/design/first-sprint.md b/docs/design/first-sprint.md index 95ff92a..134fb02 100644 --- a/docs/design/first-sprint.md +++ b/docs/design/first-sprint.md @@ -6,6 +6,8 @@ Una persona puede: abrir la app, **añadir una semilla en 20 s** (etiqueta + foto + cantidad), verla en una lista por categorías, abrir su ficha, editarla con campos avanzados plegados, y que **todo persista cifrado** entre reinicios. Multilingüe (al menos ES/EN). Corre en un dispositivo/emulador real. +**Cada historia se entrega con sus tests** (unit/widget/integration) — casi-TDD, sin pruebas manuales como red de seguridad. Ver [`testing.md`](testing.md). Sin tests, la historia no está hecha. + ## Pasos 0. **Prerequisitos (máquina):** Flutter SDK actualizado, un emulador o dispositivo. Quitar los locks del bare si hiciera falta (`rm -f ~/repos/tane.git/*.lock`). @@ -37,7 +39,8 @@ Una persona puede: abrir la app, **añadir una semilla en 20 s** (etiqueta + fot 6. **Catálogo de especies (semilla del bundle):** empezar con un CSV/JSON pequeño curado (unas pocas hortícolas ibéricas) con `scientific_name` + `wikidata_qid` + `gbif_key` + nombres comunes ES/EN, para probar el autocompletado. La "varilla" y Kew SID vienen después. -7. **Verificación:** `flutter analyze` limpio, `flutter test` (incl. test de migración), y arrancar en dispositivo. Añadir el texto canónico de la licencia: `curl -o LICENSE https://www.gnu.org/licenses/agpl-3.0.txt`. +7. **Tests y CI (transversal, no al final):** ver [`testing.md`](testing.md). Como mínimo en este sprint: unit de `commons_core` (Quantity, columnas comunes, UUIDv7/HLC), **test de migración** Drift, **test de "no hay texto en claro"** en el fichero de BD, widget tests de la lista y del alta rápida, y **un integration test** del flujo "alta → persiste cifrada → reabrir → sigue ahí". Montar CI (analyze + test + cobertura) desde el primer commit de código. +8. **Verificación final:** `flutter analyze` limpio, toda la suite en verde en emulador, cobertura publicada. Añadir el texto canónico de la licencia: `curl -o LICENSE https://www.gnu.org/licenses/agpl-3.0.txt`. ## Fuera de alcance de este sprint (no hacer) diff --git a/docs/design/testing.md b/docs/design/testing.md new file mode 100644 index 0000000..e08d1f1 --- /dev/null +++ b/docs/design/testing.md @@ -0,0 +1,40 @@ +# Tanemaki — Estrategia de pruebas (casi-TDD, sin depender de pruebas manuales) + +*Convención del proyecto. Objetivo: **cada comportamiento cubierto por una prueba automática**; las pruebas manuales son la excepción, no la red de seguridad.* + +## Principio + +**Casi-TDD.** Para lógica nueva: test primero (red → green → refactor). Como mínimo innegociable: **ninguna rama se mergea sin tests que cubran lo nuevo.** La "definición de hecho" de cada historia **incluye sus tests**; sin tests, no está hecha. CI en cada push bloquea si algo falla o baja la cobertura. + +`commons_core` es **Dart puro (sin Flutter)** → la lógica difícil (CRDT, identidad/derivación, cifrado, backup, pledge/WoT) se hace con TDD cómodo y rápido. + +## La pirámide (Flutter/Dart) + +- **Unit** (`dart test` / `flutter test`): lógica pura — CRDT, `Quantity`, derivación de claves, serialización, repositorios. Rápidos; el grueso vive en `commons_core`. +- **Widget** (`flutter_test`, `pumpWidget`): pantallas y componentes — interacción, *progressive disclosure*, i18n, accesibilidad (targets grandes). +- **Integration** (`integration_test` + `flutter test`): app entera en emulador/dispositivo, con **BD real cifrada (SQLCipher)**. Flujos e2e — **esto es lo que sustituye a las pruebas manuales.** +- **Golden** (regresión visual): pantallas clave; vigila que la accesibilidad no se rompa. +- **Migration** (Drift): `drift_dev schema generate` + `validateDatabaseSchema` por cada `schemaVersion`. + +## Tests que este proyecto necesita sí o sí + +- **Cifrado en reposo:** abrir el fichero de BD **en crudo** y afirmar que **NO hay texto en claro** (buscar una etiqueta conocida en los bytes y exigir ausencia). Regresión de seguridad clave para el "peor caso legal". +- **CRDT — convergencia:** *property-based* — fusionar operaciones en **cualquier orden** converge al mismo estado (LWW, OR-Set, log append-only, tombstones). +- **Migraciones:** cada versión migra desde la anterior y valida contra el esquema fresco. +- **Backup round-trip:** exportar → borrar → importar → **estado idéntico** (y cifrado). +- **Identidad:** derivación secp256k1 **determinista** desde la semilla (mismo input → misma clave) y unidireccional. +- **i18n:** ninguna cadena hardcodeada (lint), y todas las locales con las mismas claves. + +## Lo difícil de automatizar (y cómo) + +Cámara, keystore/biométrico, relays reales: **abstraer tras interfaces** y probar con *fakes/mocks*; dejar una capa fina de adaptadores de plataforma con un *smoke test* mínimo. Los *integration tests* en dispositivo cubren la integración real de la BD cifrada. Objetivo: **manual ≈ 0**. + +## CI y cobertura + +- CI en cada push/PR (Forgejo/Codeberg Actions o GitHub Actions): `flutter analyze` + unit + widget + **integration en emulador** + cobertura (lcov). **Merge bloqueado** si falla o baja la cobertura. +- Objetivo de cobertura **alto en `commons_core`** (p.ej. ≥ 85%); la UI, con widget + golden. +- Tests deterministas y rápidos: sin red real, reloj/UUID/HLC inyectables (para reproducibilidad). + +## Flujo de trabajo + +Dominio (`commons_core`): test primero. UI: al menos un widget test por pantalla/flujo antes de darla por terminada, y un *integration test* por flujo crítico (alta de semilla → persiste cifrada → reabrir → sigue ahí). Regla dura repetida: **sin tests, no se mergea.**