-- pgTAP: leave_collective() RPC — Fase 10.1 -- Run with: psql -U postgres -d postgres -f supabase/tests/010_leave_collective.sql -- -- Covers CU-H06: -- * normal member abandons OK (membership row gone) -- * sole admin with other members abandons OK + oldest member auto-promoted -- * sole member rejected with errcode P0001 (must use dissolve_collective) CREATE EXTENSION IF NOT EXISTS pgtap; BEGIN; SELECT plan(7); -- ── Existence ──────────────────────────────────────────────────────────────── SELECT has_function( 'public', 'leave_collective', ARRAY['uuid'], 'public.leave_collective(uuid) RPC exists' ); -- ── Setup: three test users + two collectives outside seed range ────────────── INSERT INTO public.users (id, email, display_name, language, avatar_type) VALUES ('e1000000-0000-0000-0000-000000000001', 'leave-admin@local', 'Leave Admin', 'es', 'initials'), ('e1000000-0000-0000-0000-000000000002', 'leave-member@local', 'Leave Member', 'es', 'initials'), ('e1000000-0000-0000-0000-000000000003', 'leave-solo@local', 'Leave Solo', 'es', 'initials') ON CONFLICT (id) DO NOTHING; -- Collective A: admin + member. created_by = Borja (so deleting admin row is safe). INSERT INTO public.collectives (id, name, emoji, created_by) VALUES ('cccccccc-1111-1111-1111-111111111111', 'Leave test A', '🚪', '22222222-2222-2222-2222-222222222222') ON CONFLICT (id) DO NOTHING; INSERT INTO public.collective_members (collective_id, user_id, role, joined_at) VALUES ('cccccccc-1111-1111-1111-111111111111', 'e1000000-0000-0000-0000-000000000001', 'admin', now() - INTERVAL '10 days'), ('cccccccc-1111-1111-1111-111111111111', 'e1000000-0000-0000-0000-000000000002', 'member', now() - INTERVAL '5 days') ON CONFLICT (collective_id, user_id) DO NOTHING; -- Collective B: sole member only. INSERT INTO public.collectives (id, name, emoji, created_by) VALUES ('cccccccc-2222-2222-2222-222222222222', 'Leave test B (solo)', '🚪', '22222222-2222-2222-2222-222222222222') ON CONFLICT (id) DO NOTHING; INSERT INTO public.collective_members (collective_id, user_id, role) VALUES ('cccccccc-2222-2222-2222-222222222222', 'e1000000-0000-0000-0000-000000000003', 'admin') ON CONFLICT (collective_id, user_id) DO NOTHING; -- ── Case 1: normal member leaves ────────────────────────────────────────────── -- Authenticate as leave-member SELECT set_config('request.jwt.claim.sub', 'e1000000-0000-0000-0000-000000000002', true); SELECT lives_ok( $$ SELECT public.leave_collective('cccccccc-1111-1111-1111-111111111111') $$, 'L-01a: member can leave without error' ); SELECT is( (SELECT count(*)::int FROM public.collective_members WHERE collective_id = 'cccccccc-1111-1111-1111-111111111111' AND user_id = 'e1000000-0000-0000-0000-000000000002'), 0, 'L-01a: membership row removed after leave' ); -- ── Case 2: sole admin with other members ───────────────────────────────────── -- Re-add the member to set up sole-admin scenario again. INSERT INTO public.collective_members (collective_id, user_id, role, joined_at) VALUES ('cccccccc-1111-1111-1111-111111111111', 'e1000000-0000-0000-0000-000000000002', 'member', now() - INTERVAL '5 days') ON CONFLICT (collective_id, user_id) DO UPDATE SET role = EXCLUDED.role; -- Authenticate as the admin SELECT set_config('request.jwt.claim.sub', 'e1000000-0000-0000-0000-000000000001', true); SELECT lives_ok( $$ SELECT public.leave_collective('cccccccc-1111-1111-1111-111111111111') $$, 'L-01b: sole admin can leave when other members exist' ); SELECT is( (SELECT role::text FROM public.collective_members WHERE collective_id = 'cccccccc-1111-1111-1111-111111111111' AND user_id = 'e1000000-0000-0000-0000-000000000002'), 'admin', 'L-01b: remaining member auto-promoted to admin' ); SELECT is( (SELECT count(*)::int FROM public.collective_members WHERE collective_id = 'cccccccc-1111-1111-1111-111111111111' AND user_id = 'e1000000-0000-0000-0000-000000000001'), 0, 'L-01b: departing admin row removed' ); -- ── Case 3: sole member tries to leave ──────────────────────────────────────── -- Authenticate as leave-solo SELECT set_config('request.jwt.claim.sub', 'e1000000-0000-0000-0000-000000000003', true); SELECT throws_ok( $$ SELECT public.leave_collective('cccccccc-2222-2222-2222-222222222222') $$, 'P0001', NULL, 'L-01c: sole member rejected with errcode P0001' ); SELECT * FROM finish(); ROLLBACK;