4.0 Tests primero
Vitest: search.test.ts (10 — S-01..S-04), rls-audit.test.ts (42 —
3 intruders × 14 cross-collective ops proving zero leakage)
pgTAP: 007_search_tsvector.sql (9 — columns, GIN indexes, function
signature, generated-vector invariant)
Playwright: search.test.ts (2 — SR-01 happy-path, SR-02 filter toggle)
4.1 Búsqueda global
Migration 010_search_vectors.sql: STORED GENERATED tsvector columns
on shopping_items/tasks/notes + GIN indexes + search_result_type
enum + search_in_collective() SECURITY INVOKER function (RLS-aware,
trash-excluded per RN-08) + GRANT EXECUTE to anon/authenticated.
Uses `simple` config (no stemmer — bilingual en/es).
Store apps/web/src/lib/stores/search.ts — RPC wrapper
/search: debounced input (300ms, ≥2 chars), type filter chips with
multi-toggle, results grouped per type with data-testid hooks for
Playwright, deep-link per type (shopping_item → /lists/[id], task →
/tasks/[id], note → /notes/[id])
4.4 Security
rls-audit.test.ts replaces the curl-based manual audit with a
parametrised matrix — zero cross-collective reads/writes under
three different attacker roles
Realtime flake hardening
Add 250ms settle after SUBSCRIBED in realtime-helpers.ts — the Phoenix
socket reports SUBSCRIBED slightly before the backend finishes
propagating the filter to the WAL subscription, so mutations fired
immediately after subscribe could miss the filter under load.
4.Z Verification
just test-all → 211 verdes, 2 skipped
34 pgTAP (was 25, +9 search)
140 Vitest integration (was 88, +10 search +42 rls-audit; 2 skipped)
6 Vitest unit (unchanged)
31 Playwright (was 29, +2 search)
Deferred (prod-deploy scope):
PWA install/push (needs real iOS/Android hardware)
Kong rate-limit plugin config
JWT_SECRET / ANON_KEY rotation
Lighthouse PWA ≥ 90 manual validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.6 KiB
Fase 4 — Búsqueda y Pulido Final
Estado: 🟡 Búsqueda + RLS audit completos. PWA validation / push / Kong rate-limit / JWT rotation diferidos (requieren hardware real o config prod).
Duración estimada: 1 semana
Objetivo: completar el MVP con calidad de producción.
Esta fase sigue TDD: primero los tests, al final la verificación. Ninguna tarea de implementación se da por hecha hasta que todos los tests pasan (just test-all).
4.0 Tests primero — escribir antes de implementar
Vitest (packages/test-utils/tests/):
search.test.ts— 10 tests: S-01 ítems / tareas / notas por texto; S-02 aislamiento cross-collective (Eva 0 filas, collective_id desconocido 0 filas); S-03 papelera excluida (RN-08); S-04 filtros por tipo, creador y rango de fechas ✅rls-audit.test.ts— 42 tests: 3 intrusos (Ana/Borja/David) × 14 operaciones (SELECT/INSERT/UPDATE/DELETE sobre collectives, shopping_lists, shopping_items, task_lists, tasks, notes) → 0 filas ni mutaciones a través de colectivos ✅
pgTAP (supabase/tests/):
007_search_tsvector.sql— 9 tests: columnassearchen shopping_items/tasks/notes, índices GIN creados, funciónsearch_in_collectiveexiste, invariante funcional (vector generado refleja el texto insertado y matchea sutsquery) ✅- [~]
008_rls_audit.sql— sustituido porrls-audit.test.ts(Vitest) para evitar duplicar la matriz en SQL; la auditoría cross-collective se hace en TypeScript donde es trivial parametrizar por usuario+operación
Playwright (apps/web/tests/e2e/):
search.test.ts— SR-01 (buscar "milk" → grupo shopping_item con resultado), SR-02 (desactivar filtro "Listas" → grupo shopping_item desaparece) ✅profile.test.ts— diferido:/profileno implementado en este sprint (ver 4.2)pwa.test.ts— diferido a validación manual conlighthouse-cirate-limit.test.ts— diferido a deploy de producción (Kong rate-limit plugin)
Manual / herramientas externas:
- Lighthouse PWA score ≥ 90 en mobile — pendiente (requiere build de producción)
- Instalable en iOS + Android — pendiente (requiere dispositivos reales)
- Notificaciones push iOS 16.4+ — diferido
- Offline completo — cubierto por
O-01/O-02en Playwright desde Fase 2b
4.1 Búsqueda global
- Migración
010_search_vectors.sql:- Columnas STORED GENERATED
search tsvectorenshopping_items,tasks,notes(configsimple— bilingüe en/es sin estemizar) - Índices GIN en cada una (
shopping_items_search_idx,tasks_search_idx,notes_search_idx) - Enum
search_result_type+ funciónsearch_in_collective(p_collective_id, p_query, p_types, p_creator, p_from, p_to)conSECURITY INVOKER(RLS aplica automáticamente) - Trash excluido vía
shopping_lists.deleted_at IS NULLynotes.deleted_at IS NULL GRANT EXECUTEaanon+authenticated
- Columnas STORED GENERATED
- Store
apps/web/src/lib/stores/search.ts— wrapper RPC con tipadoSearchRow - Pantalla
/search:- Input con debounce de 300ms + filtrado mínimo de 2 caracteres
- Filtros tipo (chips multi-toggle: Lists, Tasks, Notes — si todos activos no se envían, optimiza el SQL)
- Resultados agrupados por tipo con
data-testid="search-group-<type>"para Playwright - Deep-link según tipo (shopping_item →
/lists/[list_id], task →/tasks/[list_id], note →/notes/[id]) - Filtros por creador y rango de fechas: soportados por la RPC; pendiente exponer en la UI cuando se necesite
4.2 Perfil de usuario
- Pantalla
/profile— diferido. La mayoría de la funcionalidad ya existe en/settings(nombre, avatar, idioma). Abandonar colectivo y eliminar cuenta no se han priorizado; los triggers SQL relevantes (promoción automática del admin más antiguo) ya están implementados y testeados desde Fase 1.
4.3 PWA — Validación final
Diferido a un sprint posterior (requiere hardware y build de producción):
manifest.webmanifestcon iconos (512, 192, 180), theme-color, display: standalone- Migrar Service Worker a
injectManifestconsrc/sw.ts(ver gotcha #10 en CLAUDE.md) - Notificaciones push con suscripción en el perfil
El PWA con generateSW de la Fase 2b cubre el caso MVP (precaching básico + offline para listas de compra).
4.4 Seguridad y hardening
- Auditoría de políticas RLS —
rls-audit.test.ts(42 tests Vitest) prueba aislamiento cross-collective exhaustivo - Rate limiting en Kong — diferido a deploy de producción
- Headers de seguridad en nginx — gestionado externamente en la config de nginx del VPS
- Rotación de
JWT_SECRETyANON_KEYpara producción — diferido a deploy - Recordatorio: al rotar
JWT_SECREThay que actualizarroot/.envYapps/web/.env.developmenta la vez, luegodocker compose up -d --force-recreate+ reiniciar Vite (verproject_env_keys_trapen la memoria)
4.Z Verificación final — todos los tests verdes
just test-all→ 211 verdes, 2 skipped (34 pgTAP + 140 Vitest integration + 6 unit + 31 Playwright). Los 2 skipped siguen siendo los tests de presence bloqueados por bug upstream desupabase/realtime.- Lighthouse PWA score ≥ 90 — manual, diferido
- Prueba de instalación PWA en iPhone + Android — manual, diferido
- Aislamiento RLS auditado automáticamente —
rls-audit.test.tsreemplaza la prueba manual con curl
Criterio de aceptación: búsqueda global por UI funcional, RLS auditado, just test-all verde. Alcance de producción (PWA install, push, rate-limit, JWT rotation) queda explícitamente marcado como diferido para un sprint de deploy.