747 lines
65 KiB
PL/PgSQL
747 lines
65 KiB
PL/PgSQL
-- KAT-05 (PR 2c): Meldungen. Tabelle game_reports (RLS, Checks, Indizes, Fremdschlüssel),
|
|
-- Trigger games_visibility_immutable, admin_resolve_report, admin_merge_games nach dem
|
|
-- create or replace (Rechte, Schritt 4), Konto-Löschen nach Freigabe, Fremdschlüssel-Wächter und die
|
|
-- SQL-Bausteine aus docs/katalog-pflege.md, Abschnitt 6 (unverändert, ohne begin/commit; Auswahl per id).
|
|
-- Läuft auch mit eingespieltem Startbestand: eigene Fixtures mit festen IDs (Präfix 7c1d/f8), gezählt wird nur darüber.
|
|
-- Regel dieser Datei: Jede Verweigerung steht neben einem Positivfall, sonst würde "alles verweigern" bestehen.
|
|
begin;
|
|
select plan(153);
|
|
|
|
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"}');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Tabellenrechte (KAT-05 Kriterium 9)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select ok((select relrowsecurity from pg_class where oid = 'public.game_reports'::regclass),
|
|
'Sicherheit Kriterium 1: game_reports hat Row Level Security');
|
|
select ok(has_table_privilege('authenticated', 'public.game_reports', 'select'),
|
|
'KAT-05 Kriterium 5: authenticated darf game_reports lesen (eigene Zeilen, Policy)');
|
|
-- Formuliert stabil gegenüber späteren Spaltenrechten (#13 ergänzt Insert-Recht und update (result_seen_at)):
|
|
-- geprüft wird je Spalte, nicht "kein Insert/Update auf der Tabelle".
|
|
select is_empty($$select c, p from unnest(array['status', 'target_game_id', 'resolved_at', 'origin', 'owner_id', 'bgg_id', 'created_at']) c,
|
|
unnest(array['insert', 'update']) p
|
|
where has_column_privilege('authenticated', 'public.game_reports', c, p)$$,
|
|
'KAT-05 Kriterium 9: authenticated hat kein Schreibrecht auf status, target_game_id, resolved_at, origin, owner_id, bgg_id, created_at');
|
|
select is_empty($$select c, p from (select attname as c from pg_attribute where attrelid = 'public.game_reports'::regclass and attnum > 0 and not attisdropped) a,
|
|
unnest(array['insert', 'update']) p
|
|
where has_column_privilege('anon', 'public.game_reports', c, p)$$,
|
|
'KAT-05 Kriterium 9: anon hat auf keine Spalte von game_reports ein Schreibrecht');
|
|
select is_empty($$select r, p from unnest(array['anon', 'authenticated']) r, unnest(array['delete', 'truncate', 'references', 'trigger']) p
|
|
where has_table_privilege(r, 'public.game_reports', p)$$,
|
|
'KAT-05 Kriterium 9: kein Löschen, Leeren oder Trigger-Recht für anon und authenticated');
|
|
select is_empty($$select 1 from pg_policies where tablename = 'game_reports' and cmd in ('DELETE', 'ALL')$$,
|
|
'KAT-05 Kriterium 9: game_reports hat keine Delete- oder All-Policy');
|
|
select ok(not has_table_privilege('anon', 'public.game_reports', 'select'),
|
|
'Sicherheit Kriterium 3: anon darf game_reports nicht einmal lesen');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Fixtures (als postgres)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, visibility) values
|
|
('7c1d2c3d-0000-4000-8000-000000000004', 'Bestehendes Spiel', 'catalog'),
|
|
('f8000000-0000-0000-0000-00000000000c', 'MELDUNGSSPIEL', 'catalog'),
|
|
('f8000000-0000-0000-0000-00000000000e', 'Unbeteiligt', 'catalog');
|
|
update public.games set year = 2023 where id = 'f8000000-0000-0000-0000-00000000000c';
|
|
insert into public.games (id, title, year, bgg_id, visibility) values
|
|
('f8000000-0000-0000-0000-00000000000d', 'Anderer Titel', 2020, 4711, 'catalog');
|
|
insert into public.game_variants (id, game_id, name) values
|
|
('f8000000-0000-0000-0000-0000000000a4', '7c1d2c3d-0000-4000-8000-000000000004', 'Standard');
|
|
|
|
-- vorläufige private Einträge: P1 (Anna, freigeben), P2 (Anna, zusammenführen), P3 und P4 (Cara, ablehnen)
|
|
insert into public.games (id, title, year, description, image_path, visibility, owner_id) values
|
|
('7c1d2c3d-0000-4000-8000-000000000003', 'Meldungsspiel', 2024, 'Annas privater Text', 'private/anna.png', 'private', '11111111-1111-1111-1111-111111111111'),
|
|
('7c1d2c3d-0000-4000-8000-000000000008', 'Bestehendes Spiel', 2019, null, null, 'private', '11111111-1111-1111-1111-111111111111'),
|
|
('7c1d2c3d-0000-4000-8000-000000000009', 'Caras Prototyp', 2025, null, null, 'private', '33333333-3333-3333-3333-333333333333'),
|
|
('7c1d2c3d-0000-4000-8000-00000000000a', 'Caras Wunsch', 2025, null, null, 'private', '33333333-3333-3333-3333-333333333333');
|
|
|
|
insert into public.game_reports (id, owner_id, title, publisher_name, year, link, bgg_id, provisional_game_id, created_at) values
|
|
('7c1d2c3d-0000-4000-8000-000000000001', '11111111-1111-1111-1111-111111111111', 'Meldungsspiel', 'Beispielverlag', 2024, 'https://example.org/spiel', 4711, '7c1d2c3d-0000-4000-8000-000000000003', '2026-01-01'),
|
|
('7c1d2c3d-0000-4000-8000-000000000005', '11111111-1111-1111-1111-111111111111', 'Bestehendes Spiel', null, null, null, null, '7c1d2c3d-0000-4000-8000-000000000008', '2026-01-03'),
|
|
('7c1d2c3d-0000-4000-8000-000000000006', '33333333-3333-3333-3333-333333333333', 'Caras Prototyp', null, null, null, null, '7c1d2c3d-0000-4000-8000-000000000009', '2026-01-02'),
|
|
('7c1d2c3d-0000-4000-8000-000000000007', '33333333-3333-3333-3333-333333333333', 'Caras Wunsch', null, null, null, null, '7c1d2c3d-0000-4000-8000-00000000000a', '2026-01-04'),
|
|
('7c1d2c3d-0000-4000-8000-00000000000b', '22222222-2222-2222-2222-222222222222', 'Bens Meldung ohne Eintrag', null, null, null, null, null, '2026-01-05');
|
|
|
|
-- Verweise an den vorläufigen Einträgen
|
|
insert into public.game_variants (id, game_id, name) values
|
|
('f8000000-0000-0000-0000-0000000000e1', '7c1d2c3d-0000-4000-8000-000000000003', 'Deluxe'),
|
|
('f8000000-0000-0000-0000-0000000000e2', '7c1d2c3d-0000-4000-8000-000000000003', 'Prototyp'),
|
|
('f8000000-0000-0000-0000-0000000000e3', '7c1d2c3d-0000-4000-8000-000000000008', ' standard');
|
|
insert into public.collection_items (id, owner_id, game_id, variant_id, list, note) values
|
|
('f8000000-0000-0000-0000-0000000000c1', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000003', 'f8000000-0000-0000-0000-0000000000e2', 'owned', 'Annas Notiz'),
|
|
('f8000000-0000-0000-0000-0000000000c2', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000008', 'f8000000-0000-0000-0000-0000000000e3', 'wishlist', 'Annas Wunsch'),
|
|
('f8000000-0000-0000-0000-0000000000c3', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000004', null, 'owned', null),
|
|
('f8000000-0000-0000-0000-0000000000c4', '22222222-2222-2222-2222-222222222222', '7c1d2c3d-0000-4000-8000-000000000004', 'f8000000-0000-0000-0000-0000000000a4', 'owned', 'Bens Notiz'),
|
|
('f8000000-0000-0000-0000-0000000000c5', '33333333-3333-3333-3333-333333333333', '7c1d2c3d-0000-4000-8000-000000000009', null, 'owned', 'Caras Notiz'),
|
|
('f8000000-0000-0000-0000-0000000000c6', '33333333-3333-3333-3333-333333333333', '7c1d2c3d-0000-4000-8000-00000000000a', null, 'wishlist', 'Caras Wunschnotiz');
|
|
insert into public.favorite_games (id, profile_id, game_id, position) values
|
|
('f8000000-0000-0000-0000-0000000000d1', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000003', 1);
|
|
insert into public.plays (id, owner_id, game_id, variant_id, played_at, client_id) values
|
|
('f8000000-0000-0000-0000-0000000000b1', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000003', 'f8000000-0000-0000-0000-0000000000e1', '2026-01-01', 'f8000000-0000-0000-0000-0000000000b1'),
|
|
('f8000000-0000-0000-0000-0000000000b2', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000003', null, '2026-01-02', 'f8000000-0000-0000-0000-0000000000b2'),
|
|
('f8000000-0000-0000-0000-0000000000b3', '22222222-2222-2222-2222-222222222222', '7c1d2c3d-0000-4000-8000-000000000004', 'f8000000-0000-0000-0000-0000000000a4', '2026-01-03', 'f8000000-0000-0000-0000-0000000000b3'),
|
|
('f8000000-0000-0000-0000-0000000000b4', '33333333-3333-3333-3333-333333333333', '7c1d2c3d-0000-4000-8000-000000000009', null, '2026-01-04', 'f8000000-0000-0000-0000-0000000000b4');
|
|
insert into public.score_sheets (id, owner_id, game_id, name) values
|
|
('f8000000-0000-0000-0000-0000000000b5', '11111111-1111-1111-1111-111111111111', '7c1d2c3d-0000-4000-8000-000000000003', 'Bogen');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Lesen, RLS (Sicherheit Kriterium 1 und 3, KAT-05 Kriterium 5 und 9)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select cmp_ok((select count(*) from public.game_reports where owner_id <> '11111111-1111-1111-1111-111111111111'), '>', 0::bigint,
|
|
'Vorbedingung: es gibt Meldungen anderer Nutzer');
|
|
|
|
select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select is((select count(*) from public.game_reports where owner_id = '11111111-1111-1111-1111-111111111111'), 2::bigint,
|
|
'Sicherheit Kriterium 3: Anna liest ihre beiden eigenen Meldungen');
|
|
select is((select count(*) from public.game_reports where owner_id <> '11111111-1111-1111-1111-111111111111'), 0::bigint,
|
|
'Sicherheit Kriterium 3: Anna liest keine fremden Meldungen');
|
|
select is_empty($$select 1 from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000006'$$,
|
|
'Sicherheit Kriterium 3: Anna findet Caras Meldung auch per ID nicht');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values ('22222222-2222-2222-2222-222222222222', 'Im Namen von Ben')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: Anna kann keine Meldung für einen anderen Nutzer anlegen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at, target_game_id)
|
|
values ('11111111-1111-1111-1111-111111111111', 'Selbst freigegeben', 'approved', now(), '7c1d2c3d-0000-4000-8000-000000000004')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: Anna kann keine bereits freigegebene Meldung anlegen');
|
|
select throws_ok($$update public.game_reports set target_game_id = '7c1d2c3d-0000-4000-8000-000000000004' where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: Anna kann das Ziel ihrer Meldung nicht setzen');
|
|
select throws_ok($$update public.game_reports set status = 'approved' where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: Anna kann ihre Meldung nicht selbst freigeben');
|
|
select throws_ok($$delete from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: Anna kann ihre Meldung nicht löschen');
|
|
reset role;
|
|
|
|
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.game_reports where owner_id = '22222222-2222-2222-2222-222222222222'), 1::bigint,
|
|
'Sicherheit Kriterium 3: Ben liest seine eigene Meldung');
|
|
select is((select count(*) from public.game_reports where owner_id <> '22222222-2222-2222-2222-222222222222'), 0::bigint,
|
|
'Sicherheit Kriterium 3: Ben liest Annas und Caras Meldungen nicht');
|
|
reset role;
|
|
|
|
-- Gefälschte Rolle im user_metadata (vom Client änderbar) öffnet nichts
|
|
select set_config('request.jwt.claims',
|
|
'{"sub":"22222222-2222-2222-2222-222222222222","role":"authenticated","user_metadata":{"role":"admin"}}', true);
|
|
set local role authenticated;
|
|
select is((select count(*) from public.game_reports where owner_id <> '22222222-2222-2222-2222-222222222222'), 0::bigint,
|
|
'Sicherheit Kriterium 3: user_metadata.role = admin öffnet keine fremden Meldungen');
|
|
reset role;
|
|
|
|
-- Die Admin-Policy (R4) ist vorbereitet: nur app_metadata (serverseitig gesetzt) öffnet das Lesen, schreiben kann auch der Admin nicht
|
|
select set_config('request.jwt.claims',
|
|
'{"sub":"22222222-2222-2222-2222-222222222222","role":"authenticated","app_metadata":{"role":"admin"}}', true);
|
|
set local role authenticated;
|
|
select cmp_ok((select count(*) from public.game_reports where owner_id <> '22222222-2222-2222-2222-222222222222'), '>=', 4::bigint,
|
|
'Admin-Policy für R4: app_metadata.role = admin liest alle Meldungen');
|
|
select throws_ok($$update public.game_reports set status = 'rejected', resolved_at = now() where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: auch mit app_metadata.role = admin kein Ändern des Status über die Schnittstelle');
|
|
select throws_ok($$delete from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: auch mit app_metadata.role = admin kein Löschen über die Schnittstelle');
|
|
reset role;
|
|
|
|
set local role anon;
|
|
select throws_ok($$select count(*) from public.game_reports$$, '42501', null,
|
|
'Sicherheit Kriterium 3: anon liest keine Meldungen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values ('11111111-1111-1111-1111-111111111111', 'Neu')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: anon kann keine Meldung anlegen');
|
|
select throws_ok($$update public.game_reports set status = 'rejected', resolved_at = now() where id = '7c1d2c3d-0000-4000-8000-000000000001'$$, '42501', null,
|
|
'KAT-05 Kriterium 9: anon kann keine Meldung ändern');
|
|
reset role;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Checks (jeweils mit dem Namen der verletzten Regel, damit nicht ein anderer Fehler besteht)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select lives_ok($$insert into public.game_reports (id, owner_id, title, link, year, publisher_name) values
|
|
('f8000000-0000-0000-0000-000000000a01', '22222222-2222-2222-2222-222222222222', 'Gültig https', 'https://example.org/x', 1800, repeat('v', 200)),
|
|
('f8000000-0000-0000-0000-000000000a02', '22222222-2222-2222-2222-222222222222', repeat('t', 200), 'http://example.org/x', 2100, null)$$,
|
|
'Gültige Meldungen (https/http, Jahresgrenzen, Längengrenzen) lassen sich anlegen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, origin) values ('22222222-2222-2222-2222-222222222222', 'X', 'bot')$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_origin_check"', 'Check: origin nur user/import');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values ('22222222-2222-2222-2222-222222222222', '')$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_title_check"', 'Check: Titel nicht leer');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values ('22222222-2222-2222-2222-222222222222', repeat('t', 201))$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_title_check"', 'Check: Titel höchstens 200 Zeichen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, publisher_name) values ('22222222-2222-2222-2222-222222222222', 'X', repeat('v', 201))$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_publisher_name_check"', 'Check: Verlag höchstens 200 Zeichen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, year) values ('22222222-2222-2222-2222-222222222222', 'X', 1799)$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_year_check"', 'Check: Jahr mindestens 1800');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, year) values ('22222222-2222-2222-2222-222222222222', 'X', 2101)$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_year_check"', 'Check: Jahr höchstens 2100');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, link) values ('22222222-2222-2222-2222-222222222222', 'X', 'ftp://example.org')$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_link_check"', 'Check: Link nur http(s)');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, link) values ('22222222-2222-2222-2222-222222222222', 'X', 'https://' || repeat('a', 493))$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_link_check"', 'Check: Link höchstens 500 Zeichen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at) values ('22222222-2222-2222-2222-222222222222', 'X', 'done', now())$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_status_check"', 'Check: Status nur open/approved/rejected/merged');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at) values ('22222222-2222-2222-2222-222222222222', 'X', 'open', now())$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_resolved_matches_status"', 'Check: offene Meldung hat kein resolved_at');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status) values ('22222222-2222-2222-2222-222222222222', 'X', 'rejected')$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_resolved_matches_status"', 'Check: erledigte Meldung braucht resolved_at');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, target_game_id) values ('22222222-2222-2222-2222-222222222222', 'X', '7c1d2c3d-0000-4000-8000-000000000004')$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_open_has_no_target"', 'Check: offene Meldung hat kein Ziel');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at) values ('22222222-2222-2222-2222-222222222222', 'X', 'approved', now())$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_target_required"', 'Check: approved braucht ein Ziel');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at) values ('22222222-2222-2222-2222-222222222222', 'X', 'merged', now())$$,
|
|
'23514', 'new row for relation "game_reports" violates check constraint "game_reports_target_required"', 'Check: merged braucht ein Ziel');
|
|
select lives_ok($$insert into public.game_reports (id, owner_id, title, status, resolved_at, target_game_id) values
|
|
('f8000000-0000-0000-0000-000000000a03', '22222222-2222-2222-2222-222222222222', 'rejected mit Ziel', 'rejected', now(), '7c1d2c3d-0000-4000-8000-000000000004'),
|
|
('f8000000-0000-0000-0000-000000000a04', '22222222-2222-2222-2222-222222222222', 'rejected ohne Ziel', 'rejected', now(), null)$$,
|
|
'Positiv: rejected ist mit und ohne Ziel zulässig');
|
|
select is((select origin || ':' || status from public.game_reports where id = 'f8000000-0000-0000-0000-000000000a01'), 'user:open',
|
|
'Standardwerte: origin = user, status = open');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- N12a: eine offene Import-Meldung je Nutzer und BGG-Kennung
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.game_reports (id, owner_id, title, origin, bgg_id) values
|
|
('f8000000-0000-0000-0000-000000000b01', '22222222-2222-2222-2222-222222222222', 'Import 1', 'import', 9001);
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, origin, bgg_id)
|
|
values ('22222222-2222-2222-2222-222222222222', 'Import 1 doppelt', 'import', 9001)$$, '23505', null,
|
|
'N12a: zweite offene Import-Meldung desselben Nutzers mit derselben BGG-Kennung wird abgelehnt');
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, origin, bgg_id) values
|
|
('33333333-3333-3333-3333-333333333333', 'Import anderer Nutzer', 'import', 9001),
|
|
('22222222-2222-2222-2222-222222222222', 'Import andere Kennung', 'import', 9002),
|
|
('22222222-2222-2222-2222-222222222222', 'Von Hand dieselbe Kennung', 'user', 9001),
|
|
('22222222-2222-2222-2222-222222222222', 'Von Hand dieselbe Kennung 2', 'user', 9001),
|
|
('22222222-2222-2222-2222-222222222222', 'Import ohne Kennung', 'import', null),
|
|
('22222222-2222-2222-2222-222222222222', 'Import ohne Kennung 2', 'import', null)$$,
|
|
'N12a: anderer Nutzer, andere Kennung, origin = user und fehlende Kennung bleiben zulässig');
|
|
insert into public.game_reports (id, owner_id, title, origin, bgg_id, status, resolved_at) values
|
|
('f8000000-0000-0000-0000-000000000b02', '22222222-2222-2222-2222-222222222222', 'Import 1 erledigt', 'import', 9001, 'rejected', now());
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, origin, bgg_id)
|
|
values ('22222222-2222-2222-2222-222222222222', 'Import 1 neu', 'import', 9003)$$,
|
|
'N12a: Vorbedingung für die nächste Prüfung');
|
|
update public.game_reports set status = 'rejected', resolved_at = now() where id = 'f8000000-0000-0000-0000-000000000b01';
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, origin, bgg_id)
|
|
values ('22222222-2222-2222-2222-222222222222', 'Import 1 nach Erledigung', 'import', 9001)$$,
|
|
'N12a: ist die frühere Meldung erledigt, darf eine neue offene für dieselbe Kennung angelegt werden');
|
|
select is((select indexdef from pg_indexes where indexname = 'game_reports_open_idx'),
|
|
'CREATE INDEX game_reports_open_idx ON public.game_reports USING btree (created_at) WHERE (status = ''open''::text)',
|
|
'Index für die Admin-Liste (offene Meldungen nach Alter)');
|
|
select is((select count(*) from pg_indexes where tablename = 'game_reports' and indexname in
|
|
('game_reports_owner_created_idx', 'game_reports_provisional_game_id_key', 'game_reports_target_game_id_idx')), 3::bigint,
|
|
'Indizes für Tageslimit und beide Fremdschlüssel auf games vorhanden');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id)
|
|
values ('11111111-1111-1111-1111-111111111111', 'Zweite Meldung zum selben Eintrag', '7c1d2c3d-0000-4000-8000-000000000003')$$, '23505', null,
|
|
'Ein vorläufiger Eintrag gehört zu höchstens einer Meldung (1:1)');
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, target_game_id, status, resolved_at) values
|
|
('22222222-2222-2222-2222-222222222222', 'Gleiches Ziel 1', '7c1d2c3d-0000-4000-8000-000000000004', 'merged', now()),
|
|
('22222222-2222-2222-2222-222222222222', 'Gleiches Ziel 2', '7c1d2c3d-0000-4000-8000-000000000004', 'merged', now())$$,
|
|
'Mehrere Meldungen dürfen dasselbe Ziel haben, und mehrere dürfen ohne vorläufigen Eintrag sein');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Fremdschlüssel-Regeln
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, visibility, owner_id) values
|
|
('f8000000-0000-0000-0000-000000000c01', 'Wird gelöscht', 'private', '22222222-2222-2222-2222-222222222222');
|
|
insert into public.game_reports (id, owner_id, title, provisional_game_id) values
|
|
('f8000000-0000-0000-0000-000000000c02', '22222222-2222-2222-2222-222222222222', 'Vorläufiger Eintrag verschwindet', 'f8000000-0000-0000-0000-000000000c01');
|
|
delete from public.games where id = 'f8000000-0000-0000-0000-000000000c01';
|
|
select is((select count(*) || ':' || count(provisional_game_id) from public.game_reports where id = 'f8000000-0000-0000-0000-000000000c02'), '1:0',
|
|
'Fremdschlüssel: wird der vorläufige Eintrag gelöscht, bleibt die Meldung und der Verweis wird leer (on delete set null)');
|
|
-- eigenes Ziel, auf das nur eine Meldung verweist (sonst würde ein anderer Verweis die Prüfung vortäuschen)
|
|
insert into public.games (id, title, visibility) values ('f8000000-0000-0000-0000-000000000c03', 'Ziel nur für eine Meldung', 'catalog');
|
|
insert into public.game_reports (id, owner_id, title, status, resolved_at, target_game_id) values
|
|
('f8000000-0000-0000-0000-000000000c04', '22222222-2222-2222-2222-222222222222', 'Verweist auf Ziel', 'rejected', now(), 'f8000000-0000-0000-0000-000000000c03');
|
|
select throws_ok($$delete from public.games where id = 'f8000000-0000-0000-0000-000000000c03'$$, '23503', null,
|
|
'Fremdschlüssel: ein Ziel mit Verweis aus einer Meldung lässt sich nicht löschen (on delete restrict)');
|
|
select is((select target_game_id from public.game_reports where id = 'f8000000-0000-0000-0000-000000000c04'), 'f8000000-0000-0000-0000-000000000c03'::uuid,
|
|
'Der Verweis der Meldung auf das Ziel bleibt bestehen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values (gen_random_uuid(), 'Ohne Konto')$$, '23503', null,
|
|
'Fremdschlüssel: Meldung braucht ein vorhandenes Profil');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Trigger games_visibility_immutable (E14), KAT-05 Kriterium 9
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select is_empty($$select p.proname, r from pg_proc p join pg_namespace n on n.oid = p.pronamespace,
|
|
unnest(array['anon', 'authenticated', 'service_role']) as r
|
|
where n.nspname = 'public' and p.proname in ('games_visibility_immutable', 'admin_resolve_report')
|
|
and has_function_privilege(r, p.oid, 'execute')$$,
|
|
'KAT-05 Kriterium 9: games_visibility_immutable und admin_resolve_report sind für anon, authenticated und service_role nicht ausführbar');
|
|
select is((select count(*) from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
where n.nspname = 'public' and p.proname in ('games_visibility_immutable', 'admin_resolve_report', 'admin_merge_games')
|
|
and not p.prosecdef and p.proconfig = array['search_path=""']), 3::bigint,
|
|
'Alle drei Funktionen sind security invoker mit leerem search_path');
|
|
|
|
-- als postgres: private -> Katalog (mit allem, was der Check games_owner_matches_visibility verlangt)
|
|
select throws_ok($$update public.games set visibility = 'catalog', owner_id = null where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: auch postgres kann einen privaten Eintrag nicht öffentlich schalten');
|
|
select throws_ok($$update public.games set visibility = 'catalog' where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: auch nur visibility allein wird abgelehnt');
|
|
select throws_ok($$update public.games set visibility = 'private', owner_id = '11111111-1111-1111-1111-111111111111' where id = '7c1d2c3d-0000-4000-8000-000000000004'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: auch postgres kann ein Katalogspiel nicht privat schalten');
|
|
select throws_ok($$update public.games set owner_id = '22222222-2222-2222-2222-222222222222' where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: der Eigentümer eines privaten Spiels ist unveränderlich');
|
|
select throws_ok($$update public.games set owner_id = null where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: auch ein leerer Eigentümer wird abgelehnt');
|
|
select is((select visibility || ':' || owner_id::text from public.games where id = '7c1d2c3d-0000-4000-8000-000000000003'),
|
|
'private:11111111-1111-1111-1111-111111111111', 'Nach den Fehlern ist das Spiel unverändert');
|
|
select lives_ok($$update public.games set title = 'Meldungsspiel', visibility = 'private' where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'Positiv: dieselbe Sichtbarkeit nochmals zu setzen ist keine Änderung');
|
|
select lives_ok($$update public.games set year = 2022 where id = '7c1d2c3d-0000-4000-8000-000000000004'$$,
|
|
'Positiv: andere Felder eines Katalogspiels bleiben als postgres änderbar');
|
|
update public.games set year = null where id = '7c1d2c3d-0000-4000-8000-000000000004';
|
|
|
|
-- als Nutzer: der Trigger läuft vor der RLS-Prüfung, deshalb P0001 statt 42501
|
|
select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$update public.games set visibility = 'catalog' where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'P0001', 'visibility_immutable', 'KAT-05 Kriterium 9: Anna kann ihren privaten Eintrag nicht öffentlich schalten (Trigger vor RLS)');
|
|
select lives_ok($$update public.games set title = 'Meldungsspiel (neu)' where id = '7c1d2c3d-0000-4000-8000-000000000003'$$,
|
|
'Positiv: Anna ändert den Titel ihres privaten Eintrags weiterhin');
|
|
update public.games set title = 'Meldungsspiel' where id = '7c1d2c3d-0000-4000-8000-000000000003';
|
|
reset role;
|
|
select is((select visibility from public.games where id = '7c1d2c3d-0000-4000-8000-000000000003'), 'private',
|
|
'Annas Eintrag ist weiterhin privat');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- admin_resolve_report: Rechte (KAT-05 Kriterium 9)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims',
|
|
'{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated","app_metadata":{"role":"admin"}}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'rejected')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: authenticated (auch mit app_metadata.role = admin) kann admin_resolve_report nicht aufrufen');
|
|
reset role;
|
|
set local role anon;
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'rejected')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: anon kann admin_resolve_report nicht aufrufen');
|
|
reset role;
|
|
set local role service_role;
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'rejected')$$, '42501', null,
|
|
'KAT-05 Kriterium 9: service_role kann admin_resolve_report nicht aufrufen');
|
|
reset role;
|
|
select is((select status from public.game_reports where id = '7c1d2c3d-0000-4000-8000-00000000000b'), 'open',
|
|
'Die verweigerten Aufrufe haben nichts verändert');
|
|
|
|
-- Die Rollenprüfung im Funktionskörper: eine Rolle MIT execute-Recht, aber ohne postgres/supabase_admin
|
|
create role report_tester nologin;
|
|
grant report_tester to postgres with set true;
|
|
grant usage on schema public to report_tester;
|
|
grant execute on function public.admin_resolve_report(uuid, text, uuid) to report_tester;
|
|
set local role report_tester;
|
|
do $$
|
|
begin
|
|
perform public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'rejected');
|
|
perform set_config('t.resolve_err', 'kein Fehler', true);
|
|
exception when others then
|
|
perform set_config('t.resolve_err', sqlstate || ':' || sqlerrm, true);
|
|
end
|
|
$$;
|
|
reset role;
|
|
select is(current_setting('t.resolve_err'), 'P0001:admin_only',
|
|
'KAT-05 Kriterium 9: admin_resolve_report prüft die Datenbankrolle (admin_only)');
|
|
select is((select status from public.game_reports where id = '7c1d2c3d-0000-4000-8000-00000000000b'), 'open',
|
|
'admin_only ändert nichts');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- admin_resolve_report: Fehlerfälle (alle ohne Änderung)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, visibility, hidden_at) values
|
|
('f8000000-0000-0000-0000-000000000d01', 'Ziel versteckt', 'catalog', '2020-01-01');
|
|
insert into public.games (id, title, visibility, hidden_at, merged_into_id) values
|
|
('f8000000-0000-0000-0000-000000000d02', 'Ziel zusammengeführt', 'catalog', '2020-01-01', '7c1d2c3d-0000-4000-8000-000000000004');
|
|
insert into public.games (id, title, visibility, owner_id) values
|
|
('f8000000-0000-0000-0000-000000000d03', 'Ziel privat', 'private', '22222222-2222-2222-2222-222222222222');
|
|
create temp table rb_before as
|
|
select (select count(*) from public.game_reports where status <> 'open') as erledigt,
|
|
(select count(*) from public.games where hidden_at is not null) as ausgeblendet,
|
|
(select string_agg(id::text || target_game_id::text || status, ',' order by id) from public.game_reports where id::text like '7c1d2c3d%') as meldungen;
|
|
|
|
select throws_ok($$select public.admin_resolve_report(gen_random_uuid(), 'rejected')$$, 'P0001', 'report_not_found',
|
|
'KAT-05 Kriterium 5: unbekannte Meldung (report_not_found)');
|
|
select throws_ok($$select public.admin_resolve_report(null, 'rejected')$$, 'P0001', 'report_not_found',
|
|
'KAT-05 Kriterium 5: fehlende Meldung (null) ist report_not_found');
|
|
select throws_ok($$select public.admin_resolve_report('f8000000-0000-0000-0000-000000000a04', 'rejected')$$, 'P0001', 'report_not_open',
|
|
'KAT-05 Kriterium 5: bereits abgelehnte Meldung (report_not_open)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'open')$$, 'P0001', 'report_status_invalid',
|
|
'KAT-05 Kriterium 5: Status open ist kein Ergebnis (report_status_invalid)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'fertig')$$, 'P0001', 'report_status_invalid',
|
|
'KAT-05 Kriterium 5: unbekannter Status (report_status_invalid)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', null)$$, 'P0001', 'report_status_invalid',
|
|
'KAT-05 Kriterium 5: fehlender Status (null) ist report_status_invalid');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'fertig', null)$$, 'P0001', 'report_status_invalid',
|
|
'KAT-05 Kriterium 5: ungültiger Status geht vor fehlendem Ziel');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'approved')$$, 'P0001', 'report_target_required',
|
|
'KAT-05 Kriterium 5: approved ohne Ziel (report_target_required)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'merged', null)$$, 'P0001', 'report_target_required',
|
|
'KAT-05 Kriterium 5: merged ohne Ziel (report_target_required)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'approved', 'f8000000-0000-0000-0000-000000000d01')$$,
|
|
'P0001', 'report_target_not_listed', 'KAT-05 Kriterium 5: ausgeblendetes Ziel (report_target_not_listed)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'merged', 'f8000000-0000-0000-0000-000000000d02')$$,
|
|
'P0001', 'report_target_not_listed', 'KAT-05 Kriterium 5: zusammengeführtes Ziel (report_target_not_listed)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'approved', 'f8000000-0000-0000-0000-000000000d03')$$,
|
|
'P0001', 'report_target_not_listed', 'KAT-05 Kriterium 5: privates Ziel (report_target_not_listed)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'approved', 'f8000000-0000-0000-0000-0000000000ff')$$,
|
|
'P0001', 'report_target_not_listed', 'KAT-05 Kriterium 5: unbekanntes Ziel (report_target_not_listed)');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'rejected', 'f8000000-0000-0000-0000-000000000d01')$$,
|
|
'P0001', 'report_target_not_listed', 'KAT-05 Kriterium 5: auch bei rejected wird das Ziel geprüft');
|
|
|
|
-- Datenfehler: vorläufiger Eintrag ist ein Katalogspiel (Zusatzprüfung gegenüber der Spec)
|
|
insert into public.game_reports (id, owner_id, title, provisional_game_id) values
|
|
('f8000000-0000-0000-0000-000000000e01', '22222222-2222-2222-2222-222222222222', 'Vorläufig ist Katalog', 'f8000000-0000-0000-0000-00000000000e');
|
|
select throws_ok($$select public.admin_resolve_report('f8000000-0000-0000-0000-000000000e01', 'merged', '7c1d2c3d-0000-4000-8000-000000000004')$$,
|
|
'P0001', 'report_provisional_not_private', 'Zusatzprüfung: ein Katalogspiel als vorläufiger Eintrag wird nicht umgehängt');
|
|
select is((select status from public.game_reports where id = 'f8000000-0000-0000-0000-000000000e01'), 'open',
|
|
'Nach report_provisional_not_private ist die Meldung weiterhin offen');
|
|
select is((select hidden_at is null and merged_into_id is null from public.games where id = 'f8000000-0000-0000-0000-00000000000e'), true,
|
|
'Das Katalogspiel wurde nicht verändert');
|
|
select is((select row(erledigt, ausgeblendet, meldungen)::text from rb_before),
|
|
(select row((select count(*) from public.game_reports where status <> 'open'),
|
|
(select count(*) from public.games where hidden_at is not null),
|
|
(select string_agg(id::text || target_game_id::text || status, ',' order by id) from public.game_reports where id::text like '7c1d2c3d%'))::text),
|
|
'Nach allen Fehlerfällen ist nichts verändert (Meldungen, Ausblendungen)');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Runbook Abschnitt 6, Schritt 1 und 2 (SQL-Bausteine unverändert)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Vorbereitung für Schritt 2 (ähnliche Spiele): Titel gleich bzw. BGG-Kennung 4711 am Katalogspiel
|
|
-- (Fixtures oben: MELDUNGSSPIEL, Anderer Titel)
|
|
|
|
-- Schritt 1: Offene Meldungen
|
|
create temp table rb_open as
|
|
select r.id, r.created_at, r.origin, r.title, r.publisher_name, r.year, r.link, r.bgg_id, r.provisional_game_id
|
|
from public.game_reports r
|
|
where r.status = 'open'
|
|
order by r.created_at, r.id;
|
|
select results_eq($$select id from rb_open where id::text like '7c1d2c3d%' order by created_at, id$$,
|
|
$$values ('7c1d2c3d-0000-4000-8000-000000000001'::uuid), ('7c1d2c3d-0000-4000-8000-000000000006'), ('7c1d2c3d-0000-4000-8000-000000000005'),
|
|
('7c1d2c3d-0000-4000-8000-000000000007'), ('7c1d2c3d-0000-4000-8000-00000000000b')$$,
|
|
'KAT-05 Kriterium 8: Runbook 6, offene Meldungen (älteste zuerst)');
|
|
select is((select row(origin, title, publisher_name, year, link, bgg_id, provisional_game_id)::text from rb_open where id = '7c1d2c3d-0000-4000-8000-000000000001'),
|
|
'(user,Meldungsspiel,Beispielverlag,2024,https://example.org/spiel,4711,7c1d2c3d-0000-4000-8000-000000000003)',
|
|
'KAT-05 Kriterium 8: Runbook 6, die Spalten der Meldung stehen in der Liste');
|
|
|
|
-- Schritt 2: Ähnliche Spiele im Katalog
|
|
create temp table rb_similar as
|
|
select g.id, g.title, g.year, g.bgg_id
|
|
from public.game_reports r
|
|
join public.catalog_games g
|
|
on lower(btrim(g.title)) = lower(btrim(r.title))
|
|
or (r.bgg_id is not null and g.bgg_id = r.bgg_id)
|
|
where r.id = '7c1d2c3d-0000-4000-8000-000000000001'
|
|
order by g.title, g.id;
|
|
select results_eq($$select id, title, year, bgg_id from rb_similar$$,
|
|
$$values ('f8000000-0000-0000-0000-00000000000d'::uuid, 'Anderer Titel'::text, 2020::smallint, 4711::integer),
|
|
('f8000000-0000-0000-0000-00000000000c', 'MELDUNGSSPIEL', 2023::smallint, null::integer)$$,
|
|
'KAT-05 Kriterium 8: Runbook 6, ähnliche Spiele (gleicher Titel ohne Groß-/Kleinschreibung oder gleiche BGG-Kennung)');
|
|
|
|
-- Schritt 2: Was hängt am vorläufigen Eintrag?
|
|
create temp table rb_counts as
|
|
select g.id, g.title,
|
|
(select count(*) from public.collection_items c where c.game_id = g.id) as sammlung,
|
|
(select count(*) from public.favorite_games f where f.game_id = g.id) as favoriten,
|
|
(select count(*) from public.plays p where p.game_id = g.id) as partien,
|
|
(select count(*) from public.score_sheets s where s.game_id = g.id) as wertungsboegen
|
|
from public.game_reports r
|
|
join public.games g on g.id = r.provisional_game_id
|
|
where r.id = '7c1d2c3d-0000-4000-8000-000000000001';
|
|
select results_eq($$select id, title, sammlung, favoriten, partien, wertungsboegen from rb_counts$$,
|
|
$$values ('7c1d2c3d-0000-4000-8000-000000000003'::uuid, 'Meldungsspiel'::text, 1::bigint, 1::bigint, 2::bigint, 1::bigint)$$,
|
|
'KAT-05 Kriterium 8: Runbook 6, Anzahlen am vorläufigen Eintrag');
|
|
|
|
-- Schritt 2: Editionen des vorläufigen Eintrags
|
|
create temp table rb_editions as
|
|
select v.id, v.name, v.year, v.language
|
|
from public.game_reports r
|
|
join public.game_variants v on v.game_id = r.provisional_game_id
|
|
where r.id = '7c1d2c3d-0000-4000-8000-000000000001'
|
|
order by v.name, v.id;
|
|
select results_eq($$select id, name from rb_editions$$,
|
|
$$values ('f8000000-0000-0000-0000-0000000000e1'::uuid, 'Deluxe'::text), ('f8000000-0000-0000-0000-0000000000e2', 'Prototyp')$$,
|
|
'KAT-05 Kriterium 8: Runbook 6, Namen der Editionen des vorläufigen Eintrags');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Runbook Abschnitt 6, Schritt 3a: Freigeben (neuer Katalogeintrag, nur Fakten aus der Meldung)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, year, description, description_locale, visibility)
|
|
values ('7c1d2c3d-0000-4000-8000-000000000002', 'Meldungsspiel', 2024, 'Eigener Kurztext des Admins.', 'de', 'catalog');
|
|
|
|
insert into public.publishers (name) values ('Beispielverlag') on conflict ((lower(name))) do nothing;
|
|
insert into public.game_publishers (game_id, publisher_id)
|
|
select '7c1d2c3d-0000-4000-8000-000000000002', p.id from public.publishers p where lower(p.name) = lower('Beispielverlag');
|
|
|
|
insert into public.game_variants (game_id, name, year) values ('7c1d2c3d-0000-4000-8000-000000000002', 'Deluxe', 2024);
|
|
|
|
select is((select manual_fields from public.games where id = '7c1d2c3d-0000-4000-8000-000000000002'),
|
|
array['description', 'description_locale', 'publishers', 'title', 'year'],
|
|
'KAT-05 Kriterium 8: Runbook 6, der neue Katalogeintrag vermerkt seine Felder in manual_fields');
|
|
|
|
-- Ben hat das neue Spiel nicht; Bens Daten am vorhandenen Ziel (…04) merken, sie dürfen sich nicht ändern
|
|
create temp table ben_before as
|
|
select c.id, c.game_id, c.variant_id, c.list, c.note from public.collection_items c where c.owner_id = '22222222-2222-2222-2222-222222222222'
|
|
union all
|
|
select p.id, p.game_id, p.variant_id, 'play', null from public.plays p where p.owner_id = '22222222-2222-2222-2222-222222222222';
|
|
|
|
create temp table rb_approve as
|
|
select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-000000000001', 'approved', '7c1d2c3d-0000-4000-8000-000000000002') as r;
|
|
select is((select r - 'report_id' from rb_approve),
|
|
jsonb_build_object(
|
|
'status', 'approved', 'target_id', '7c1d2c3d-0000-4000-8000-000000000002',
|
|
'provisional_game_id', '7c1d2c3d-0000-4000-8000-000000000003', 'repointed', true,
|
|
'collection_items', 1, 'collection_conflicts', 0, 'favorite_games', 1, 'favorite_conflicts', 0,
|
|
'plays', 2, 'score_sheets', 1, 'variants_copied', 0, 'variants_mapped', 1, 'variants_cleared', 1),
|
|
'KAT-05 Kriterium 5: Runbook 6, approved: Ergebnis-JSON zählt alles richtig (nichts kopiert, eine Edition abgebildet, eine geleert)');
|
|
select is((select r ->> 'report_id' from rb_approve), '7c1d2c3d-0000-4000-8000-000000000001', 'Das Ergebnis nennt die Meldung');
|
|
|
|
-- Schritt 4: Nachprüfen
|
|
create temp table rb_check as
|
|
select r.id, r.status, r.target_game_id, r.resolved_at is not null as erledigt,
|
|
g.hidden_at is not null as vorlaeufig_ausgeblendet, g.merged_into_id
|
|
from public.game_reports r
|
|
left join public.games g on g.id = r.provisional_game_id
|
|
where r.id = '7c1d2c3d-0000-4000-8000-000000000001';
|
|
select results_eq($$select id, status, target_game_id, erledigt, vorlaeufig_ausgeblendet, merged_into_id from rb_check$$,
|
|
$$values ('7c1d2c3d-0000-4000-8000-000000000001'::uuid, 'approved'::text, '7c1d2c3d-0000-4000-8000-000000000002'::uuid, true, true, '7c1d2c3d-0000-4000-8000-000000000002'::uuid)$$,
|
|
'KAT-05 Kriterium 8: Runbook 6, Nachprüfen: Meldung freigegeben, vorläufiger Eintrag ausgeblendet und verwiesen');
|
|
|
|
select is((select row(visibility, owner_id)::text from public.games where id = '7c1d2c3d-0000-4000-8000-000000000003'),
|
|
'(private,11111111-1111-1111-1111-111111111111)', 'KAT-05 Kriterium 5: der vorläufige Eintrag bleibt privat und gehört weiter Anna (E14)');
|
|
select is((select row(description, image_path)::text from public.games where id = '7c1d2c3d-0000-4000-8000-000000000002'),
|
|
'("Eigener Kurztext des Admins.",)', 'Beschreibung und Bild des vorläufigen Eintrags werden nie übernommen');
|
|
select is((select row(visibility, owner_id, hidden_at is null, merged_into_id is null)::text from public.games where id = '7c1d2c3d-0000-4000-8000-000000000002'),
|
|
'(catalog,,t,t)', 'Das neue Katalogspiel ist sichtbar und gehört niemandem');
|
|
select is((select count(*) from public.catalog_games where id = '7c1d2c3d-0000-4000-8000-000000000002'), 1::bigint,
|
|
'Das neue Katalogspiel erscheint in catalog_games');
|
|
select is((select count(*) from public.catalog_games where id = '7c1d2c3d-0000-4000-8000-000000000003'), 0::bigint,
|
|
'Der vorläufige Eintrag erscheint nie in catalog_games');
|
|
select is((select resolved_at is not null and updated_at >= created_at from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000001'), true,
|
|
'resolved_at ist gesetzt');
|
|
|
|
-- Verweise
|
|
select is((select row(game_id, variant_id, list, note)::text from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c1'),
|
|
'(7c1d2c3d-0000-4000-8000-000000000002,,owned,"Annas Notiz")',
|
|
'RB1: Sammlungseintrag am neuen Spiel, Edition "Prototyp" ohne Gegenstück ist geleert');
|
|
select is((select row(p.game_id, v.name, v.game_id)::text from public.plays p join public.game_variants v on v.id = p.variant_id
|
|
where p.id = 'f8000000-0000-0000-0000-0000000000b1'),
|
|
'(7c1d2c3d-0000-4000-8000-000000000002,Deluxe,7c1d2c3d-0000-4000-8000-000000000002)',
|
|
'RB1: die Edition "Deluxe" der Partie zeigt auf die vom Admin angelegte gleichnamige Variante am Katalogeintrag');
|
|
select is((select row(game_id, variant_id)::text from public.plays where id = 'f8000000-0000-0000-0000-0000000000b2'),
|
|
'(7c1d2c3d-0000-4000-8000-000000000002,)', 'Partie ohne Edition wechselt auf das neue Spiel');
|
|
select is((select game_id from public.favorite_games where id = 'f8000000-0000-0000-0000-0000000000d1'), '7c1d2c3d-0000-4000-8000-000000000002'::uuid,
|
|
'Favorit wechselt auf das neue Spiel');
|
|
select is((select game_id from public.score_sheets where id = 'f8000000-0000-0000-0000-0000000000b5'), '7c1d2c3d-0000-4000-8000-000000000002'::uuid,
|
|
'Wertungsbogen wechselt auf das neue Spiel');
|
|
select is((select count(*) from public.game_variants where game_id = '7c1d2c3d-0000-4000-8000-000000000002'), 1::bigint,
|
|
'RB1: am Katalogeintrag gibt es nur die vom Admin angelegte Variante, die private wurde nicht kopiert');
|
|
select is((select count(*) from public.game_variants where game_id = '7c1d2c3d-0000-4000-8000-000000000003'), 2::bigint,
|
|
'RB1: die privaten Varianten bleiben am vorläufigen Eintrag');
|
|
select is((select count(*) from public.collection_items where game_id = '7c1d2c3d-0000-4000-8000-000000000003'), 0::bigint,
|
|
'Nichts hängt mehr am vorläufigen Eintrag');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Runbook Abschnitt 6, Schritt 3b: Zusammenführen mit einem vorhandenen Eintrag (Kollision)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
create temp table rb_merged as
|
|
select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-000000000005', 'merged', '7c1d2c3d-0000-4000-8000-000000000004') as r;
|
|
select is((select r - 'report_id' from rb_merged),
|
|
jsonb_build_object(
|
|
'status', 'merged', 'target_id', '7c1d2c3d-0000-4000-8000-000000000004',
|
|
'provisional_game_id', '7c1d2c3d-0000-4000-8000-000000000008', 'repointed', true,
|
|
'collection_items', 0, 'collection_conflicts', 1, 'favorite_games', 0, 'favorite_conflicts', 0,
|
|
'plays', 0, 'score_sheets', 0, 'variants_copied', 0, 'variants_mapped', 1, 'variants_cleared', 0),
|
|
'KAT-05 Kriterium 5: Runbook 6, merged: Kollision (Anna hatte das Ziel schon), gleichnamige Variante abgebildet');
|
|
select is((select row(status, target_game_id)::text from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000005'),
|
|
'(merged,7c1d2c3d-0000-4000-8000-000000000004)', 'merged: Status und Ziel gespeichert');
|
|
select is((select row(hidden_at is not null, merged_into_id)::text from public.games where id = '7c1d2c3d-0000-4000-8000-000000000008'),
|
|
'(t,7c1d2c3d-0000-4000-8000-000000000004)', 'merged: der vorläufige Eintrag ist ausgeblendet und verwiesen');
|
|
select is((select row(list, note, variant_id)::text from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c3'),
|
|
'(owned,"Annas Wunsch",f8000000-0000-0000-0000-0000000000a4)',
|
|
'Spec-F1: Anna: der Zieleintrag gewinnt (owned vor wishlist), die leere Notiz und Edition füllt der vorläufige Eintrag (Edition über die gleichnamige Variante)');
|
|
select is((select count(*) from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c2'), 0::bigint,
|
|
'Spec-F1: der Quell-Eintrag der Kollision ist gelöscht');
|
|
select is((select count(*) from public.game_variants where game_id = '7c1d2c3d-0000-4000-8000-000000000004'), 1::bigint,
|
|
'RB1: das vorhandene Katalogspiel bekommt keine Variante');
|
|
select is((select count(*) from public.game_variants where game_id = '7c1d2c3d-0000-4000-8000-000000000008'), 1::bigint,
|
|
'RB1: die private Variante bleibt am vorläufigen Eintrag');
|
|
select results_eq($$select c.id, c.game_id, c.variant_id, c.list, c.note from public.collection_items c
|
|
where c.owner_id = '22222222-2222-2222-2222-222222222222'
|
|
union all select p.id, p.game_id, p.variant_id, 'play', null from public.plays p where p.owner_id = '22222222-2222-2222-2222-222222222222'$$,
|
|
$$select id, game_id, variant_id, list, note from ben_before$$,
|
|
'Daten anderer Nutzer am Ziel (Bens Sammlung und Partie) sind unverändert');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Runbook Abschnitt 6, Schritt 3c: Ablehnen
|
|
-- ---------------------------------------------------------------------------------------------
|
|
create temp table rb_rej_snapshot as
|
|
select 'c5' as k, game_id::text || coalesce(variant_id::text, '') || list as v from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c5'
|
|
union all select 'c6', game_id::text || coalesce(variant_id::text, '') || list from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c6'
|
|
union all select 'b4', game_id::text from public.plays where id = 'f8000000-0000-0000-0000-0000000000b4'
|
|
union all select 'g9', visibility || coalesce(hidden_at::text, '') || coalesce(merged_into_id::text, '') from public.games where id = '7c1d2c3d-0000-4000-8000-000000000009'
|
|
union all select 'ga', visibility || coalesce(hidden_at::text, '') || coalesce(merged_into_id::text, '') from public.games where id = '7c1d2c3d-0000-4000-8000-00000000000a';
|
|
|
|
create temp table rb_rej1 as
|
|
select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-000000000006', 'rejected') as r;
|
|
create temp table rb_rej2 as
|
|
select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-000000000007', 'rejected', '7c1d2c3d-0000-4000-8000-000000000004') as r;
|
|
select is((select r - 'report_id' from rb_rej1),
|
|
'{"status":"rejected","target_id":null,"provisional_game_id":"7c1d2c3d-0000-4000-8000-000000000009","repointed":false}'::jsonb,
|
|
'KAT-05 Kriterium 5: Runbook 6, rejected ohne Ziel: nichts umgehängt, keine Zähler');
|
|
select is((select r - 'report_id' from rb_rej2),
|
|
'{"status":"rejected","target_id":"7c1d2c3d-0000-4000-8000-000000000004","provisional_game_id":"7c1d2c3d-0000-4000-8000-00000000000a","repointed":false}'::jsonb,
|
|
'KAT-05 Kriterium 5: Runbook 6, rejected mit Ziel: nur der Verweis, nichts umgehängt');
|
|
select is((select row(status, target_game_id, resolved_at is not null)::text from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000006'),
|
|
'(rejected,,t)', 'rejected ohne Ziel: Status und Zeitpunkt gespeichert, kein Ziel');
|
|
select is((select row(status, target_game_id, resolved_at is not null)::text from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000007'),
|
|
'(rejected,7c1d2c3d-0000-4000-8000-000000000004,t)', 'rejected mit Ziel: der Verweis auf den Zieleintrag ist gespeichert (Spec-F3)');
|
|
select results_eq($$select 'c5', game_id::text || coalesce(variant_id::text, '') || list from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c5'
|
|
union all select 'c6', game_id::text || coalesce(variant_id::text, '') || list from public.collection_items where id = 'f8000000-0000-0000-0000-0000000000c6'
|
|
union all select 'b4', game_id::text from public.plays where id = 'f8000000-0000-0000-0000-0000000000b4'
|
|
union all select 'g9', visibility || coalesce(hidden_at::text, '') || coalesce(merged_into_id::text, '') from public.games where id = '7c1d2c3d-0000-4000-8000-000000000009'
|
|
union all select 'ga', visibility || coalesce(hidden_at::text, '') || coalesce(merged_into_id::text, '') from public.games where id = '7c1d2c3d-0000-4000-8000-00000000000a'$$,
|
|
$$select k, v from rb_rej_snapshot$$,
|
|
'KAT-05 Kriterium 5: bei rejected (mit und ohne Ziel) bleiben vorläufige Einträge, Sammlung und Partien unverändert');
|
|
|
|
-- approved ohne vorläufigen Eintrag: nur Status und Ziel
|
|
create temp table rb_noprov as
|
|
select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-00000000000b', 'approved', '7c1d2c3d-0000-4000-8000-000000000004') as r;
|
|
select is((select r - 'report_id' from rb_noprov),
|
|
'{"status":"approved","target_id":"7c1d2c3d-0000-4000-8000-000000000004","provisional_game_id":null,"repointed":false}'::jsonb,
|
|
'approved ohne vorläufigen Eintrag: nichts umzuhängen (repointed = false)');
|
|
select is((select row(status, target_game_id)::text from public.game_reports where id = '7c1d2c3d-0000-4000-8000-00000000000b'),
|
|
'(approved,7c1d2c3d-0000-4000-8000-000000000004)', 'approved ohne vorläufigen Eintrag: Status und Ziel gespeichert');
|
|
select throws_ok($$select public.admin_resolve_report('7c1d2c3d-0000-4000-8000-000000000001', 'rejected')$$, 'P0001', 'report_not_open',
|
|
'KAT-05 Kriterium 5: eine freigegebene Meldung lässt sich nicht erneut bearbeiten');
|
|
|
|
-- Der Nutzer sieht das Ergebnis seiner Meldung (KAT-04 Krit. 4-6), aber keine fremden
|
|
select set_config('request.jwt.claims', '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select is((select status || ':' || target_game_id::text from public.game_reports where id = '7c1d2c3d-0000-4000-8000-000000000001'),
|
|
'approved:7c1d2c3d-0000-4000-8000-000000000002', 'Anna sieht das Ergebnis ihrer Meldung');
|
|
select is((select count(*) from public.game_reports where owner_id <> '11111111-1111-1111-1111-111111111111'), 0::bigint,
|
|
'Anna sieht weiterhin keine fremden Meldungen');
|
|
select cmp_ok((select count(*) from public.games where id = '7c1d2c3d-0000-4000-8000-000000000003'), '=', 1::bigint,
|
|
'Anna sieht ihren ausgeblendeten vorläufigen Eintrag weiterhin');
|
|
reset role;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- admin_merge_games nach dem create or replace (Schritt 4: Meldungen umhängen) und Rechte
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select is_empty($$select p.proname, r from pg_proc p join pg_namespace n on n.oid = p.pronamespace,
|
|
unnest(array['anon', 'authenticated', 'service_role']) as r
|
|
where n.nspname = 'public' and p.proname in ('admin_merge_games', 'repoint_game_references')
|
|
and has_function_privilege(r, p.oid, 'execute')$$,
|
|
'KAT-05 Kriterium 9: nach dem create or replace sind admin_merge_games und repoint_game_references für anon, authenticated und service_role nicht ausführbar');
|
|
select set_config('request.jwt.claims',
|
|
'{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated","app_metadata":{"role":"admin"}}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.admin_merge_games(gen_random_uuid(), gen_random_uuid())$$, '42501', null,
|
|
'KAT-05 Kriterium 9: authenticated (auch mit app_metadata.role = admin) kann admin_merge_games weiterhin nicht aufrufen');
|
|
reset role;
|
|
set local role anon;
|
|
select throws_ok($$select public.admin_merge_games(gen_random_uuid(), gen_random_uuid())$$, '42501', null,
|
|
'KAT-05 Kriterium 9: anon kann admin_merge_games weiterhin nicht aufrufen');
|
|
reset role;
|
|
set local role service_role;
|
|
select throws_ok($$select public.admin_merge_games(gen_random_uuid(), gen_random_uuid())$$, '42501', null,
|
|
'KAT-05 Kriterium 9: service_role kann admin_merge_games weiterhin nicht aufrufen');
|
|
reset role;
|
|
create role merge_tester nologin;
|
|
grant merge_tester to postgres with set true;
|
|
grant usage on schema public to merge_tester;
|
|
grant execute on function public.admin_merge_games(uuid, uuid) to merge_tester;
|
|
set local role merge_tester;
|
|
do $$
|
|
begin
|
|
perform public.admin_merge_games('f8000000-0000-0000-0000-000000000f01', 'f8000000-0000-0000-0000-000000000f02');
|
|
perform set_config('t.merge_err', 'kein Fehler', true);
|
|
exception when others then
|
|
perform set_config('t.merge_err', sqlstate || ':' || sqlerrm, true);
|
|
end
|
|
$$;
|
|
reset role;
|
|
select is(current_setting('t.merge_err'), 'P0001:admin_only',
|
|
'KAT-05 Kriterium 9: admin_merge_games prüft nach dem create or replace weiterhin die Datenbankrolle');
|
|
|
|
-- Schritt 4: Quelle (…f01) -> Ziel (…f02); Meldungen mit Ziel = Quelle ziehen mit
|
|
insert into public.games (id, title, visibility) values
|
|
('f8000000-0000-0000-0000-000000000f01', 'Merge Quelle', 'catalog'),
|
|
('f8000000-0000-0000-0000-000000000f02', 'Merge Ziel', 'catalog'),
|
|
('f8000000-0000-0000-0000-000000000f03', 'Anderes Ziel', 'catalog');
|
|
insert into public.game_reports (id, owner_id, title, status, resolved_at, target_game_id) values
|
|
('f8000000-0000-0000-0000-000000000f11', '22222222-2222-2222-2222-222222222222', 'Freigegeben auf Quelle', 'approved', now(), 'f8000000-0000-0000-0000-000000000f01'),
|
|
('f8000000-0000-0000-0000-000000000f12', '22222222-2222-2222-2222-222222222222', 'Abgelehnt mit Verweis auf Quelle', 'rejected', now(), 'f8000000-0000-0000-0000-000000000f01'),
|
|
('f8000000-0000-0000-0000-000000000f13', '22222222-2222-2222-2222-222222222222', 'Zusammengeführt mit anderem', 'merged', now(), 'f8000000-0000-0000-0000-000000000f03');
|
|
insert into public.game_reports (id, owner_id, title) values
|
|
('f8000000-0000-0000-0000-000000000f14', '22222222-2222-2222-2222-222222222222', 'Offen');
|
|
create temp table rb_mg as select public.admin_merge_games('f8000000-0000-0000-0000-000000000f01', 'f8000000-0000-0000-0000-000000000f02') as r;
|
|
select is((select r -> 'reports_repointed' from rb_mg), '2'::jsonb, 'KAT-05 Kriterium 4: admin_merge_games meldet 2 umgehängte Meldungen');
|
|
select results_eq($$select id, target_game_id from public.game_reports where id::text like 'f8000000-0000-0000-0000-000000000f1%' order by id$$,
|
|
$$values ('f8000000-0000-0000-0000-000000000f11'::uuid, 'f8000000-0000-0000-0000-000000000f02'::uuid),
|
|
('f8000000-0000-0000-0000-000000000f12', 'f8000000-0000-0000-0000-000000000f02'),
|
|
('f8000000-0000-0000-0000-000000000f13', 'f8000000-0000-0000-0000-000000000f03'),
|
|
('f8000000-0000-0000-0000-000000000f14', null)$$,
|
|
'KAT-05 Kriterium 4: Meldungen mit Ziel = Quelle zeigen auf das Ziel, andere Meldungen bleiben unverändert');
|
|
select is((select row(status, resolved_at is not null)::text from public.game_reports where id = 'f8000000-0000-0000-0000-000000000f11'), '(approved,t)',
|
|
'Das Umhängen ändert Status und Zeitpunkt der Meldung nicht');
|
|
select is(public.admin_merge_games('f8000000-0000-0000-0000-000000000f03', 'f8000000-0000-0000-0000-000000000f02') -> 'reports_repointed', '1'::jsonb,
|
|
'Ein weiteres Zusammenführen hängt die nächste Meldung um');
|
|
select lives_ok($$delete from public.games where id = 'f8000000-0000-0000-0000-000000000f01'$$,
|
|
'Positiv: Die Quelle ist nach dem Umhängen nicht mehr Ziel einer Meldung und lässt sich löschen (keine verwaiste Restrict-Sperre)');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Fremdschlüssel-Wächter (wie in 07): jeder Verweis auf games ist behandelt oder bewusst ausgelassen
|
|
-- ---------------------------------------------------------------------------------------------
|
|
create temp table fk_found as
|
|
select (select relname from pg_class where oid = c.conrelid) || '.' ||
|
|
(select attname from pg_attribute where attrelid = c.conrelid and attnum = c.conkey[1]) as fk
|
|
from pg_constraint c
|
|
where c.contype = 'f' and c.confrelid = 'public.games'::regclass;
|
|
select is((select count(*) from pg_constraint c where c.contype = 'f' and c.confrelid = 'public.games'::regclass and cardinality(c.conkey) > 1), 0::bigint,
|
|
'Fremdschlüssel-Wächter: alle Verweise auf games sind einspaltig');
|
|
select set_eq($$select fk from fk_found$$,
|
|
$$values
|
|
-- behandelt von repoint_game_references / admin_merge_games
|
|
('collection_items.game_id'), ('favorite_games.game_id'), ('plays.game_id'), ('score_sheets.game_id'),
|
|
('game_variants.game_id'), ('games.merged_into_id'), ('game_reports.target_game_id'),
|
|
-- bewusst ausgelassen: bleiben am ausgeblendeten Quell-Eintrag
|
|
('game_genres.game_id'), ('game_mechanics.game_id'), ('game_categories.game_id'), ('game_publishers.game_id'),
|
|
-- bewusst ausgelassen: zeigt auf ein privates Spiel, das Zusammenführen zweier Katalogspiele betrifft ihn nicht
|
|
('game_reports.provisional_game_id')$$,
|
|
'KAT-05 Kriterium 4: Fremdschlüssel-Wächter: neue Verweise auf games erzwingen eine Entscheidung im Zusammenführen');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- Konto löschen nach Freigabe (QUE-04, Klärung 10; Spec 9.5 KTO-05)
|
|
-- Anna: freigegebene Meldung (neu angelegter Katalogeintrag), zusammengeführte Meldung, ausgeblendete
|
|
-- vorläufige Einträge mit merged_into_id auf Katalogspiele.
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.collection_items (id, owner_id, game_id, list, note) values
|
|
('f8000000-0000-0000-0000-0000000000c7', '22222222-2222-2222-2222-222222222222', '7c1d2c3d-0000-4000-8000-000000000002', 'owned', 'Bens Notiz am neuen Spiel');
|
|
create temp table other_before as
|
|
select 'game2' as k, row(g.title, g.year, g.description, g.hidden_at, g.merged_into_id, g.visibility, g.manual_fields)::text as v
|
|
from public.games g where g.id = '7c1d2c3d-0000-4000-8000-000000000002'
|
|
union all select 'game4', row(g.title, g.year, g.hidden_at, g.merged_into_id, g.visibility)::text from public.games g where g.id = '7c1d2c3d-0000-4000-8000-000000000004'
|
|
union all select 'ben_c7', row(c.game_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c7'
|
|
union all select 'ben_c4', row(c.game_id, c.variant_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c4'
|
|
union all select 'cara_report', row(r.status, r.target_game_id)::text from public.game_reports r where r.id = '7c1d2c3d-0000-4000-8000-000000000007'
|
|
union all select 'cara_c5', row(c.game_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c5'
|
|
union all select 'variants', (select count(*) from public.game_variants where game_id in ('7c1d2c3d-0000-4000-8000-000000000002', '7c1d2c3d-0000-4000-8000-000000000004'))::text;
|
|
|
|
select cmp_ok((select count(*) from public.game_reports where owner_id = '11111111-1111-1111-1111-111111111111'), '=', 2::bigint,
|
|
'Vorbedingung: Anna hat zwei bearbeitete Meldungen');
|
|
select cmp_ok((select count(*) from public.games where owner_id = '11111111-1111-1111-1111-111111111111' and merged_into_id is not null), '=', 2::bigint,
|
|
'Vorbedingung: Anna hat zwei ausgeblendete vorläufige Einträge mit merged_into_id');
|
|
select lives_ok($$delete from auth.users where id = '11111111-1111-1111-1111-111111111111'$$,
|
|
'KAT-05 Kriterium 5 / QUE-04: das Konto der Meldenden lässt sich nach der Freigabe löschen');
|
|
select is((select count(*) from public.game_reports where owner_id = '11111111-1111-1111-1111-111111111111'), 0::bigint,
|
|
'QUE-04: die Meldungen sind mit dem Konto gelöscht');
|
|
select is((select count(*) from public.games where owner_id = '11111111-1111-1111-1111-111111111111'), 0::bigint,
|
|
'QUE-04: die vorläufigen Einträge sind mit dem Konto gelöscht');
|
|
select is((select count(*) from public.collection_items where owner_id = '11111111-1111-1111-1111-111111111111'), 0::bigint,
|
|
'QUE-04: Annas Sammlung (auch die umgehängten Einträge) ist gelöscht');
|
|
select results_eq($$select 'game2', row(g.title, g.year, g.description, g.hidden_at, g.merged_into_id, g.visibility, g.manual_fields)::text from public.games g where g.id = '7c1d2c3d-0000-4000-8000-000000000002'
|
|
union all select 'game4', row(g.title, g.year, g.hidden_at, g.merged_into_id, g.visibility)::text from public.games g where g.id = '7c1d2c3d-0000-4000-8000-000000000004'
|
|
union all select 'ben_c7', row(c.game_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c7'
|
|
union all select 'ben_c4', row(c.game_id, c.variant_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c4'
|
|
union all select 'cara_report', row(r.status, r.target_game_id)::text from public.game_reports r where r.id = '7c1d2c3d-0000-4000-8000-000000000007'
|
|
union all select 'cara_c5', row(c.game_id, c.list, c.note)::text from public.collection_items c where c.id = 'f8000000-0000-0000-0000-0000000000c5'
|
|
union all select 'variants', (select count(*) from public.game_variants where game_id in ('7c1d2c3d-0000-4000-8000-000000000002', '7c1d2c3d-0000-4000-8000-000000000004'))::text$$,
|
|
$$select k, v from other_before$$,
|
|
'QUE-04: Katalogspiele (neu angelegt und vorhanden), ihre Varianten und die Daten und Meldungen anderer Nutzer bleiben unverändert');
|
|
select is((select count(*) from public.game_reports where owner_id = '33333333-3333-3333-3333-333333333333'), 3::bigint,
|
|
'Caras Meldungen (zwei aus dem Runbook, eine Import-Meldung) sind nach Annas Löschung vorhanden');
|
|
|
|
select * from finish();
|
|
rollback;
|