-- pgTAP: dissolve_collective() RPC — Fase 10.3 (CU-H08) -- Run with: psql -U postgres -d postgres -f supabase/tests/011_dissolve_collective.sql -- -- Covers: -- * admin disolves: collective + all content cascade-deleted -- * member rejected (errcode P0002) -- * guest rejected -- * non-member rejected CREATE EXTENSION IF NOT EXISTS pgtap; BEGIN; SELECT plan(8); -- ── Existence ──────────────────────────────────────────────────────────────── SELECT has_function( 'public', 'dissolve_collective', ARRAY['uuid'], 'public.dissolve_collective(uuid) RPC exists' ); -- ── Setup: one collective with content, three members ──────────────────────── INSERT INTO public.users (id, email, display_name, language, avatar_type) VALUES ('e2000000-0000-0000-0000-000000000001', 'diss-admin@local', 'Diss Admin', 'es', 'initials'), ('e2000000-0000-0000-0000-000000000002', 'diss-member@local', 'Diss Member', 'es', 'initials'), ('e2000000-0000-0000-0000-000000000003', 'diss-guest@local', 'Diss Guest', 'es', 'initials') ON CONFLICT (id) DO NOTHING; -- Collective owned by Borja so we can delete the test admin user later. INSERT INTO public.collectives (id, name, emoji, created_by) VALUES ('cccccccc-3333-3333-3333-333333333333', 'Dissolve test', '💥', '22222222-2222-2222-2222-222222222222') ON CONFLICT (id) DO NOTHING; INSERT INTO public.collective_members (collective_id, user_id, role) VALUES ('cccccccc-3333-3333-3333-333333333333', 'e2000000-0000-0000-0000-000000000001', 'admin'), ('cccccccc-3333-3333-3333-333333333333', 'e2000000-0000-0000-0000-000000000002', 'member'), ('cccccccc-3333-3333-3333-333333333333', 'e2000000-0000-0000-0000-000000000003', 'guest') ON CONFLICT (collective_id, user_id) DO NOTHING; INSERT INTO public.shopping_lists (id, collective_id, name, status, created_by) VALUES ('cccccccc-3333-3333-3333-3333deadbeef', 'cccccccc-3333-3333-3333-333333333333', 'List to nuke', 'active', 'e2000000-0000-0000-0000-000000000001') ON CONFLICT (id) DO NOTHING; INSERT INTO public.shopping_items (list_id, name, sort_order, created_by) VALUES ('cccccccc-3333-3333-3333-3333deadbeef', 'Milk', 1, 'e2000000-0000-0000-0000-000000000001'); -- ── Case 1: member tries to dissolve → P0002 ───────────────────────────────── SELECT set_config('request.jwt.claim.sub', 'e2000000-0000-0000-0000-000000000002', true); SELECT throws_ok( $$ SELECT public.dissolve_collective('cccccccc-3333-3333-3333-333333333333') $$, 'P0002', NULL, 'D-pgtap-a: member rejected with P0002' ); -- ── Case 2: guest tries to dissolve → P0002 ────────────────────────────────── SELECT set_config('request.jwt.claim.sub', 'e2000000-0000-0000-0000-000000000003', true); SELECT throws_ok( $$ SELECT public.dissolve_collective('cccccccc-3333-3333-3333-333333333333') $$, 'P0002', NULL, 'D-pgtap-b: guest rejected with P0002' ); -- ── Case 3: non-member rejected ────────────────────────────────────────────── SELECT set_config('request.jwt.claim.sub', '55555555-5555-5555-5555-555555555555', true); -- Eva SELECT throws_ok( $$ SELECT public.dissolve_collective('cccccccc-3333-3333-3333-333333333333') $$, 'P0002', NULL, 'D-pgtap-c: non-member rejected with P0002' ); -- ── Case 4: admin succeeds + cascade ───────────────────────────────────────── SELECT set_config('request.jwt.claim.sub', 'e2000000-0000-0000-0000-000000000001', true); SELECT lives_ok( $$ SELECT public.dissolve_collective('cccccccc-3333-3333-3333-333333333333') $$, 'D-pgtap-d: admin can dissolve without error' ); SELECT is( (SELECT count(*)::int FROM public.collectives WHERE id = 'cccccccc-3333-3333-3333-333333333333'), 0, 'D-pgtap-d: collective row gone' ); SELECT is( (SELECT count(*)::int FROM public.shopping_lists WHERE id = 'cccccccc-3333-3333-3333-3333deadbeef'), 0, 'D-pgtap-d: child shopping_list cascade-deleted' ); SELECT is( (SELECT count(*)::int FROM public.collective_members WHERE collective_id = 'cccccccc-3333-3333-3333-333333333333'), 0, 'D-pgtap-d: collective_members cascade-deleted' ); SELECT * FROM finish(); ROLLBACK;