Shuffle/supabase/tests/database/04_plays.test.sql
cschlaefke 395816609f feat(db): enforce registration rules and add terms_version
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>
2026-11-02 22:36:21 +01:00

174 lines
12 KiB
PL/PgSQL

-- Partien, Mitspieler, Wertung, Statistik, Profil-Sicht und Konto löschen.
begin;
select plan(39);
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":"test","display_name":"Anna"}'),
('22222222-2222-2222-2222-222222222222', 'ben@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"Ben"}'),
('33333333-3333-3333-3333-333333333333', 'cara@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"Cara"}'),
('44444444-4444-4444-4444-444444444444', 'dan@example.com', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"Dan"}');
update public.profiles set city = 'Hannover' where id = '11111111-1111-1111-1111-111111111111';
update public.profiles set city = 'Bielefeld', city_visible = false where id = '22222222-2222-2222-2222-222222222222';
insert into public.games (id, title, visibility) values
('a0000000-0000-0000-0000-000000000001', 'Catan', 'catalog');
insert into public.games (id, title, visibility, owner_id) values
('a0000000-0000-0000-0000-000000000002', 'Annas Prototyp', 'private', '11111111-1111-1111-1111-111111111111');
insert into public.score_sheets (id, owner_id, name) values
('d0000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'Catan Standard'),
('d0000000-0000-0000-0000-000000000002', '33333333-3333-3333-3333-333333333333', 'Caras Bogen');
insert into public.score_categories (id, sheet_id, name, position) values
('e0000000-0000-0000-0000-000000000001', 'd0000000-0000-0000-0000-000000000001', 'Siedlungen', 0),
('e0000000-0000-0000-0000-000000000002', 'd0000000-0000-0000-0000-000000000001', 'Straßen', 1);
-- ===== Anna legt Partien an =====
select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true);
set local role authenticated;
select lives_ok(
$$insert into public.plays (id, owner_id, game_id, sheet_id, played_at, duration_minutes, client_id) values
('f0000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001',
'd0000000-0000-0000-0000-000000000001', '2026-10-01 19:00+00', 90, '90000000-0000-0000-0000-000000000001')$$,
'Anna darf eine Partie anlegen');
select throws_ok(
$$insert into public.plays (owner_id, game_id, played_at, client_id) values
('11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001', '2026-10-01 19:00+00', '90000000-0000-0000-0000-000000000001')$$,
'23505', null, 'Dieselbe client_id wird nicht doppelt angelegt (Offline-Abgleich)');
select throws_ok(
$$insert into public.plays (owner_id, game_id, played_at, client_id) values
('22222222-2222-2222-2222-222222222222', 'a0000000-0000-0000-0000-000000000001', '2026-10-01 19:00+00', '90000000-0000-0000-0000-000000000009')$$,
'42501', null, 'Anna kann keine Partien für andere anlegen');
select throws_ok(
$$insert into public.plays (owner_id, game_id, sheet_id, played_at, client_id) values
('11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001', 'd0000000-0000-0000-0000-000000000002',
'2026-10-01 19:00+00', '90000000-0000-0000-0000-000000000008')$$,
'42501', null, 'Anna kann keinen fremden Wertungsbogen verwenden');
select throws_ok(
$$insert into public.plays (owner_id, game_id, played_at, note, client_id) values
('11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001', '2026-10-01 19:00+00', repeat('x', 1001), '90000000-0000-0000-0000-000000000007')$$,
'23514', null, 'Notiz ist auf 1000 Zeichen begrenzt');
-- Mitspieler: sie selbst, ein Gast, Ben (Einladung), Cara (Einladung)
select lives_ok(
$$insert into public.play_players (id, play_id, profile_id, link_status, place, total_score) values
('f1000000-0000-0000-0000-000000000001', 'f0000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'confirmed', 1, 10)$$,
'Anna trägt sich selbst als Mitspielerin ein');
select lives_ok(
$$insert into public.play_players (play_id, guest_name, link_status, place, total_score) values
('f0000000-0000-0000-0000-000000000001', 'Opa', 'none', 2, 8)$$,
'Anna darf einen Gast eintragen');
select lives_ok(
$$insert into public.play_players (id, play_id, profile_id, link_status, place, total_score) values
('f1000000-0000-0000-0000-000000000003', 'f0000000-0000-0000-0000-000000000001', '22222222-2222-2222-2222-222222222222', 'pending', 3, 6)$$,
'Anna darf Ben einladen (offen)');
select throws_ok(
$$insert into public.play_players (play_id, profile_id, link_status) values
('f0000000-0000-0000-0000-000000000001', '33333333-3333-3333-3333-333333333333', 'confirmed')$$,
'42501', null, 'Anna kann Cara nicht selbst als bestätigt eintragen');
select throws_ok(
$$insert into public.play_players (play_id, guest_name, link_status) values
('f0000000-0000-0000-0000-000000000001', 'Bella', 'confirmed')$$,
'42501', null, 'Ein Gast kann nicht als bestätigt eingetragen werden');
select throws_ok(
$$update public.play_players set link_status = 'confirmed' where id = 'f1000000-0000-0000-0000-000000000003'$$,
'P0001', 'link_change_not_allowed', 'Anna kann Bens Einladung nicht selbst bestätigen');
select lives_ok(
$$update public.play_players set place = 2, total_score = 9 where id = 'f1000000-0000-0000-0000-000000000003'$$,
'Anna darf Platz und Punkte ändern');
-- Wertungsbogen und Werte
select lives_ok(
$$insert into public.play_scores (play_player_id, category_id, value) values
('f1000000-0000-0000-0000-000000000001', 'e0000000-0000-0000-0000-000000000001', 6),
('f1000000-0000-0000-0000-000000000001', 'e0000000-0000-0000-0000-000000000002', 4)$$,
'Anna darf Einzelwerte erfassen');
select throws_ok(
$$insert into public.play_scores (play_player_id, category_id, value) values
('f1000000-0000-0000-0000-000000000001', 'e0000000-0000-0000-0000-000000000001', 7)$$,
'23505', null, 'Je Mitspieler und Kategorie gibt es einen Wert');
-- Limit von 20 Mitspielern (3 vorhanden, 17 weitere sind erlaubt)
select lives_ok(
$$insert into public.play_players (play_id, guest_name) select 'f0000000-0000-0000-0000-000000000001', 'Gast ' || g from generate_series(1, 17) g$$,
'Bis 20 Mitspieler sind erlaubt');
select throws_ok(
$$insert into public.play_players (play_id, guest_name) values ('f0000000-0000-0000-0000-000000000001', 'Zu viel')$$,
'P0001', 'play_players_limit_exceeded', 'Der 21. Mitspieler wird abgelehnt');
reset role;
-- ===== Ben (eingeladen, noch offen) =====
select set_config('request.jwt.claims', '{"sub":"22222222-2222-2222-2222-222222222222","role":"authenticated"}', true);
set local role authenticated;
select is((select count(*) from public.plays), 1::bigint, 'Ben sieht die Partie, zu der er eingeladen ist');
select is((select count(*) from public.my_game_stats), 0::bigint, 'Offene Einladungen zählen nicht in die Statistik');
select is((select count(*) from public.public_profiles), 2::bigint, 'Ben sieht sich und die Einladende');
select is((select city from public.public_profiles where id = '11111111-1111-1111-1111-111111111111'),
'Hannover', 'Stadt ist sichtbar, wenn freigegeben');
select throws_ok(
$$update public.play_players set place = 1 where id = 'f1000000-0000-0000-0000-000000000003'$$,
'P0001', 'invitation_answer_only', 'Ben darf nur die Einladung beantworten, nichts sonst ändern');
select is_empty($$update public.play_players set link_status = 'confirmed' where id = 'f1000000-0000-0000-0000-000000000001' returning 1$$,
'Ben kann nicht für andere bestätigen');
select throws_ok(
$$insert into public.play_scores (play_player_id, category_id, value) values
('f1000000-0000-0000-0000-000000000003', 'e0000000-0000-0000-0000-000000000001', 5)$$,
'42501', null, 'Ben kann keine Werte erfassen');
select is_empty($$update public.plays set note = 'Fremd' returning 1$$, 'Ben kann die Partie nicht ändern');
select lives_ok(
$$update public.play_players set link_status = 'confirmed' where id = 'f1000000-0000-0000-0000-000000000003'$$,
'Ben darf seine Einladung bestätigen');
select is((select count(*) from public.play_scores), 2::bigint, 'Bestätigte Mitspieler sehen die Werte');
select is((select count(*) from public.score_sheets), 0::bigint, 'Ben sieht Annas Wertungsbogen nicht');
select is((select play_count from public.my_game_stats), 1::bigint, 'Bestätigte Partien zählen in die Statistik');
reset role;
-- ===== Cara (eingeladen, lehnt ab) und Dan (Fremder) =====
-- Aufbau als Superuser: keine Nutzer-Claims aktiv
select set_config('request.jwt.claims', '', true);
update public.play_players
set profile_id = '33333333-3333-3333-3333-333333333333', guest_name = null, link_status = 'pending'
where play_id = 'f0000000-0000-0000-0000-000000000001' and guest_name = 'Gast 1';
select set_config('request.jwt.claims', '{"sub":"33333333-3333-3333-3333-333333333333","role":"authenticated"}', true);
set local role authenticated;
select lives_ok(
$$update public.play_players set link_status = 'declined' where profile_id = '33333333-3333-3333-3333-333333333333'$$,
'Cara darf die Einladung ablehnen');
select is((select count(*) from public.plays), 0::bigint, 'Wer abgelehnt hat, sieht die Partie nicht mehr');
select is((select count(*) from public.play_players), 1::bigint, 'Cara sieht nur ihre eigene Zeile');
reset role;
select set_config('request.jwt.claims', '{"sub":"44444444-4444-4444-4444-444444444444","role":"authenticated"}', true);
set local role authenticated;
select is((select count(*) from public.plays), 0::bigint, 'Fremde sehen keine Partien');
select is((select count(*) from public.play_players), 0::bigint, 'Fremde sehen keine Mitspieler');
select is((select count(*) from public.play_scores), 0::bigint, 'Fremde sehen keine Werte');
select is((select count(*) from public.public_profiles), 1::bigint, 'Fremde sehen nur sich selbst, es gibt keine Nutzersuche');
reset role;
-- ===== Statistik: Siegquote zählt nur Partien mit Ergebnis =====
select set_config('request.jwt.claims', '', true);
insert into public.plays (id, owner_id, game_id, played_at, duration_minutes, client_id) values
('f0000000-0000-0000-0000-000000000002', '11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001', '2026-10-02', 60, '90000000-0000-0000-0000-000000000002'),
('f0000000-0000-0000-0000-000000000003', '11111111-1111-1111-1111-111111111111', 'a0000000-0000-0000-0000-000000000001', '2026-10-03', 30, '90000000-0000-0000-0000-000000000003');
insert into public.play_players (play_id, profile_id, link_status, place) values
('f0000000-0000-0000-0000-000000000002', '11111111-1111-1111-1111-111111111111', 'confirmed', 2),
('f0000000-0000-0000-0000-000000000003', '11111111-1111-1111-1111-111111111111', 'confirmed', null);
select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true);
set local role authenticated;
select results_eq(
$$select play_count, total_minutes, wins, plays_with_result, win_rate from public.my_game_stats where game_id = 'a0000000-0000-0000-0000-000000000001'$$,
$$values (3::bigint, 180::bigint, 1::bigint, 2::bigint, 0.5000::numeric)$$,
'Statistik: 3 Partien, 180 Minuten, 1 Sieg bei 2 Partien mit Ergebnis, Siegquote 0,5');
reset role;
-- ===== Konto löschen: Partien bleiben für andere, ohne Beteiligte verschwinden sie =====
select set_config('request.jwt.claims', '', true);
delete from auth.users where id = '11111111-1111-1111-1111-111111111111';
select is((select count(*) from public.plays), 1::bigint, 'Nach dem Löschen bleibt nur die Partie mit einem anderen bestätigten Mitspieler');
select is((select owner_id is null from public.plays), true, 'Die Partie hat keinen Ersteller mehr');
select is((select count(*) from public.play_players where profile_id is null and guest_name is null and link_status = 'none'),
1::bigint, 'Der gelöschte Nutzer erscheint anonym');
select * from finish();
rollback;