feat(fase-3): Tareas + Notas — TDD complete (148 tests green)
3.0 Tests primero
pgTAP: 005_tasks_rls.sql (5 — completion CHECK), 006_notes_trash_purge.sql (4 — purge SQL inline + pin/archive CHECK)
Vitest RLS: rls-tasks.test.ts (14), rls-notes.test.ts (15)
Playwright: tasks.test.ts (3), notes.test.ts (4)
3.1 Tareas
Migration 008_tasks.sql: task_lists + tasks + task_list_collective_id() helper
CHECK constraint: is_completed ⇔ completed_by ⇔ completed_at
RLS by role + cross-collective isolation
Store apps/web/src/lib/stores/tasks.ts (optimistic CRUD + reorder)
/tasks overview (grid + sticky create input + 5s polling)
/tasks/[id] detail with svelte-dnd-action reorder, flip animation,
inline edit (dblclick), "Completed by …" attribution via lazy-loaded
collective_members
3.2 Notas
Migration 009_notes.sql: notes + note_color enum (8) + pin/archive CHECK
+ 7d trash window in RLS + fn_notes_touch trigger + pg_cron purge job
Store apps/web/src/lib/stores/notes.ts (CRUD + pin/archive/trash/duplicate)
/notes board (pinned section testid notes-pinned + 30s polling)
/notes/[id] editor (title + textarea, 500ms debounced autosave,
color picker, pin/archive/duplicate/trash actions)
/notes/archive (restore + send to trash)
/notes/trash (restore + permanent delete)
3.Z Verification
just test-all → 148 verdes, 2 skipped:
25 pgTAP (was 16, +9)
88 Vitest integration (was 59, +29; 2 presence skipped)
6 Vitest unit (unchanged)
29 Playwright (was 22, +3 tasks +4 notes)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
75
supabase/tests/006_notes_trash_purge.sql
Normal file
75
supabase/tests/006_notes_trash_purge.sql
Normal file
@@ -0,0 +1,75 @@
|
||||
-- pgTAP: notes trash purge + pin/archive exclusivity — Fase 3
|
||||
-- Run with: psql -U postgres -d postgres -f supabase/tests/006_notes_trash_purge.sql
|
||||
--
|
||||
-- Validates:
|
||||
-- - The CHECK constraint forbidding (is_pinned AND is_archived)
|
||||
-- - The pg_cron purge job (executed inline) hard-deletes notes with
|
||||
-- deleted_at older than 7 days
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgtap;
|
||||
|
||||
BEGIN;
|
||||
SELECT plan(4);
|
||||
|
||||
-- Insert a note that is already 8 days in the trash
|
||||
INSERT INTO public.notes (id, collective_id, content, created_by, updated_by, deleted_at)
|
||||
VALUES (
|
||||
'99999999-1111-1111-1111-111111111111',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'expired trash note',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
now() - INTERVAL '8 days'
|
||||
);
|
||||
|
||||
-- Insert a note still inside the 7-day window
|
||||
INSERT INTO public.notes (id, collective_id, content, created_by, updated_by, deleted_at)
|
||||
VALUES (
|
||||
'99999999-2222-2222-2222-222222222222',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||
'recent trash note',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
now() - INTERVAL '2 days'
|
||||
);
|
||||
|
||||
-- N-PG-01: pre-purge sanity — both notes exist
|
||||
SELECT is(
|
||||
(SELECT count(*)::int FROM public.notes
|
||||
WHERE id IN ('99999999-1111-1111-1111-111111111111', '99999999-2222-2222-2222-222222222222')),
|
||||
2,
|
||||
'N-PG-01: both trash fixtures inserted'
|
||||
);
|
||||
|
||||
-- Run the purge job's SQL directly (matches what pg_cron schedules)
|
||||
DELETE FROM public.notes WHERE deleted_at < now() - INTERVAL '7 days';
|
||||
|
||||
-- N-PG-02: 8-day note is gone
|
||||
SELECT is(
|
||||
(SELECT count(*)::int FROM public.notes
|
||||
WHERE id = '99999999-1111-1111-1111-111111111111'),
|
||||
0,
|
||||
'N-PG-02: note older than 7 days is purged'
|
||||
);
|
||||
|
||||
-- N-PG-03: 2-day note still exists
|
||||
SELECT is(
|
||||
(SELECT count(*)::int FROM public.notes
|
||||
WHERE id = '99999999-2222-2222-2222-222222222222'),
|
||||
1,
|
||||
'N-PG-03: note inside the 7-day window survives'
|
||||
);
|
||||
|
||||
-- N-PG-04: pin + archive simultaneously violates CHECK
|
||||
SELECT throws_ok(
|
||||
$$ INSERT INTO public.notes (collective_id, content, created_by, updated_by, is_pinned, is_archived)
|
||||
VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'pin+archive',
|
||||
'11111111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111',
|
||||
true, true) $$,
|
||||
'23514',
|
||||
NULL,
|
||||
'N-PG-04: notes with is_pinned AND is_archived are rejected by CHECK'
|
||||
);
|
||||
|
||||
SELECT * FROM finish();
|
||||
ROLLBACK;
|
||||
Reference in New Issue
Block a user