-- KTO-01 Registrierung (Datenbankseite): Regeln des Triggers handle_new_user, Anzeigename, -- Nachweis der Zustimmung (terms_version), Sprache und Mailsprache-Sync. begin; select plan(46); -- --------------------------------------------------------------------------------------------- -- KTO-01 Kriterium 1: gültige Registrierung legt ein Profil an -- --------------------------------------------------------------------------------------------- select lives_ok( $$insert into auth.users (id, email, raw_user_meta_data) values ('11111111-1111-1111-1111-111111111111', 'anna@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"2026-11-draft","display_name":" Anna ","locale":"de"}')$$, 'KTO-01 Kriterium 1: gültige Registrierung wird angenommen'); select is((select display_name from public.profiles where id = '11111111-1111-1111-1111-111111111111'), 'Anna', 'KTO-01 Kriterium 1: Anzeigename wird getrimmt gespeichert'); select is((select locale from public.profiles where id = '11111111-1111-1111-1111-111111111111'), 'de', 'KTO-01 Kriterium 11: Sprache aus der Registrierung wird übernommen'); select is((select terms_version from public.profiles where id = '11111111-1111-1111-1111-111111111111'), '2026-11-draft', 'KTO-01 Kriterium 1: terms_version wird gespeichert (QUE-04 Nachweis)'); select ok((select terms_accepted_at is not null and age_confirmed_at is not null and terms_accepted_at = now() and age_confirmed_at = now() from public.profiles where id = '11111111-1111-1111-1111-111111111111'), 'KTO-01 Kriterium 1: Zeitpunkte von Zustimmung und Altersbestätigung sind gesetzt'); -- --------------------------------------------------------------------------------------------- -- KTO-01 Kriterium 2: Mindestalter muss bestätigt sein -- --------------------------------------------------------------------------------------------- select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'k2a@example.com', '{"age_confirmed":"false","terms_accepted":"true","terms_version":"v1","display_name":"X"}')$$, 'P0001', 'age_confirmation_required', 'KTO-01 Kriterium 2: age_confirmed=false wird abgelehnt'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'k2b@example.com', '{"terms_accepted":"true","terms_version":"v1","display_name":"X"}')$$, 'P0001', 'age_confirmation_required', 'KTO-01 Kriterium 2: fehlende Altersbestätigung wird abgelehnt'); select is((select count(*) from auth.users where email like 'k2%@example.com'), 0::bigint, 'KTO-01 Kriterium 2: abgelehnte Registrierung legt kein Konto an'); -- --------------------------------------------------------------------------------------------- -- QUE-04 Kriterium 1: ausdrückliche Zustimmung -- --------------------------------------------------------------------------------------------- select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'q1@example.com', '{"age_confirmed":"true","terms_accepted":"false","terms_version":"v1","display_name":"X"}')$$, 'P0001', 'terms_acceptance_required', 'QUE-04 Kriterium 1: terms_accepted=false wird abgelehnt'); -- terms_version Pflicht (Nachweis F5) select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'tv1@example.com', '{"age_confirmed":"true","terms_accepted":"true","display_name":"X"}')$$, 'P0001', 'terms_version_required', 'QUE-04 Nachweis: fehlende terms_version wird abgelehnt'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'tv2@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"","display_name":"X"}')$$, 'P0001', 'terms_version_required', 'QUE-04 Nachweis: leere terms_version wird abgelehnt'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'tv3@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":" ","display_name":"X"}')$$, 'P0001', 'terms_version_required', 'QUE-04 Nachweis: terms_version nur aus Leerzeichen wird abgelehnt'); select throws_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'tv4@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"%s","display_name":"X"}')$$, repeat('v', 51)), 'P0001', 'terms_version_required', 'QUE-04 Nachweis: terms_version mit 51 Zeichen wird abgelehnt'); select lives_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values ('55555555-5555-5555-5555-555555555555', 'tv5@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":" %s ","display_name":"X"}')$$, repeat('v', 50)), 'QUE-04 Nachweis: terms_version mit 50 Zeichen (getrimmt) wird angenommen'); select is((select char_length(terms_version) from public.profiles where id = '55555555-5555-5555-5555-555555555555'), 50, 'QUE-04 Nachweis: terms_version wird getrimmt gespeichert'); -- --------------------------------------------------------------------------------------------- -- KTO-01 Kriterium 10: Anzeigename 1-30 Zeichen, streng -- --------------------------------------------------------------------------------------------- select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n1@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1"}')$$, 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: fehlender Anzeigename wird abgelehnt (kein E-Mail-Präfix)'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n2@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":""}')$$, 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: leerer Anzeigename wird abgelehnt'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n3@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":" "}')$$, 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: Anzeigename nur aus Leerzeichen wird abgelehnt'); select throws_ok( $$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n4@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":null}')$$, 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: Anzeigename null wird abgelehnt'); select throws_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n5@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":"%s"}')$$, repeat('a', 31)), 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: Anzeigename mit 31 Zeichen wird abgelehnt (kein Kürzen)'); select throws_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values (gen_random_uuid(), 'n6@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":"%s"}')$$, repeat('😀', 31)), 'P0001', 'display_name_invalid', 'KTO-01 Kriterium 10: 31 Emoji (Codepoints) werden abgelehnt'); select is((select count(*) from auth.users where email ~ '^n[0-9]@example.com$'), 0::bigint, 'KTO-01 Kriterium 10: abgelehnte Registrierungen legen kein Konto an'); select lives_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values ('66666666-6666-6666-6666-666666666666', 'n7@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":"%s"}')$$, repeat('😀', 30)), 'KTO-01 Kriterium 10: 30 Emoji (30 Codepoints, 120 Bytes) werden angenommen'); select lives_ok( format($$insert into auth.users (id, email, raw_user_meta_data) values ('77777777-7777-7777-7777-777777777777', 'n8@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":" %s "}')$$, repeat('b', 30)), 'KTO-01 Kriterium 10: 30 Zeichen mit umgebenden Leerzeichen werden nach dem Trim angenommen'); select is((select display_name from public.profiles where id = '77777777-7777-7777-7777-777777777777'), repeat('b', 30), 'KTO-01 Kriterium 10: der Name wird ohne Leerraum gespeichert'); -- Check-Constraint (gilt auch für spätere Änderungen, KTO-02) select col_has_check('public', 'profiles', 'display_name', 'KTO-01 Kriterium 10: Anzeigename hat Check-Constraints'); select throws_ok( $$update public.profiles set display_name = ' ' where id = '11111111-1111-1111-1111-111111111111'$$, '23514', null, 'KTO-01 Kriterium 10: Constraint lehnt Anzeigename nur aus Leerzeichen ab (Update)'); -- --------------------------------------------------------------------------------------------- -- KTO-01 Kriterium 11: Sprache -- --------------------------------------------------------------------------------------------- select lives_ok( $$insert into auth.users (id, email, raw_user_meta_data) values ('22222222-2222-2222-2222-222222222222', 'ben@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":"Ben"}'), ('33333333-3333-3333-3333-333333333333', 'cara@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"v1","display_name":"Cara","locale":"fr"}')$$, 'KTO-01 Kriterium 11: Registrierungen ohne und mit ungültiger Sprache werden angenommen'); select is((select locale from public.profiles where id = '22222222-2222-2222-2222-222222222222'), 'en', 'KTO-01 Kriterium 11: ohne locale gilt Englisch'); select is((select locale from public.profiles where id = '33333333-3333-3333-3333-333333333333'), 'en', 'KTO-01 Kriterium 11: ungültige locale fällt auf Englisch zurück'); select col_default_is('public', 'profiles', 'locale', 'en', 'KTO-01 Kriterium 11: Spalten-Standard ist en'); -- Sync profiles.locale -> auth.users.raw_user_meta_data.locale select is((select raw_user_meta_data ->> 'locale' from auth.users where id = '11111111-1111-1111-1111-111111111111'), 'de', 'KTO-01 Kriterium 11: Ausgangszustand der Mail-Sprache (Anna de)'); -- Marker setzen: Eine Änderung anderer Spalten darf den Sync nicht auslösen update auth.users set raw_user_meta_data = raw_user_meta_data || '{"locale":"xx"}' where id = '11111111-1111-1111-1111-111111111111'; select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true); set local role authenticated; select lives_ok($$update public.profiles set bio = 'nur Bio' where id = '11111111-1111-1111-1111-111111111111'$$, 'KTO-01 Kriterium 11: Anna darf ihre Bio ändern'); reset role; select is((select raw_user_meta_data ->> 'locale' from auth.users where id = '11111111-1111-1111-1111-111111111111'), 'xx', 'KTO-01 Kriterium 11: Änderung einer anderen Spalte löst den Sync nicht aus'); set local role authenticated; select lives_ok($$update public.profiles set locale = 'en' where id = '11111111-1111-1111-1111-111111111111'$$, 'KTO-01 Kriterium 11: Anna darf ihre Sprache ändern'); select is_empty($$update public.profiles set locale = 'de' where id = '22222222-2222-2222-2222-222222222222' returning 1$$, 'KTO-01 Kriterium 11: Anna kann die Sprache von Ben nicht ändern'); select throws_ok($$update auth.users set raw_user_meta_data = '{}' where id = '11111111-1111-1111-1111-111111111111'$$, '42501', null, 'KTO-01 Kriterium 11: Nutzer kann auth.users nicht direkt schreiben'); select throws_ok($$select public.sync_profile_locale_to_auth()$$, '42501', null, 'KTO-01 Kriterium 11: Sync-Funktion ist für Nutzer nicht aufrufbar'); select throws_ok($$select public.handle_new_user()$$, '42501', null, 'KTO-01 Kriterium 1: handle_new_user ist für Nutzer nicht aufrufbar'); -- Sicherheit: Zustimmungsnachweis ist für den Nutzer unveränderlich select throws_ok($$update public.profiles set terms_version = 'gefälscht' where id = '11111111-1111-1111-1111-111111111111'$$, '42501', null, 'KTO-01 Sicherheit: Nutzer kann terms_version nicht ändern'); select throws_ok($$update public.profiles set terms_accepted_at = now() where id = '11111111-1111-1111-1111-111111111111'$$, '42501', null, 'KTO-01 Sicherheit: Nutzer kann terms_accepted_at nicht ändern'); select is((select terms_version from public.profiles where id = '11111111-1111-1111-1111-111111111111'), '2026-11-draft', 'KTO-01 Sicherheit: Nutzer kann seine terms_version lesen'); select is((select count(*) from public.profiles), 1::bigint, 'KTO-01 Sicherheit: Nutzer sieht nur das eigene Profil'); reset role; select set_config('request.jwt.claims', '', true); select is((select raw_user_meta_data ->> 'locale' from auth.users where id = '11111111-1111-1111-1111-111111111111'), 'en', 'KTO-01 Kriterium 11: Profilsprache wird in die Mail-Sprache übernommen'); select is((select raw_user_meta_data ->> 'display_name' from auth.users where id = '11111111-1111-1111-1111-111111111111'), ' Anna ', 'KTO-01 Kriterium 11: übrige Metadaten bleiben beim Sync erhalten'); select is((select raw_user_meta_data ->> 'locale' from auth.users where id = '22222222-2222-2222-2222-222222222222'), null, 'KTO-01 Kriterium 11: Metadaten fremder Konten bleiben unverändert'); select * from finish(); rollback;