\set ON_ERROR_STOP on BEGIN; CREATE OR REPLACE FUNCTION pg_temp.assert_true(condition boolean, message text) RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF condition IS NOT TRUE THEN RAISE EXCEPTION 'assertion_failed: %', message; END IF; END; $$; INSERT INTO auth.users ( id, aud, role, email, encrypted_password, email_confirmed_at, raw_app_meta_data, raw_user_meta_data, created_at, updated_at ) VALUES ( '29000000-0000-4000-8000-000000000001', 'authenticated', 'authenticated', 'content-reporter@example.invalid', crypt('fixture-password', gen_salt('bf')), now(), '{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now() ), ( '29000000-0000-4000-8000-000000000002', 'authenticated', 'authenticated', 'content-other@example.invalid', crypt('fixture-password', gen_salt('bf')), now(), '{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now() ), ( '29000000-0000-4000-8000-000000000003', 'authenticated', 'authenticated', 'content-manager@example.invalid', crypt('fixture-password', gen_salt('bf')), now(), '{"provider":"email","providers":["email"],"role":"manager"}'::jsonb, '{}'::jsonb, now(), now() ), ( '29000000-0000-4000-8000-000000000004', 'authenticated', 'authenticated', 'content-rate@example.invalid', crypt('fixture-password', gen_salt('bf')), now(), '{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now() ); UPDATE public.profiles SET role = 'manager' WHERE id = '29000000-0000-4000-8000-000000000003'; SELECT pg_temp.assert_true( NOT has_table_privilege('authenticated', 'public.content_generation_receipts', 'SELECT'), 'authenticated users cannot read generation receipts' ); SELECT pg_temp.assert_true( NOT has_table_privilege('authenticated', 'public.content_reports', 'SELECT'), 'authenticated users cannot read moderation snapshots directly' ); SELECT pg_temp.assert_true( NOT has_table_privilege('authenticated', 'public.content_reports', 'INSERT'), 'authenticated users cannot bypass the submission RPC' ); SELECT pg_temp.assert_true( NOT has_function_privilege( 'authenticated', 'public.submit_content_report_v1(uuid,uuid,text,text,uuid,text,text,text)', 'EXECUTE' ), 'authenticated users cannot forge the service submission actor' ); SELECT pg_temp.assert_true( NOT has_function_privilege( 'authenticated', 'public.purge_expired_content_reporting_data_v1()', 'EXECUTE' ), 'authenticated users cannot purge moderation evidence' ); SELECT pg_temp.assert_true( has_function_privilege( 'authenticated', 'public.admin_list_content_reports_v1(text,integer,timestamptz)', 'EXECUTE' ), 'moderation reads cross an explicit role-checking RPC' ); SELECT pg_temp.assert_true( NOT has_column_privilege('authenticated', 'public.profiles', 'role', 'UPDATE'), 'authenticated users cannot self-promote through profiles_update_own' ); SELECT pg_temp.assert_true( NOT has_column_privilege('authenticated', 'public.profiles', 'tier', 'UPDATE'), 'authenticated users cannot self-assign an entitlement tier' ); SELECT pg_temp.assert_true( has_column_privilege('authenticated', 'public.profiles', 'name', 'UPDATE') AND has_column_privilege('authenticated', 'public.profiles', 'avatar_url', 'UPDATE') AND has_column_privilege('authenticated', 'public.profiles', 'locale', 'UPDATE'), 'authenticated users retain the intended self-service profile fields' ); SET LOCAL ROLE authenticated; SELECT set_config('request.jwt.claim.role', 'authenticated', true); SELECT set_config('request.jwt.claim.sub', '29000000-0000-4000-8000-000000000002', true); DO $$ BEGIN BEGIN UPDATE public.profiles SET role = 'manager' WHERE id = '29000000-0000-4000-8000-000000000002'; RAISE EXCEPTION 'expected_profile_role_update_denied'; EXCEPTION WHEN insufficient_privilege THEN NULL; END; BEGIN UPDATE public.profiles SET tier = 'pro_plus' WHERE id = '29000000-0000-4000-8000-000000000002'; RAISE EXCEPTION 'expected_profile_tier_update_denied'; EXCEPTION WHEN insufficient_privilege THEN NULL; END; UPDATE public.profiles SET name = 'Safe self-service profile', locale = 'en' WHERE id = '29000000-0000-4000-8000-000000000002'; END; $$; RESET ROLE; SELECT pg_temp.assert_true( EXISTS ( SELECT 1 FROM public.profiles WHERE id = '29000000-0000-4000-8000-000000000002' AND name = 'Safe self-service profile' AND locale = 'en' AND role = 'user' AND tier = 'free' ), 'profile ACL preserves public edits while managed fields remain unchanged' ); SELECT set_config('request.jwt.claim.role', 'service_role', true); DO $$ DECLARE receipt jsonb; first_result jsonb; replay_result jsonb; command_receipt jsonb; action_receipt jsonb; BEGIN receipt := public.issue_content_generation_receipt_v1( '29000000-0000-4000-8000-000000000001', 'talk_response', 'claude-haiku-4-5-20251001' ); PERFORM pg_temp.assert_true(receipt->>'generationId' IS NOT NULL, 'Talk generation receives a server-side receipt'); first_result := public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000001', 'ai_output', 'talk_response', (receipt->>'generationId')::uuid, 'harmful', 'Please review this output', 'Reporter-selected AI response evidence' ); PERFORM pg_temp.assert_true( first_result->>'status' = 'submitted' AND NOT (first_result->>'idempotent')::boolean, 'first submission creates a report' ); replay_result := public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000001', 'ai_output', 'talk_response', (receipt->>'generationId')::uuid, 'harmful', 'Please review this output', 'Reporter-selected AI response evidence' ); PERFORM pg_temp.assert_true( (replay_result->>'idempotent')::boolean AND replay_result->>'reportId' = first_result->>'reportId', 'same key and body replay without a duplicate row' ); BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000001', 'ai_output', 'talk_response', (receipt->>'generationId')::uuid, 'privacy', NULL, 'Different body' ); RAISE EXCEPTION 'expected_idempotency_conflict'; EXCEPTION WHEN SQLSTATE 'PT409' THEN NULL; END; BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000002', '29100000-0000-4000-8000-000000000002', 'ai_output', 'talk_response', (receipt->>'generationId')::uuid, 'privacy', NULL, 'Cross-user attempt' ); RAISE EXCEPTION 'expected_source_not_found'; EXCEPTION WHEN SQLSTATE 'P0002' THEN NULL; END; command_receipt := public.issue_content_generation_receipt_v1( '29000000-0000-4000-8000-000000000001', 'command_response', 'claude-haiku-4-5-20251001' ); action_receipt := public.issue_content_generation_receipt_v1( '29000000-0000-4000-8000-000000000001', 'action_response', 'claude-haiku-4-5-20251001' ); PERFORM pg_temp.assert_true( command_receipt->>'generationId' IS NOT NULL AND action_receipt->>'generationId' IS NOT NULL, 'Commands and Actions share the implemented receipt boundary' ); BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000003', 'ai_output', 'talk_response', (command_receipt->>'generationId')::uuid, 'other', NULL, 'Surface mismatch' ); RAISE EXCEPTION 'expected_surface_mismatch'; EXCEPTION WHEN SQLSTATE 'P0002' THEN NULL; END; END; $$; INSERT INTO public.meetings(id, user_id, title, status) VALUES ( '29700000-0000-4000-8000-000000000001', '29000000-0000-4000-8000-000000000001', 'Generated document report fixture', 'completed' ); INSERT INTO public.meeting_documents( id, meeting_id, user_id, template_type, title, content, prompt_used, llm_model, llm_latency_ms, generation_idempotency_key ) VALUES ( '29800000-0000-4000-8000-000000000001', '29700000-0000-4000-8000-000000000001', '29000000-0000-4000-8000-000000000001', 'minutes', 'Generated minutes', 'Generated meeting document evidence', 'template:fixture@1', 'claude-haiku-4-5-20251001', 12, '29900000-0000-4000-8000-000000000001' ), ( '29800000-0000-4000-8000-000000000002', '29700000-0000-4000-8000-000000000001', '29000000-0000-4000-8000-000000000001', 'custom', 'Manual document', 'This row has no generation provenance', NULL, NULL, NULL, NULL ); INSERT INTO public.meeting_document_generation_audit( user_id, meeting_id, document_id, idempotency_key, model, template_revision, transcript_hash, latency_ms ) VALUES ( '29000000-0000-4000-8000-000000000001', '29700000-0000-4000-8000-000000000001', '29800000-0000-4000-8000-000000000001', '29900000-0000-4000-8000-000000000001', 'claude-haiku-4-5-20251001', 1, repeat('a', 64), 12 ); DO $$ DECLARE result jsonb; BEGIN result := public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000007', 'ai_output', 'meeting_document', '29800000-0000-4000-8000-000000000001', 'misinformation', NULL, 'Generated meeting document evidence' ); PERFORM pg_temp.assert_true( result->>'status' = 'submitted' AND EXISTS ( SELECT 1 FROM public.content_reports WHERE id = (result->>'reportId')::uuid AND source_type = 'meeting_document' AND generation_receipt_id IS NULL ), 'owned meeting document with generation audit is reportable' ); BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000002', '29100000-0000-4000-8000-000000000008', 'ai_output', 'meeting_document', '29800000-0000-4000-8000-000000000001', 'privacy', NULL, 'Cross-user generated document' ); RAISE EXCEPTION 'expected_cross_user_document_not_found'; EXCEPTION WHEN SQLSTATE 'P0002' THEN NULL; END; BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000001', '29100000-0000-4000-8000-000000000009', 'ai_output', 'meeting_document', '29800000-0000-4000-8000-000000000002', 'other', NULL, 'Manual document masquerading as AI output' ); RAISE EXCEPTION 'expected_generation_audit_required'; EXCEPTION WHEN SQLSTATE 'P0002' THEN NULL; END; END; $$; DO $$ DECLARE purge_result jsonb; BEGIN INSERT INTO public.content_generation_receipts( id, user_id, purpose, model, created_at, expires_at ) VALUES ( '29600000-0000-4000-8000-000000000001', '29000000-0000-4000-8000-000000000002', 'talk_response', 'expired-fixture', now() - interval '31 days', now() - interval '1 day' ); purge_result := public.purge_expired_content_reporting_data_v1(); PERFORM pg_temp.assert_true( (purge_result->>'purgedReceipts')::integer >= 1 AND NOT EXISTS ( SELECT 1 FROM public.content_generation_receipts WHERE id = '29600000-0000-4000-8000-000000000001' ), 'service cleanup removes an expired content-free receipt' ); END; $$; -- Fill the hourly window for a separate actor through service-only rows, then -- prove that the atomic RPC denies the next otherwise valid receipt. INSERT INTO public.content_generation_receipts(id, user_id, purpose, model) SELECT ('29200000-0000-4000-8000-' || lpad(ordinal::text, 12, '0'))::uuid, '29000000-0000-4000-8000-000000000004'::uuid, 'talk_response', 'fixture-model' FROM generate_series(1, 11) AS ordinal; INSERT INTO public.content_reports( reporter_id, idempotency_key, request_hash, kind, source_type, source_id, generation_receipt_id, reason, reported_snapshot, snapshot_sha256 ) SELECT '29000000-0000-4000-8000-000000000004'::uuid, gen_random_uuid(), encode(extensions.digest(('fixture-' || ordinal)::text, 'sha256'), 'hex'), 'ai_output', 'talk_response', ('29200000-0000-4000-8000-' || lpad(ordinal::text, 12, '0'))::uuid, ('29200000-0000-4000-8000-' || lpad(ordinal::text, 12, '0'))::uuid, 'spam', 'fixture evidence ' || ordinal, encode(extensions.digest(('fixture evidence ' || ordinal)::text, 'sha256'), 'hex') FROM generate_series(1, 10) AS ordinal; DO $$ DECLARE remaining_receipt uuid; BEGIN SELECT receipt.id INTO remaining_receipt FROM public.content_generation_receipts AS receipt WHERE receipt.user_id = '29000000-0000-4000-8000-000000000004' AND NOT EXISTS ( SELECT 1 FROM public.content_reports AS report WHERE report.generation_receipt_id = receipt.id ) LIMIT 1; BEGIN PERFORM public.submit_content_report_v1( '29000000-0000-4000-8000-000000000004', '29100000-0000-4000-8000-000000000004', 'ai_output', 'talk_response', remaining_receipt, 'spam', NULL, 'Rate-limited evidence' ); RAISE EXCEPTION 'expected_rate_limit'; EXCEPTION WHEN SQLSTATE 'PT429' THEN NULL; END; END; $$; SELECT set_config('request.jwt.claim.role', 'authenticated', true); SELECT set_config('request.jwt.claim.sub', '29000000-0000-4000-8000-000000000003', true); DO $$ DECLARE queued record; acted jsonb; replay jsonb; resolved jsonb; evidence_expiry timestamptz; BEGIN SELECT * INTO queued FROM public.admin_list_content_reports_v1('pending', 100, NULL) WHERE reporter_id = '29000000-0000-4000-8000-000000000001' LIMIT 1; PERFORM pg_temp.assert_true( queued.report_id IS NOT NULL AND queued.reported_snapshot = 'Reporter-selected AI response evidence', 'manager can read the bounded moderation evidence through the RPC' ); acted := public.admin_act_on_content_report_v1( queued.report_id, '29100000-0000-4000-8000-000000000005', 'begin_review', 'Initial policy review' ); PERFORM pg_temp.assert_true( acted->>'status' = 'reviewing' AND NOT (acted->>'idempotent')::boolean, 'manager can start review through the action RPC' ); replay := public.admin_act_on_content_report_v1( queued.report_id, '29100000-0000-4000-8000-000000000005', 'begin_review', 'Initial policy review' ); PERFORM pg_temp.assert_true( (replay->>'idempotent')::boolean, 'moderation action replay is idempotent' ); resolved := public.admin_act_on_content_report_v1( queued.report_id, '29100000-0000-4000-8000-000000000006', 'dismiss', 'No policy violation after review' ); SELECT evidence_expires_at INTO evidence_expiry FROM public.content_reports WHERE id = queued.report_id; PERFORM pg_temp.assert_true( resolved->>'status' = 'dismissed' AND evidence_expiry BETWEEN now() + interval '179 days' AND now() + interval '181 days', 'terminal moderation sets the implemented 180-day evidence window' ); END; $$; SELECT set_config('request.jwt.claim.sub', '29000000-0000-4000-8000-000000000002', true); DO $$ BEGIN BEGIN PERFORM public.admin_list_content_reports_v1(NULL, 10, NULL); RAISE EXCEPTION 'expected_manager_required'; EXCEPTION WHEN insufficient_privilege THEN NULL; END; END; $$; SELECT pg_temp.assert_true( EXISTS ( SELECT 1 FROM public.audit_log WHERE action = 'content_report.dismiss' AND target_type = 'content_report' AND admin_id = '29000000-0000-4000-8000-000000000003' ), 'moderation resolution records metadata-only audit evidence before manager deletion' ); DELETE FROM auth.users WHERE id = '29000000-0000-4000-8000-000000000003'; SELECT pg_temp.assert_true( EXISTS ( SELECT 1 FROM public.audit_log WHERE action = 'content_report.dismiss' AND target_type = 'content_report' AND admin_id IS NULL ), 'manager deletion preserves audit evidence while unlinking the manager identity' ); -- Account deletion removes the generation ledger identity but retains a -- de-identified moderation record and its selected evidence. DELETE FROM auth.users WHERE id = '29000000-0000-4000-8000-000000000001'; SELECT pg_temp.assert_true( EXISTS ( SELECT 1 FROM public.content_reports WHERE reporter_id IS NULL AND reported_snapshot = 'Reporter-selected AI response evidence' AND generation_receipt_id IS NULL ), 'moderation evidence survives account deletion without the reporter identity' ); ROLLBACK;