BEGIN; SELECT plan(6); DO $$ DECLARE owner_id uuid := gen_random_uuid(); member_id uuid := gen_random_uuid(); tablo_id text; parent_task_id text; BEGIN -- Insert auth users INSERT INTO auth.users (id, instance_id, aud, role, email, encrypted_password, email_confirmed_at, created_at, updated_at) VALUES (owner_id, '00000000-0000-0000-0000-000000000000', 'authenticated', 'authenticated', 'etape_owner_' || owner_id::text || '@test.com', 'encrypted', now(), now(), now()), (member_id, '00000000-0000-0000-0000-000000000000', 'authenticated', 'authenticated', 'etape_member_' || member_id::text || '@test.com', 'encrypted', now(), now(), now()) ON CONFLICT DO NOTHING; -- Insert public profiles INSERT INTO public.profiles (id, email, first_name, last_name, short_user_id) VALUES (owner_id, 'etape_owner_' || owner_id::text || '@test.com', 'Etape', 'Owner', substring(owner_id::text from 1 for 8)), (member_id, 'etape_member_' || member_id::text || '@test.com', 'Etape', 'Member', substring(member_id::text from 1 for 8)) ON CONFLICT DO NOTHING; -- Create tablo as owner PERFORM set_config('request.jwt.claims', json_build_object('sub', owner_id::text)::text, true); INSERT INTO public.tablos (owner_id, name, status, position) VALUES (owner_id, 'Parent Tasks Test Tablo', 'todo', 0) RETURNING id INTO tablo_id; -- Grant access to member INSERT INTO public.tablo_access (tablo_id, user_id, granted_by, is_active, is_admin) VALUES (tablo_id, member_id, owner_id, true, false); -- Create an Etape as owner INSERT INTO public.tasks (tablo_id, title, description, status, position, is_parent) VALUES (tablo_id, 'Initial Étape', 'Owner created etape', 'todo', 0, true) RETURNING id INTO parent_task_id; -- Persist identifiers for tests PERFORM set_config('test.owner_id', owner_id::text, true); PERFORM set_config('test.member_id', member_id::text, true); PERFORM set_config('test.tablo_id', tablo_id, true); PERFORM set_config('test.parent_task_id', parent_task_id, true); END $$; SELECT is( (SELECT is_parent FROM public.tasks WHERE id = current_setting('test.parent_task_id')), true, 'Owner-created task is flagged as an Etape' ); SELECT is( (SELECT parent_task_id FROM public.tasks WHERE id = current_setting('test.parent_task_id')), NULL, 'Etape cannot reference another parent task' ); DO $$ BEGIN PERFORM set_config('request.jwt.claims', json_build_object('sub', current_setting('test.owner_id'))::text, true); UPDATE public.tasks SET title = 'Renamed Étape' WHERE id = current_setting('test.parent_task_id'); END $$; SELECT is( (SELECT title FROM public.tasks WHERE id = current_setting('test.parent_task_id')), 'Renamed Étape', 'Tablo owner can update an Etape' ); DO $$ DECLARE failed boolean := false; BEGIN PERFORM set_config('request.jwt.claims', json_build_object('sub', current_setting('test.member_id'))::text, true); BEGIN INSERT INTO public.tasks (tablo_id, title, status, position, is_parent) VALUES (current_setting('test.tablo_id'), 'Member Étape Attempt', 'todo', 1, true); EXCEPTION WHEN insufficient_privilege THEN failed := true; WHEN others THEN RAISE; END; PERFORM set_config('test.member_etape_insert_blocked', failed::text, true); END $$; SELECT is( current_setting('test.member_etape_insert_blocked', true)::boolean, true, 'Non-owner cannot create an Etape' ); DO $$ DECLARE failed boolean := false; BEGIN PERFORM set_config('request.jwt.claims', json_build_object('sub', current_setting('test.member_id'))::text, true); BEGIN UPDATE public.tasks SET title = 'Member Update Attempt' WHERE id = current_setting('test.parent_task_id'); EXCEPTION WHEN insufficient_privilege THEN failed := true; WHEN others THEN RAISE; END; PERFORM set_config('test.member_etape_update_blocked', failed::text, true); END $$; SELECT is( current_setting('test.member_etape_update_blocked', true)::boolean, true, 'Non-owner cannot update an Etape' ); DO $$ DECLARE child_task_id text; BEGIN PERFORM set_config('request.jwt.claims', json_build_object('sub', current_setting('test.member_id'))::text, true); INSERT INTO public.tasks (tablo_id, title, status, position, is_parent, parent_task_id) VALUES ( current_setting('test.tablo_id'), 'Child Task', 'todo', 2, false, current_setting('test.parent_task_id') ) RETURNING id INTO child_task_id; PERFORM set_config('test.child_task_id', child_task_id, true); END $$; SELECT ok( ( SELECT EXISTS ( SELECT 1 FROM public.tasks WHERE id = current_setting('test.child_task_id') ) ), 'Members can still create regular tasks assigned to an Etape' ); SELECT * FROM finish(); ROLLBACK;