Adds display name validation, terms_version, locale sync trigger and pgTAP tests (KTO-01 criteria 1, 2, 10, 11). Includes GoTrue spike results. Refs #1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
183 lines
13 KiB
PL/PgSQL
183 lines
13 KiB
PL/PgSQL
-- 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;
|