670 lines
62 KiB
PL/PgSQL
670 lines
62 KiB
PL/PgSQL
-- KAT-04 (PR 13a): Fehlendes Spiel melden. Normalisierung (strip_leading_article, normalize_game_title),
|
|
-- report_day, Rechte/Policies/Trigger auf game_reports, find_similar_catalog_games, report_missing_game,
|
|
-- Ergebnis nach admin_resolve_report (merged/rejected, rejected nur ohne Ziel), Check game_reports_rejected_no_target, Konto-Löschen.
|
|
-- Läuft auch mit eingespieltem Startbestand: eigene Fixtures mit festen IDs (Präfix a130/f13) und
|
|
-- unwahrscheinlichen Titeln (zyxq...), damit der Katalog nicht hineinspielt.
|
|
-- Fixtures legt postgres an; "als Nutzer" läuft wirklich als Rolle authenticated mit gesetztem
|
|
-- request.jwt.claims. Regel dieser Datei: Jede Verweigerung steht neben einem Positivfall.
|
|
begin;
|
|
select plan(190);
|
|
|
|
insert into auth.users (id, email, raw_user_meta_data) values
|
|
('a1300000-0000-4000-8000-0000000000a1', 'kat04-a@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"KA"}'),
|
|
('a1300000-0000-4000-8000-0000000000b2', 'kat04-b@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"KB"}'),
|
|
('a1300000-0000-4000-8000-0000000000c3', 'kat04-c@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"KC"}'),
|
|
('a1300000-0000-4000-8000-0000000000d4', 'kat04-d@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"KD"}'),
|
|
('a1300000-0000-4000-8000-0000000000e1', 'kat04-l1@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"L1"}'),
|
|
('a1300000-0000-4000-8000-0000000000e2', 'kat04-l2@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"L2"}'),
|
|
('a1300000-0000-4000-8000-0000000000e3', 'kat04-l3@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"L3"}'),
|
|
('a1300000-0000-4000-8000-0000000000e4', 'kat04-y@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"KY"}'),
|
|
('a1300000-0000-4000-8000-0000000000e5', 'kat04-t1@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"T1"}'),
|
|
('a1300000-0000-4000-8000-0000000000e6', 'kat04-t2@example.test', '{"age_confirmed":"true","terms_accepted":"true","terms_version":"test","display_name":"T2"}');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 1. Funktionen: Rechte, Attribute (Spec 4.1)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
create temp table fn (sig text primary key, names text, granted boolean);
|
|
grant all on fn to public;
|
|
insert into fn values
|
|
('public.strip_leading_article(text)', 'strip_leading_article', true),
|
|
('public.normalize_game_title(text)', 'normalize_game_title', true),
|
|
('public.report_day(timestamptz)', 'report_day', true),
|
|
('public.find_similar_catalog_games(text, integer)', 'find_similar_catalog_games', true),
|
|
('public.report_missing_game(text, text, integer, text, text, boolean)', 'report_missing_game', true),
|
|
('public.game_reports_daily_limit()', 'game_reports_daily_limit', false);
|
|
|
|
select is_empty($$select f.sig, r from fn f, unnest(array['anon', 'service_role']) r
|
|
where has_function_privilege(r, f.sig::regprocedure, 'execute')$$,
|
|
'Sicherheit Kriterium 2: anon und service_role dürfen keine der neuen Funktionen ausführen');
|
|
select is_empty($$select f.sig from fn f
|
|
where has_function_privilege('authenticated', f.sig::regprocedure, 'execute') <> f.granted$$,
|
|
'KAT-04: authenticated darf genau die RPCs und Hilfsfunktionen ausführen, die Trigger-Funktion nicht');
|
|
select is_empty($$select f.sig from fn f join pg_proc p on p.oid = f.sig::regprocedure
|
|
where p.proacl is null or exists (select 1 from aclexplode(p.proacl) a where a.grantee = 0)$$,
|
|
'KAT-04: keine neue Funktion ist für PUBLIC ausführbar (ACL gesetzt, kein Eintrag für grantee 0)');
|
|
select is_empty($$select f.sig from fn f join pg_proc p on p.oid = f.sig::regprocedure where p.prosecdef$$,
|
|
'KAT-04: keine der neuen Funktionen ist security definer (Entscheidung Chris 2026-11-04: kein adopt_report_target)');
|
|
select is((select count(*) from pg_proc p join pg_namespace n on n.oid = p.pronamespace where n.nspname = 'public' and p.proname = 'adopt_report_target'), 0::bigint,
|
|
'KAT-04 Kriterium 5: es gibt keine Funktion adopt_report_target');
|
|
select is_empty($$select f.sig from fn f join pg_proc p on p.oid = f.sig::regprocedure
|
|
where p.proconfig is null or not (p.proconfig @> array['search_path=""'])$$,
|
|
'KAT-04: alle neuen Funktionen haben search_path = "" als Funktionseinstellung');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 2. Normalisierung (Krit. 2, Klärung 15)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select is(public.strip_leading_article('die siedler von catan'), 'siedler von catan', 'KAT-04 Kriterium 2: führendes "die" entfällt');
|
|
select is(public.strip_leading_article('the crew'), 'crew', 'KAT-04 Kriterium 2: führendes "the" entfällt');
|
|
select is(public.strip_leading_article('a game of thrones'), 'game of thrones', 'KAT-04 Kriterium 2: führendes "a" entfällt');
|
|
select is(public.strip_leading_article('an apple'), 'apple', 'KAT-04 Kriterium 2: führendes "an" entfällt');
|
|
select is(public.strip_leading_article('einem haus'), 'haus', 'KAT-04 Kriterium 2: auch "einem" entfällt');
|
|
select is(public.strip_leading_article('das'), 'das', 'KAT-04 Kriterium 2: ein Titel nur aus dem Artikel bleibt unverändert');
|
|
select is(public.strip_leading_article('theater der welt'), 'theater der welt', 'KAT-04 Kriterium 2: kein Artikel, wenn das Wort nur damit beginnt');
|
|
select is(public.strip_leading_article('herr der ringe'), 'herr der ringe', 'KAT-04 Kriterium 2: Artikel mitten im Titel bleiben');
|
|
select is(public.strip_leading_article('der die das'), 'die das', 'KAT-04 Kriterium 2: nur einmal ein Artikel');
|
|
select is(public.strip_leading_article(null), null, 'KAT-04: strip_leading_article(null) ist null');
|
|
|
|
select is(public.normalize_game_title('Die Siedler von Catan'), public.normalize_game_title('siedler von catan'),
|
|
'KAT-04 Kriterium 2: "Die Siedler von Catan" und "siedler von catan" ergeben denselben Wert');
|
|
select is(public.normalize_game_title('CATAN!'), public.normalize_game_title('Catan'), 'KAT-04 Kriterium 2: Großbuchstaben und Sonderzeichen sind egal');
|
|
select is(public.normalize_game_title('The Crew'), public.normalize_game_title('Crew'), 'KAT-04 Kriterium 2: "The Crew" = "Crew"');
|
|
select is(public.normalize_game_title('Das'), 'das', 'KAT-04 Kriterium 2: ein Titel nur aus einem Artikel bleibt nicht leer');
|
|
select is(public.normalize_game_title('The'), 'the', 'KAT-04 Kriterium 2: auch "The" allein bleibt nicht leer');
|
|
select is(public.normalize_game_title('Mölkky'), public.normalize_game_title('Molkky'), 'KAT-04 Kriterium 2: Umlaute werden über search_normalize gefaltet');
|
|
select is(public.normalize_game_title('日本語'), '日本語', 'KAT-04 Kriterium 2: nicht-lateinischer Titel fällt auf lower(btrim) zurück');
|
|
select is(public.normalize_game_title(' !!! '), '!!!', 'KAT-04 Kriterium 2: Titel nur aus Sonderzeichen fällt auf lower(btrim) zurück');
|
|
select is(public.normalize_game_title(null), null, 'KAT-04: normalize_game_title(null) ist null');
|
|
|
|
select is(public.report_day('2026-01-15 23:30:00+00'), '2026-01-16'::date, 'KAT-04 Kriterium 7: 23:30 UTC im Winter ist schon der nächste Berliner Tag');
|
|
select is(public.report_day('2026-01-15 22:59:00+00'), '2026-01-15'::date, 'KAT-04 Kriterium 7: 22:59 UTC im Winter ist noch derselbe Tag');
|
|
select is(public.report_day('2026-07-15 21:59:00+00'), '2026-07-15'::date, 'KAT-04 Kriterium 7: 21:59 UTC im Sommer ist noch derselbe Tag');
|
|
select is(public.report_day('2026-07-15 22:00:00+00'), '2026-07-16'::date, 'KAT-04 Kriterium 7: 22:00 UTC im Sommer ist der nächste Tag');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 3. Rechte, Policies, Trigger auf game_reports (Spec 3.2)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select is((select array_agg(a.attname::text order by a.attname) from pg_attribute a
|
|
where a.attrelid = 'public.game_reports'::regclass and a.attnum > 0 and not a.attisdropped
|
|
and has_column_privilege('authenticated', 'public.game_reports', a.attname, 'insert')),
|
|
array['link', 'owner_id', 'provisional_game_id', 'publisher_name', 'title', 'year'],
|
|
'Sicherheit Kriterium 1: authenticated darf beim Anlegen genau owner_id, title, publisher_name, year, link, provisional_game_id setzen');
|
|
select is((select array_agg(a.attname::text order by a.attname) from pg_attribute a
|
|
where a.attrelid = 'public.game_reports'::regclass and a.attnum > 0 and not a.attisdropped
|
|
and has_column_privilege('authenticated', 'public.game_reports', a.attname, 'update')),
|
|
array['result_seen_at'],
|
|
'KAT-04 Kriterium 8: authenticated darf genau result_seen_at ändern');
|
|
select is_empty($$select a.attname, p from pg_attribute a, unnest(array['select', 'insert', 'update', 'references']) p
|
|
where a.attrelid = 'public.game_reports'::regclass and a.attnum > 0 and not a.attisdropped
|
|
and has_column_privilege('anon', 'public.game_reports', a.attname, p)$$,
|
|
'Sicherheit Kriterium 2: anon hat auf keine Spalte von game_reports ein Recht');
|
|
select is_empty($$select p from unnest(array['delete', 'truncate']) p where has_table_privilege('authenticated', 'public.game_reports', p)$$,
|
|
'Sicherheit Kriterium 1: authenticated darf Meldungen nicht löschen');
|
|
select is((select array_agg(policyname::text order by policyname) from pg_policies where tablename = 'game_reports' and cmd in ('INSERT', 'UPDATE')),
|
|
array['game_reports_insert_own', 'game_reports_update_seen'], 'KAT-04: genau eine Insert- und eine Update-Policy');
|
|
select ok((select pg_get_triggerdef(t.oid) ~ 'BEFORE INSERT ON public.game_reports FOR EACH ROW WHEN \(\(?new.origin = ''user''::text\)?\)'
|
|
from pg_trigger t where t.tgname = 'game_reports_daily_limit' and t.tgrelid = 'public.game_reports'::regclass),
|
|
'KAT-04 Kriterium 7: der Limit-Trigger läuft vor dem Insert und nur für origin = user');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 4. Katalog-Fixtures (postgres)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, year, visibility) values
|
|
('f13c0000-0000-4000-8000-000000000001', 'Quorblaxwitz Towers', 2020, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000002', 'Die Zyxqwvland Saga', 2018, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000003', 'Sortzyxq Reihenfolge', 2000, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000004', 'Sortzyxq Reihenfolg', 2000, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000005', 'Sortzyxq Reihenfol', 2000, 'catalog'),
|
|
('f13c0000-0000-4000-8000-00000000000d', 'Verstecktzyxq Spiel', 2015, 'catalog'),
|
|
('f13c0000-0000-4000-8000-00000000000e', 'Verstecktzyxq Spiel', 2015, 'catalog'),
|
|
('f13c0000-0000-4000-8000-00000000000f', 'Verstecktzyxq Spiel', 2015, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000010', 'Jahrlosxyz Spiel', null, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000011', '日本語ゲーム', 2010, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000012', 'Das', 2010, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000013', 'Adoptzyxq Ziel', 2021, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000014', 'Adoptzyxq Verstecktes Ziel', 2021, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000015', 'Adoptzyxq Zusammengefuehrtes Ziel', 2021, 'catalog'),
|
|
('f13c0000-0000-4000-8000-000000000017', repeat('x', 200), 2001, 'catalog');
|
|
insert into public.games (id, title, year, visibility)
|
|
select ('f13c0000-0000-4000-8000-' || lpad(to_hex(5 + i), 12, '0'))::uuid, 'Limitzyxq Spiel', 2005, 'catalog'
|
|
from generate_series(1, 7) i;
|
|
update public.games set hidden_at = now() where id in ('f13c0000-0000-4000-8000-00000000000e', 'f13c0000-0000-4000-8000-000000000014');
|
|
update public.games set hidden_at = now(), merged_into_id = 'f13c0000-0000-4000-8000-00000000000d'
|
|
where id in ('f13c0000-0000-4000-8000-00000000000f');
|
|
update public.games set hidden_at = now(), merged_into_id = 'f13c0000-0000-4000-8000-000000000013'
|
|
where id = 'f13c0000-0000-4000-8000-000000000015';
|
|
insert into public.game_alt_titles (game_id, title, locale) values
|
|
('f13c0000-0000-4000-8000-000000000002', 'The Zyxqwvland Chronicle', 'en');
|
|
insert into public.publishers (id, name) values
|
|
('f13c0000-0000-4000-8000-0000000000a1', 'Zyxq Verlag B'),
|
|
('f13c0000-0000-4000-8000-0000000000a2', 'Zyxq Verlag A');
|
|
insert into public.game_publishers (game_id, publisher_id) values
|
|
('f13c0000-0000-4000-8000-000000000001', 'f13c0000-0000-4000-8000-0000000000a1'),
|
|
('f13c0000-0000-4000-8000-000000000001', 'f13c0000-0000-4000-8000-0000000000a2');
|
|
insert into public.game_variants (id, game_id, name) values
|
|
('f13c0000-0000-4000-8000-0000000000b1', 'f13c0000-0000-4000-8000-000000000013', 'Standard');
|
|
-- private Spiele mit dem Titel des sichtbaren Katalogspiels: Bens und Annas eigener vorläufiger Eintrag
|
|
insert into public.games (id, title, year, visibility, owner_id) values
|
|
('f13a0000-0000-4000-8000-000000000001', 'Verstecktzyxq Spiel', 2015, 'private', 'a1300000-0000-4000-8000-0000000000b2'),
|
|
('f13a0000-0000-4000-8000-000000000002', 'Verstecktzyxq Spiel', 2015, 'private', 'a1300000-0000-4000-8000-0000000000a1');
|
|
|
|
-- Schwellen vorab nachrechnen, damit die Trigramm-Fälle wirklich an der Grenze von 0,8 liegen
|
|
select cmp_ok(extensions.similarity('quorblaxwitz towers', 'quorblaxwitz towe'), '>=', 0.8::real, 'Vorbedingung: "towe" liegt knapp über 0,8');
|
|
select cmp_ok(extensions.similarity('quorblaxwitz towers', 'quorblaxwitz tow'), '<', 0.8::real, 'Vorbedingung: "tow" liegt unter 0,8');
|
|
select cmp_ok(extensions.similarity('quorblaxwitz towers', 'quorblaxwitz tow'), '>=', 0.7::real, 'Vorbedingung: "tow" liegt über 0,7 (fängt die Schwelle 0,7)');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 5. find_similar_catalog_games (Krit. 2)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000b2","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
|
|
select results_eq($$select game_id, title, matched_title, year, publishers, match_kind, similarity
|
|
from public.find_similar_catalog_games('Quorblaxwitz Towers', 2020)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000001'::uuid, 'Quorblaxwitz Towers', 'Quorblaxwitz Towers', 2020::smallint,
|
|
array['Zyxq Verlag A', 'Zyxq Verlag B'], 'same_title', 1.0::real)$$,
|
|
'KAT-04 Kriterium 2: gleicher Titel liefert Treffer mit Haupttitel, Jahr, sortierten Verlagen, same_title und Ähnlichkeit 1');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towers', 2030)), 1::bigint,
|
|
'KAT-04 Kriterium 2: gleicher normalisierter Titel gilt unabhängig vom Jahr');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towers', null)), 1::bigint,
|
|
'KAT-04 Kriterium 2: gleicher Titel ohne Jahr trifft');
|
|
select is((select count(*) from public.find_similar_catalog_games('The QUORBLAXWITZ-TOWERS!', 1999)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Artikel, Großbuchstaben und Sonderzeichen werden ignoriert');
|
|
select is((select count(*) from public.find_similar_catalog_games(' Quorblaxwitz Towers ', 2020)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Leerraum am Rand ändert nichts');
|
|
select results_eq($$select match_kind, similarity > 0.8 and similarity < 1 from public.find_similar_catalog_games('Quorblaxwitz Towe', 2021)$$,
|
|
$$values ('similar_title', true)$$,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit >= 0,8 bei Jahr + 1 trifft als similar_title mit echter Ähnlichkeit');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 2019)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit >= 0,8 bei Jahr - 1 trifft');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 2020)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit >= 0,8 bei gleichem Jahr trifft');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 2022)), 0::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit >= 0,8 bei Jahr + 2 trifft nicht');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 2018)), 0::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit >= 0,8 bei Jahr - 2 trifft nicht');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', null)), 1::bigint,
|
|
'KAT-04 Kriterium 2 (Spec-F2): ohne Jahr gilt die Trigramm-Regel ohne Jahresbedingung');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 2147483647)), 1::bigint,
|
|
'KAT-04 Kriterium 2: ein Jahr weit außerhalb 1800-2100 gilt wie ein fehlendes Jahr (kein Überlauf)');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', -2147483648)), 1::bigint,
|
|
'KAT-04 Kriterium 2: ein stark negatives Jahr gilt wie ein fehlendes Jahr (kein 22003)');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Towe', 1700)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Jahr 1700 gilt wie ein fehlendes Jahr');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Tow', 2020)), 0::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit < 0,8 trifft nicht, auch bei gleichem Jahr');
|
|
select is((select count(*) from public.find_similar_catalog_games('Quorblaxwitz Tow', null)), 0::bigint,
|
|
'KAT-04 Kriterium 2: Ähnlichkeit < 0,8 trifft auch ohne Jahr nicht');
|
|
select is((select count(*) from public.find_similar_catalog_games('Jahrlosxyz Spie', 2020)), 1::bigint,
|
|
'KAT-04 Kriterium 2 (Spec-F2): fehlt das Jahr am Katalogspiel, gilt die Trigramm-Regel ohne Jahresbedingung');
|
|
select is((select count(*) from public.find_similar_catalog_games('Jahrlosxyz Spi', 2020)), 0::bigint,
|
|
'KAT-04 Kriterium 2: auch ohne Katalogjahr zählt die Schwelle 0,8');
|
|
|
|
-- alternative Titel und Haupttitel mit Artikel
|
|
select results_eq($$select game_id, matched_title, match_kind from public.find_similar_catalog_games('Zyxqwvland Chronicle', 1900)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000002'::uuid, 'The Zyxqwvland Chronicle', 'same_title')$$,
|
|
'KAT-04 Kriterium 2: Treffer über title_search des alternativen Titels (matched_title), Artikel am alternativen Titel ignoriert');
|
|
select results_eq($$select title, matched_title, match_kind from public.find_similar_catalog_games('zyxqwvland saga', 1900)$$,
|
|
$$values ('Die Zyxqwvland Saga', 'Die Zyxqwvland Saga', 'same_title')$$,
|
|
'KAT-04 Kriterium 2: Treffer über den Haupttitel (games.title_search), Artikel am Haupttitel ignoriert');
|
|
select results_eq($$select matched_title, match_kind from public.find_similar_catalog_games('Zyxqwvland Chronicl', 2019)$$,
|
|
$$values ('The Zyxqwvland Chronicle', 'similar_title')$$,
|
|
'KAT-04 Kriterium 2: Trigramm-Treffer über den alternativen Titel (Jahr + 1 zum Spiel)');
|
|
select is((select count(*) from public.find_similar_catalog_games('Zyxqwvland Chronicl', 2021)), 0::bigint,
|
|
'KAT-04 Kriterium 2: Trigramm-Treffer über den alternativen Titel gilt das Jahr des Spiels (± 2 trifft nicht)');
|
|
|
|
-- Sonderfälle der Normalisierung
|
|
select is((select count(*) from public.find_similar_catalog_games('日本語ゲーム', null)), 1::bigint,
|
|
'KAT-04 Kriterium 2: Titel ohne normalisierbare Zeichen trifft über den Rückfall lower(btrim)');
|
|
select is((select count(*) from public.find_similar_catalog_games('das', null)), 1::bigint,
|
|
'KAT-04 Kriterium 2: ein Titel nur aus einem Artikel trifft das gleichnamige Spiel');
|
|
|
|
-- nicht sichtbare Spiele (hidden, merged, privat von Ben und Anna) treffen nicht; das sichtbare Spiel schon
|
|
select results_eq($$select game_id from public.find_similar_catalog_games('Verstecktzyxq Spiel', 2015)$$,
|
|
$$values ('f13c0000-0000-4000-8000-00000000000d'::uuid)$$,
|
|
'KAT-04 Kriterium 2: nur das sichtbare Katalogspiel trifft, nicht ausgeblendete, zusammengeführte oder private Spiele (auch nicht Bens und das eigene)');
|
|
|
|
-- Eingabegrenzen, Reihenfolge, Höchstzahl
|
|
select is((select count(*) from public.find_similar_catalog_games(repeat('x', 200), null)), 1::bigint, 'KAT-04 Kriterium 2: Titel mit genau 200 Zeichen ist erlaubt und trifft das gleichlange Katalogspiel');
|
|
select is((select count(*) from public.find_similar_catalog_games(repeat('x', 201), null)), 0::bigint, 'KAT-04 Kriterium 2: Titel über 200 Zeichen liefert eine leere Menge (obwohl dieselben Trigramme ein Katalogspiel träfen)');
|
|
select is((select count(*) from public.find_similar_catalog_games(' ', null)), 0::bigint, 'KAT-04 Kriterium 2: leerer Titel liefert eine leere Menge');
|
|
select is((select count(*) from public.find_similar_catalog_games(null, null)), 0::bigint, 'KAT-04 Kriterium 2: Titel null liefert eine leere Menge');
|
|
select is((select count(*) from public.find_similar_catalog_games('!!!', null)), 0::bigint, 'KAT-04 Kriterium 2: Titel nur aus Sonderzeichen liefert keinen Treffer');
|
|
select is((select count(*) from public.find_similar_catalog_games('Limitzyxq Spiel', 2005)), 5::bigint,
|
|
'KAT-04 Kriterium 2: höchstens 5 Treffer');
|
|
select results_eq($$select game_id, match_kind from public.find_similar_catalog_games('Sortzyxq Reihenfolge', 2000)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000003'::uuid, 'same_title'),
|
|
('f13c0000-0000-4000-8000-000000000004'::uuid, 'similar_title'),
|
|
('f13c0000-0000-4000-8000-000000000005'::uuid, 'similar_title')$$,
|
|
'KAT-04 Kriterium 2: same_title zuerst, danach nach Ähnlichkeit absteigend');
|
|
reset role;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 6. report_missing_game (Krit. 1, 2, 3, 7, 8)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- nicht angemeldet
|
|
select set_config('request.jwt.claims', '{"role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Ohne Anmeldung')$$, 'P0001', 'not_authenticated',
|
|
'Sicherheit Kriterium 2: ohne Nutzerkennung (auth.uid() null) wird nichts gemeldet');
|
|
reset role;
|
|
select set_config('request.jwt.claims', '{"role":"anon"}', true);
|
|
set local role anon;
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Anon')$$, '42501', null, 'Sicherheit Kriterium 2: anon darf report_missing_game nicht aufrufen');
|
|
select throws_ok($$select * from public.find_similar_catalog_games('Zyxqtest Anon')$$, '42501', null, 'Sicherheit Kriterium 2: anon darf find_similar_catalog_games nicht aufrufen');
|
|
select throws_ok($$select public.normalize_game_title('x')$$, '42501', null, 'Sicherheit Kriterium 2: anon darf normalize_game_title nicht aufrufen');
|
|
select throws_ok($$select public.strip_leading_article('x')$$, '42501', null, 'Sicherheit Kriterium 2: anon darf strip_leading_article nicht aufrufen');
|
|
select throws_ok($$select public.report_day(now())$$, '42501', null, 'Sicherheit Kriterium 2: anon darf report_day nicht aufrufen');
|
|
select throws_ok($$select 1 from public.game_reports$$, '42501', null, 'Sicherheit Kriterium 2: anon darf keine Meldungen lesen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000a1', 'x', null)$$, '42501', null,
|
|
'Sicherheit Kriterium 2: anon darf keine Meldung anlegen');
|
|
select throws_ok($$update public.game_reports set result_seen_at = now()$$, '42501', null, 'Sicherheit Kriterium 2: anon darf keine Meldung ändern');
|
|
reset role;
|
|
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
|
|
select results_eq($$select game_id from public.find_similar_catalog_games('Verstecktzyxq Spiel', 2015)$$,
|
|
$$values ('f13c0000-0000-4000-8000-00000000000d'::uuid)$$,
|
|
'KAT-04 Kriterium 2 (Spec-F7): auch Annas eigener vorläufiger Eintrag mit demselben Titel trifft nicht');
|
|
|
|
-- Validierung (jeder Fehler mit Code, nichts bleibt zurück)
|
|
select throws_ok($$select public.report_missing_game('')$$, 'P0001', 'report_title_required', 'KAT-04 Kriterium 1: leerer Titel');
|
|
select throws_ok($$select public.report_missing_game(' ')$$, 'P0001', 'report_title_required', 'KAT-04 Kriterium 1: Titel nur aus Leerzeichen');
|
|
select throws_ok($$select public.report_missing_game(null)$$, 'P0001', 'report_title_required', 'KAT-04 Kriterium 1: Titel null');
|
|
select throws_ok($$select public.report_missing_game(repeat('t', 201))$$, 'P0001', 'report_title_too_long', 'KAT-04 Kriterium 1: Titel über 200 Zeichen');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Verlag', repeat('v', 201))$$, 'P0001', 'report_publisher_too_long', 'KAT-04 Kriterium 1: Verlag über 200 Zeichen');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Jahr', null, 1799)$$, 'P0001', 'report_year_invalid', 'KAT-04 Kriterium 1: Jahr 1799');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Jahr', null, 2101)$$, 'P0001', 'report_year_invalid', 'KAT-04 Kriterium 1: Jahr 2101');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Jahr', null, 99999)$$, 'P0001', 'report_year_invalid', 'KAT-04 Kriterium 1: Jahr 99999 ergibt report_year_invalid, kein 22003');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Link', null, null, 'ftp://example.org')$$, 'P0001', 'report_link_invalid', 'KAT-04 Kriterium 1: Link mit anderem Schema');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Link', null, null, 'example.org/spiel')$$, 'P0001', 'report_link_invalid', 'KAT-04 Kriterium 1: Link ohne Schema');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Link', null, null, 'HTTP://example.org')$$, 'P0001', 'report_link_invalid', 'KAT-04 Kriterium 1: Schema in Großbuchstaben (wie der Tabellen-Check)');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Link', null, null, 'https://' || repeat('l', 493))$$, 'P0001', 'report_link_invalid', 'KAT-04 Kriterium 1: Link mit 501 Zeichen');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Liste', null, null, null, 'foo')$$, 'P0001', 'report_list_invalid', 'KAT-04 Kriterium 3: unbekannte Liste');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Liste', null, null, null, '')$$, 'P0001', 'report_list_invalid', 'KAT-04 Kriterium 3: leere Liste ist keine Liste');
|
|
select is((select count(*) from public.game_reports) + (select count(*) from public.games where owner_id = 'a1300000-0000-4000-8000-0000000000a1' and title like 'Zyxqtest%'),
|
|
0::bigint, 'KAT-04 Kriterium 1: nach allen Validierungsfehlern bleibt nichts zurück');
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest Grenzwerte ' || repeat('t', 180), repeat('v', 200), 1800, 'https://' || repeat('l', 492))$$,
|
|
'KAT-04 Kriterium 1: Grenzwerte (Titel 200, Verlag 200, Jahr 1800, Link 500) sind gültig');
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest Jahr 2100', null, 2100, 'http://example.org')$$, 'KAT-04 Kriterium 1: Jahr 2100 und http-Link sind gültig');
|
|
reset role;
|
|
select is((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000a1'), 2::bigint, 'Vorbedingung: Anna hat 2 Meldungen von den Grenzwert-Fällen');
|
|
delete from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000a1';
|
|
delete from public.games where owner_id = 'a1300000-0000-4000-8000-0000000000a1' and title like 'Zyxqtest%';
|
|
|
|
-- Erfolg ohne Liste: Trimmen, Felder, Rückgabe
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
create temp table res1 as select public.report_missing_game(' Zyxqtest Eins ', ' ', 2019, ' https://example.org/eins ') as j;
|
|
grant all on res1 to public;
|
|
select ok((select j ? 'report_id' and j ? 'provisional_game_id' and j ? 'collection_item_id' and j->'collection_item_id' = 'null'::jsonb from res1),
|
|
'KAT-04 Kriterium 1: Rückgabe enthält report_id, provisional_game_id und collection_item_id null');
|
|
select results_eq($$select r.owner_id, r.origin, r.status, r.title, r.publisher_name, r.year, r.link, r.target_game_id, r.resolved_at, r.result_seen_at,
|
|
r.provisional_game_id = (res1.j->>'provisional_game_id')::uuid, r.created_at >= now() - interval '1 minute'
|
|
from public.game_reports r, res1 where r.id = (res1.j->>'report_id')::uuid$$,
|
|
$$values ('a1300000-0000-4000-8000-0000000000a1'::uuid, 'user', 'open', 'Zyxqtest Eins', null::text, 2019::smallint,
|
|
'https://example.org/eins', null::uuid, null::timestamptz, null::timestamptz, true, true)$$,
|
|
'KAT-04 Kriterium 1 und 8: Meldung gespeichert (getrimmt, Leerverlag null), Status offen, Herkunft user, kein Ziel');
|
|
select results_eq($$select g.title, g.year, g.visibility, g.owner_id, g.description, g.image_path, g.bgg_id, g.hidden_at, g.merged_into_id
|
|
from public.games g, res1 where g.id = (res1.j->>'provisional_game_id')::uuid$$,
|
|
$$values ('Zyxqtest Eins', 2019::smallint, 'private', 'a1300000-0000-4000-8000-0000000000a1'::uuid, null::text, null::text, null::integer, null::timestamptz, null::uuid)$$,
|
|
'KAT-04 Kriterium 3: vorläufiger Eintrag ist privat, gehört Anna und hat nur Titel und Jahr');
|
|
select is((select count(*) from public.collection_items where owner_id = 'a1300000-0000-4000-8000-0000000000a1'), 0::bigint,
|
|
'KAT-04 Kriterium 3: ohne p_list entsteht kein Sammlungseintrag');
|
|
|
|
create temp table res_tab as select public.report_missing_game(E'\tZyxqtest Tab\n') as j;
|
|
grant all on res_tab to public;
|
|
select is((select title from public.games where id = (select (j->>'provisional_game_id')::uuid from res_tab)), 'Zyxqtest Tab',
|
|
'KAT-04 Kriterium 1: Tab und Zeilenumbruch am Rand des Titels werden getrimmt');
|
|
|
|
-- Erfolg mit Liste
|
|
create temp table res2 as select public.report_missing_game('Zyxqtest Zwei', 'Zyxq Eigenverlag', null, null, 'wishlist') as j;
|
|
grant all on res2 to public;
|
|
select results_eq($$select c.owner_id, c.game_id = (res2.j->>'provisional_game_id')::uuid, c.list, c.variant_id, c.id = (res2.j->>'collection_item_id')::uuid
|
|
from public.collection_items c, res2 where c.id = (res2.j->>'collection_item_id')::uuid$$,
|
|
$$values ('a1300000-0000-4000-8000-0000000000a1'::uuid, true, 'wishlist', null::uuid, true)$$,
|
|
'KAT-04 Kriterium 3: p_list legt den Sammlungseintrag auf den vorläufigen Eintrag an, ohne Edition');
|
|
select is((select publisher_name from public.game_reports where id = (select (j->>'report_id')::uuid from res2)), 'Zyxq Eigenverlag',
|
|
'KAT-04 Kriterium 1: der Verlag wird als Freitext gespeichert');
|
|
create temp table res_own as select public.report_missing_game('Zyxqtest Besitz', null, null, null, 'owned') as j;
|
|
grant all on res_own to public;
|
|
select is((select list from public.collection_items where id = (select (j->>'collection_item_id')::uuid from res_own)), 'owned', 'KAT-04 Kriterium 3: Liste owned');
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest Einkauf', null, null, null, 'shopping')$$, 'KAT-04 Kriterium 3: Liste shopping');
|
|
reset role;
|
|
|
|
-- Isolation: Ben sieht nichts von Anna
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000b2","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select is((select count(*) from public.game_reports where title like 'Zyxqtest%'), 0::bigint, 'Sicherheit Kriterium 1: Ben sieht Annas Meldungen nicht');
|
|
select is((select count(*) from public.games where title like 'Zyxqtest%'), 0::bigint, 'Sicherheit Kriterium 3: Ben sieht Annas vorläufige Einträge nicht');
|
|
select is((select count(*) from public.collection_items where game_id in (select (j->>'provisional_game_id')::uuid from res1 union all select (j->>'provisional_game_id')::uuid from res2)), 0::bigint,
|
|
'Sicherheit Kriterium 3: Ben sieht Annas Listeneinträge nicht');
|
|
reset role;
|
|
|
|
-- Ähnlichkeitsprüfung beim Speichern (Krit. 2)
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000c3","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.report_missing_game('Die Quorblaxwitz Towers', null, 2020)$$, 'P0001', 'report_similar_exists',
|
|
'KAT-04 Kriterium 2: ein sehr ähnlicher Katalogeintrag ohne Bestätigung wird nicht gespeichert');
|
|
select throws_ok($$select public.report_missing_game('Quorblaxwitz Towers', null, 2020, null, null, null)$$, 'P0001', 'report_similar_exists',
|
|
'KAT-04 Kriterium 2: p_confirmed_distinct = null gilt als nicht bestätigt');
|
|
select is((select count(*) from public.game_reports) + (select count(*) from public.games where owner_id = 'a1300000-0000-4000-8000-0000000000c3'), 0::bigint,
|
|
'KAT-04 Kriterium 2: ohne Bestätigung bleibt nichts zurück');
|
|
select throws_ok($$select public.report_missing_game('Quorblaxwitz Towe', null, 2021)$$, 'P0001', 'report_similar_exists',
|
|
'KAT-04 Kriterium 2: die RPC prüft die Trigramm-Regel mit dem gemeldeten Jahr (± 1 trifft)');
|
|
select lives_ok($$select public.report_missing_game('Quorblaxwitz Towe', null, 2022)$$,
|
|
'KAT-04 Kriterium 2: die RPC prüft die Trigramm-Regel mit dem gemeldeten Jahr (± 2 trifft nicht, ohne Bestätigung gespeichert)');
|
|
select lives_ok($$select public.report_missing_game('Die Quorblaxwitz Towers', null, 2020, null, null, true)$$,
|
|
'KAT-04 Kriterium 2: mit ausdrücklicher Bestätigung wird gespeichert');
|
|
select is((select count(*) from public.game_reports where title in ('Die Quorblaxwitz Towers', 'Quorblaxwitz Towe')), 2::bigint, 'KAT-04 Kriterium 2: die bestätigte und die nicht ähnliche Meldung sind gespeichert');
|
|
reset role;
|
|
|
|
-- 23505 am eindeutigen Index auf provisional_game_id: über die RPC nicht erreichbar (jeder Aufruf legt ein frisches Spiel an).
|
|
-- Ein Hilfstrigger lenkt den Insert in game_reports auf ein schon verwendetes Spiel um.
|
|
create function pg_temp.reuse_prov() returns trigger language plpgsql as $$
|
|
begin
|
|
new.provisional_game_id := (select r.provisional_game_id from public.game_reports r where r.title = 'Die Quorblaxwitz Towers' and r.owner_id = new.owner_id);
|
|
return new;
|
|
end $$;
|
|
create trigger zz_reuse_prov before insert on public.game_reports for each row execute function pg_temp.reuse_prov();
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000c3","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Doppelt belegt')$$, 'P0001', 'report_provisional_in_use',
|
|
'KAT-04 Kriterium 3: ein schon verwendeter vorläufiger Eintrag wird als report_provisional_in_use gemeldet');
|
|
select is((select count(*) from public.games where title = 'Zyxqtest Doppelt belegt'), 0::bigint, 'KAT-04 Kriterium 3: der Konflikt lässt kein vorläufiges Spiel zurück');
|
|
reset role;
|
|
drop trigger zz_reuse_prov on public.game_reports;
|
|
|
|
-- Rollback bei Fehler im Listeneintrag: ein Hilfstrigger lässt den Insert in collection_items scheitern
|
|
create function pg_temp.fail_ci() returns trigger language plpgsql as $$ begin raise exception 'boom' using errcode = 'P0001'; end $$;
|
|
create trigger fail_ci before insert on public.collection_items for each row execute function pg_temp.fail_ci();
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000c3","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest Rollback', null, null, null, 'owned')$$, 'P0001', 'boom',
|
|
'Vorbedingung: der Listeneintrag scheitert');
|
|
select is((select count(*) from public.game_reports where title = 'Zyxqtest Rollback') + (select count(*) from public.games where title = 'Zyxqtest Rollback'), 0::bigint,
|
|
'KAT-04 Kriterium 3: scheitert der Listeneintrag, bleiben weder Meldung noch vorläufiges Spiel zurück');
|
|
reset role;
|
|
drop trigger fail_ci on public.collection_items;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 7. Tageslimit (Krit. 7)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$select public.report_missing_game('Tageslimit Meldung ' || i) from generate_series(1, 10) i$$,
|
|
'KAT-04 Kriterium 7: 10 Meldungen über report_missing_game sind erlaubt');
|
|
select throws_ok($$select public.report_missing_game('Tageslimit Meldung 11')$$, 'P0001', 'report_daily_limit', 'KAT-04 Kriterium 7: die 11. Meldung scheitert am Tageslimit');
|
|
select throws_ok($$select public.report_missing_game('Quorblaxwitz Towers', null, 2020)$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: das Limit wird vor der Ähnlichkeitsprüfung gemeldet');
|
|
select is((select count(*) from public.game_reports), 10::bigint, 'KAT-04 Kriterium 7: es bleiben genau 10 Meldungen');
|
|
select is((select count(*) from public.games where owner_id = 'a1300000-0000-4000-8000-0000000000e1'), 10::bigint, 'KAT-04 Kriterium 7: kein vorläufiges Spiel der abgelehnten Meldung bleibt übrig');
|
|
reset role;
|
|
|
|
-- Meldungen von gestern zählen nicht
|
|
insert into public.games (id, title, visibility, owner_id)
|
|
select ('f13b0000-0000-4000-8000-' || lpad(to_hex(i), 12, '0'))::uuid, 'Gestern ' || i, 'private', 'a1300000-0000-4000-8000-0000000000e4'
|
|
from generate_series(1, 13) i;
|
|
insert into public.game_reports (owner_id, title, provisional_game_id, created_at)
|
|
select 'a1300000-0000-4000-8000-0000000000e4', 'Gestern ' || i, ('f13b0000-0000-4000-8000-' || lpad(to_hex(i), 12, '0'))::uuid,
|
|
-- created_at ist nach dem Anlegen unveränderlich: feste Zeitstempel gleich beim Einfügen (Nr. 1 und 2 an der Berliner Tagesgrenze)
|
|
case i when 1 then '2026-01-15 22:59:00+00'::timestamptz when 2 then '2026-01-15 23:30:00+00'::timestamptz else '2026-01-01 12:00:00+00'::timestamptz end
|
|
from generate_series(1, 10) i;
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e4","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$select public.report_missing_game('Heute nach zehn Meldungen von gestern')$$, 'KAT-04 Kriterium 7: Meldungen von gestern zählen nicht');
|
|
reset role;
|
|
-- Grenze des Berliner Tages mit festem Zeitstempel: 23:30 UTC im Winter (00:30 am Folgetag in Berlin) zählt nicht zu 22:59 UTC desselben Tages
|
|
select is((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000e4' and public.report_day(created_at) = '2026-01-15'), 1::bigint,
|
|
'KAT-04 Kriterium 7: der Berliner Kalendertag trennt 22:59 und 23:30 UTC im Winter');
|
|
|
|
-- Tagesgrenze: Meldungen kurz nach Mitternacht Berlin zählen als heute (auch wenn das UTC-Datum ein anderes ist).
|
|
-- Uhrzeitabhängig: Läuft der Test zwischen Mitternacht Berlin und Mitternacht UTC, hat created_at dasselbe UTC-Datum wie now();
|
|
-- die Tests bestehen dann trotzdem, erkennen aber die Mutante created_at::date = now()::date nicht.
|
|
insert into public.games (id, title, visibility, owner_id)
|
|
select ('f13b0000-0000-4000-8000-' || lpad(to_hex(200 + i), 12, '0'))::uuid, 'Mitternacht ' || i, 'private',
|
|
case when i <= 10 then 'a1300000-0000-4000-8000-0000000000e5'::uuid else 'a1300000-0000-4000-8000-0000000000e6'::uuid end
|
|
from generate_series(1, 22) i;
|
|
insert into public.game_reports (owner_id, title, provisional_game_id, created_at)
|
|
select case when i <= 10 then 'a1300000-0000-4000-8000-0000000000e5'::uuid else 'a1300000-0000-4000-8000-0000000000e6'::uuid end,
|
|
'Mitternacht ' || i, ('f13b0000-0000-4000-8000-' || lpad(to_hex(200 + i), 12, '0'))::uuid,
|
|
(date_trunc('day', now() at time zone 'Europe/Berlin') at time zone 'Europe/Berlin') + interval '1 minute'
|
|
from generate_series(1, 20) i;
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e5","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$select public.report_missing_game('Mitternacht Vorprüfung')$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: 10 Meldungen kurz nach Mitternacht Berlin zählen für heute (Vorprüfung der RPC)');
|
|
select throws_ok($$select public.report_missing_game('Quorblaxwitz Towers', null, 2020)$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: die Vorprüfung zählt Meldungen nach Mitternacht Berlin auch vor der Ähnlichkeitsprüfung');
|
|
reset role;
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e6","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values
|
|
('a1300000-0000-4000-8000-0000000000e6', 'Mitternacht 21', 'f13b0000-0000-4000-8000-0000000000dd')$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: 10 Meldungen kurz nach Mitternacht Berlin zählen für heute (Trigger)');
|
|
reset role;
|
|
|
|
-- Direkter Insert als authenticated: Trigger zählt, report_day ist für authenticated ausführbar
|
|
insert into public.games (id, title, visibility, owner_id)
|
|
select ('f13b0000-0000-4000-8000-' || lpad(to_hex(100 + i), 12, '0'))::uuid, 'Direkt ' || i, 'private', 'a1300000-0000-4000-8000-0000000000e2'
|
|
from generate_series(1, 11) i;
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e2","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, provisional_game_id)
|
|
select 'a1300000-0000-4000-8000-0000000000e2', 'Direkt ' || i, ('f13b0000-0000-4000-8000-' || lpad(to_hex(100 + i), 12, '0'))::uuid from generate_series(1, 10) i$$,
|
|
'KAT-04 Kriterium 7: 10 direkte Inserts als authenticated sind erlaubt (der Trigger darf report_day ausführen)');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values
|
|
('a1300000-0000-4000-8000-0000000000e2', 'Direkt 11', 'f13b0000-0000-4000-8000-00000000006f')$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: auch der direkte 11. Insert scheitert am Limit');
|
|
reset role;
|
|
|
|
-- Import-Meldungen zählen nicht und werden nicht begrenzt (N12)
|
|
insert into public.game_reports (owner_id, origin, title, bgg_id)
|
|
select 'a1300000-0000-4000-8000-0000000000e3', 'import', 'Import ' || i, 9000 + i from generate_series(1, 12) i;
|
|
select is((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000e3' and origin = 'import'), 12::bigint,
|
|
'KAT-04 Kriterium 7 (N12): mehr als 10 Import-Meldungen werden nicht begrenzt');
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000e3","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest nach Import ' || i) from generate_series(1, 10) i$$,
|
|
'KAT-04 Kriterium 7 (N12): Import-Meldungen zählen nicht auf das Limit des Nutzers');
|
|
select throws_ok($$select public.report_missing_game('Zyxqtest nach Import 11')$$, 'P0001', 'report_daily_limit',
|
|
'KAT-04 Kriterium 7: danach greift das Limit für Nutzer-Meldungen');
|
|
reset role;
|
|
-- Meldungen von Nutzer B zählen nicht für Nutzer A: Anna hat oben nur wenige, Ben (e1 hat 10) ändert nichts
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest Anna nach Limit anderer')$$, 'KAT-04 Kriterium 7: die Meldungen anderer Nutzer zählen nicht für Anna');
|
|
reset role;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 8. Direkter Zugriff auf game_reports: Policies und Spaltenrechte (Krit. 8, Sicherheit 1 und 3)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
insert into public.games (id, title, visibility, owner_id, hidden_at, merged_into_id) values
|
|
('f13a0000-0000-4000-8000-000000000011', 'Direkt ok', 'private', 'a1300000-0000-4000-8000-0000000000c3', null, null),
|
|
('f13a0000-0000-4000-8000-000000000012', 'Direkt versteckt', 'private', 'a1300000-0000-4000-8000-0000000000c3', now(), null),
|
|
('f13a0000-0000-4000-8000-000000000013', 'Direkt zusammengeführt', 'private', 'a1300000-0000-4000-8000-0000000000c3', now(), 'f13c0000-0000-4000-8000-00000000000d'),
|
|
('f13a0000-0000-4000-8000-000000000014', 'Bens Privatspiel', 'private', 'a1300000-0000-4000-8000-0000000000b2', null, null),
|
|
('f13a0000-0000-4000-8000-000000000015', 'Direkt ok 2', 'private', 'a1300000-0000-4000-8000-0000000000c3', null, null);
|
|
-- Hilfsfunktion (läuft mit den Rechten des Aufrufers): result_seen_at setzen und die Zahl der geänderten Zeilen liefern.
|
|
-- (Ein datenändernder WITH-Block ist in einer Unterabfrage nicht erlaubt.)
|
|
create function pg_temp.mark_seen_all(p_at timestamptz) returns bigint language plpgsql as $$
|
|
declare n bigint;
|
|
begin
|
|
update public.game_reports set result_seen_at = p_at; -- bewusst ohne WHERE: nur die Update-Policy begrenzt die Zeilen
|
|
get diagnostics n = row_count;
|
|
return n;
|
|
end $$;
|
|
create function pg_temp.mark_seen(p_id uuid, p_at timestamptz) returns bigint language plpgsql as $$
|
|
declare n bigint;
|
|
begin
|
|
update public.game_reports set result_seen_at = p_at where p_id is null or id = p_id;
|
|
get diagnostics n = row_count;
|
|
return n;
|
|
end $$;
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000c3","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Direkt ok', 'f13a0000-0000-4000-8000-000000000011')$$,
|
|
'Sicherheit Kriterium 1: Cara legt direkt eine Meldung zu ihrem eigenen privaten Spiel an (Positivfall)');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000b2', 'Im Namen von Ben', 'f13a0000-0000-4000-8000-000000000014')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: Cara kann keine Meldung für Ben anlegen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000b2', 'Fremder Besitzer, eigenes Spiel', 'f13a0000-0000-4000-8000-000000000015')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: owner_id eines anderen Nutzers wird abgelehnt, auch wenn das vorläufige Spiel Cara gehört');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title) values ('a1300000-0000-4000-8000-0000000000c3', 'Ohne vorläufigen Eintrag')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: eine Meldung ohne vorläufigen Eintrag wird abgelehnt');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Bens Spiel', 'f13a0000-0000-4000-8000-000000000014')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: ein fremdes privates Spiel als vorläufiger Eintrag wird abgelehnt');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Katalogspiel', 'f13c0000-0000-4000-8000-000000000001')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: ein Katalogspiel als vorläufiger Eintrag wird abgelehnt');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Versteckt', 'f13a0000-0000-4000-8000-000000000012')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: ein ausgeblendetes Spiel als vorläufiger Eintrag wird abgelehnt');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Zusammengeführt', 'f13a0000-0000-4000-8000-000000000013')$$, '42501', null,
|
|
'Sicherheit Kriterium 1: ein zusammengeführtes Spiel als vorläufiger Eintrag wird abgelehnt');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'Doppelt', 'f13a0000-0000-4000-8000-000000000011')$$, '23505', null,
|
|
'Sicherheit Kriterium 1: eine zweite Meldung zum selben vorläufigen Eintrag scheitert am eindeutigen Index');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, status) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', 'approved')$$, '42501', null,
|
|
'KAT-04 Kriterium 8: status lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, origin) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', 'import')$$, '42501', null,
|
|
'KAT-04 Kriterium 7: origin lässt sich beim Anlegen nicht setzen (Import-Meldungen umgehen das Limit nicht)');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, created_at) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', '2026-01-01')$$, '42501', null,
|
|
'KAT-04 Kriterium 7: created_at lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, target_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', 'f13c0000-0000-4000-8000-000000000001')$$, '42501', null,
|
|
'KAT-04 Kriterium 8: target_game_id lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, resolved_at) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', now())$$, '42501', null,
|
|
'KAT-04 Kriterium 8: resolved_at lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id, bgg_id) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015', 1)$$, '42501', null,
|
|
'KAT-04: bgg_id lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (id, owner_id, title, provisional_game_id) values (gen_random_uuid(), 'a1300000-0000-4000-8000-0000000000c3', 'S', 'f13a0000-0000-4000-8000-000000000015')$$, '42501', null,
|
|
'KAT-04: id lässt sich beim Anlegen nicht setzen');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', '', 'f13a0000-0000-4000-8000-000000000015')$$, '23514', null,
|
|
'KAT-04 Kriterium 1: ein leerer Titel scheitert am Tabellen-Check');
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, year, provisional_game_id) values ('a1300000-0000-4000-8000-0000000000c3', 'S', 1700, 'f13a0000-0000-4000-8000-000000000015')$$, '23514', null,
|
|
'KAT-04 Kriterium 1: ein Jahr außerhalb 1800-2100 scheitert am Tabellen-Check');
|
|
select throws_ok($$update public.game_reports set status = 'approved' where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '42501', null,
|
|
'KAT-04 Kriterium 8: status lässt sich nicht ändern');
|
|
select throws_ok($$update public.game_reports set title = 'Umbenannt' where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '42501', null,
|
|
'KAT-04: title lässt sich nicht ändern');
|
|
select throws_ok($$update public.game_reports set target_game_id = 'f13c0000-0000-4000-8000-000000000001' where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '42501', null,
|
|
'KAT-04 Kriterium 8: target_game_id lässt sich nicht ändern');
|
|
select throws_ok($$update public.game_reports set resolved_at = now() where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '42501', null,
|
|
'KAT-04 Kriterium 8: resolved_at lässt sich nicht ändern');
|
|
select throws_ok($$delete from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '42501', null,
|
|
'Sicherheit Kriterium 1: eigene Meldungen lassen sich nicht löschen');
|
|
-- result_seen_at bei offener Meldung: 0 Zeilen
|
|
select is(pg_temp.mark_seen(null, now()), 0::bigint,
|
|
'KAT-04 Kriterium 8: result_seen_at an einer offenen Meldung ändert keine Zeile');
|
|
reset role;
|
|
select throws_ok($$update public.game_reports set result_seen_at = now() where owner_id = 'a1300000-0000-4000-8000-0000000000c3'$$, '23514', null,
|
|
'KAT-04 Kriterium 8: auch postgres kann result_seen_at bei einer offenen Meldung nicht setzen (Check)');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 9. Ergebnis nach admin_resolve_report (Krit. 4 und 6), result_seen_at
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
create temp table res_ap as select public.report_missing_game('Freigabezyxq Neu', 'Freigabe Verlag', 2023, null, 'owned') as j;
|
|
grant all on res_ap to public;
|
|
create temp table res_rej as select public.report_missing_game('Ablehnzyxq Spiel', null, null, null, 'wishlist') as j;
|
|
grant all on res_rej to public;
|
|
update public.collection_items set note = 'Annas Notiz', acquired_on = '2025-05-05'
|
|
where id = (select (j->>'collection_item_id')::uuid from res_ap);
|
|
reset role;
|
|
insert into public.game_variants (id, game_id, name) values
|
|
('f13a0000-0000-4000-8000-0000000000b1', (select (j->>'provisional_game_id')::uuid from res_ap), 'Eigene Edition');
|
|
update public.collection_items set variant_id = 'f13a0000-0000-4000-8000-0000000000b1'
|
|
where id = (select (j->>'collection_item_id')::uuid from res_ap);
|
|
insert into public.games (id, title, year, visibility) values ('f13c0000-0000-4000-8000-000000000016', 'Freigabezyxq Neu', 2023, 'catalog');
|
|
select lives_ok($$select public.admin_resolve_report((select (j->>'report_id')::uuid from res_ap), 'approved', 'f13c0000-0000-4000-8000-000000000016')$$,
|
|
'Vorbedingung: der Admin gibt die Meldung frei');
|
|
select lives_ok($$select public.admin_resolve_report((select (j->>'report_id')::uuid from res_rej), 'rejected')$$,
|
|
'Vorbedingung: der Admin lehnt die zweite Meldung ohne Ziel ab');
|
|
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select results_eq($$select game_id, list, note, acquired_on, variant_id from public.collection_items where id = (select (j->>'collection_item_id')::uuid from res_ap)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000016'::uuid, 'owned', 'Annas Notiz', '2025-05-05'::date, null::uuid)$$,
|
|
'KAT-04 Kriterium 4: der Katalogeintrag ersetzt den vorläufigen; Liste, Notiz und Kaufdatum bleiben, die Edition ohne gleichnamige Variante ist leer');
|
|
select results_eq($$select r.status, r.target_game_id, r.editions_cleared, r.result_seen_at from public.game_reports r where r.id = (select (j->>'report_id')::uuid from res_ap)$$,
|
|
$$values ('approved', 'f13c0000-0000-4000-8000-000000000016'::uuid, 1, null::timestamptz)$$,
|
|
'KAT-04 Kriterium 4: Meldung freigegeben, editions_cleared zählt die geleerte Edition, Hinweis noch ungesehen');
|
|
select is((select count(*) from public.game_variants where id = 'f13a0000-0000-4000-8000-0000000000b1'), 1::bigint,
|
|
'KAT-04 Kriterium 4: Annas private Variante bleibt für sie lesbar');
|
|
select results_eq($$select g.visibility, g.merged_into_id, g.hidden_at is not null from public.games g where g.id = (select (j->>'provisional_game_id')::uuid from res_ap)$$,
|
|
$$values ('private', 'f13c0000-0000-4000-8000-000000000016'::uuid, true)$$,
|
|
'KAT-04 Kriterium 4: der vorläufige Eintrag ist ausgeblendet und zeigt auf den Katalogeintrag, Anna liest ihn weiter');
|
|
select is(pg_temp.mark_seen((select (j->>'report_id')::uuid from res_ap), '2026-11-04 10:00:00+00'), 1::bigint,
|
|
'KAT-04 Kriterium 4: Anna kann den Hinweis an der freigegebenen Meldung wegklicken (result_seen_at)');
|
|
select is((select result_seen_at from public.game_reports where id = (select (j->>'report_id')::uuid from res_ap)), '2026-11-04 10:00:00+00'::timestamptz,
|
|
'KAT-04 Kriterium 4: result_seen_at ist gesetzt');
|
|
-- Krit. 6
|
|
select results_eq($$select r.status, r.target_game_id, r.provisional_game_id is not null from public.game_reports r where r.id = (select (j->>'report_id')::uuid from res_rej)$$,
|
|
$$values ('rejected', null::uuid, true)$$, 'KAT-04 Kriterium 6: abgelehnt ohne Ziel, der vorläufige Eintrag ist noch verknüpft');
|
|
select results_eq($$select g.visibility, g.merged_into_id, g.hidden_at from public.games g where g.id = (select (j->>'provisional_game_id')::uuid from res_rej)$$,
|
|
$$values ('private', null::uuid, null::timestamptz)$$, 'KAT-04 Kriterium 6: der vorläufige Eintrag bleibt privat, sichtbar und lesbar');
|
|
select is((select count(*) from public.collection_items where id = (select (j->>'collection_item_id')::uuid from res_rej) and game_id = (select (j->>'provisional_game_id')::uuid from res_rej)), 1::bigint,
|
|
'KAT-04 Kriterium 6: der Listeneintrag bleibt am vorläufigen Eintrag');
|
|
select is(pg_temp.mark_seen((select (j->>'report_id')::uuid from res_rej), now()), 1::bigint,
|
|
'KAT-04 Kriterium 6: auch an der abgelehnten Meldung lässt sich der Hinweis wegklicken');
|
|
reset role;
|
|
-- Fremder Nutzer kann nichts wegklicken
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000b2","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select is(pg_temp.mark_seen((select (j->>'report_id')::uuid from res_ap), now()), 0::bigint,
|
|
'Sicherheit Kriterium 1: Ben kann bei fremden Meldungen nichts wegklicken (0 Zeilen)');
|
|
select is(pg_temp.mark_seen_all(now()), 0::bigint,
|
|
'Sicherheit Kriterium 1: auch ein Update ohne WHERE trifft bei Ben keine fremde Meldung (Update-Policy)');
|
|
reset role;
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 10. Verweis auf vorhandenen Eintrag = merged, rejected nur ohne Ziel (Krit. 5; Entscheidung Chris 2026-11-04)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
create temp table res_mg as select public.report_missing_game('Verweiszyxq Spiel', null, null, null, 'wishlist') as j;
|
|
grant all on res_mg to public;
|
|
reset role;
|
|
select throws_ok($$select public.admin_resolve_report((select (j->>'report_id')::uuid from res_mg), 'rejected', 'f13c0000-0000-4000-8000-000000000013')$$,
|
|
'P0001', 'report_reject_has_target', 'KAT-04 Kriterium 5: rejected mit Ziel wird abgewiesen');
|
|
select is((select status from public.game_reports where id = (select (j->>'report_id')::uuid from res_mg)), 'open',
|
|
'KAT-04 Kriterium 5: die abgewiesene Meldung bleibt offen');
|
|
select throws_ok($$update public.game_reports set status = 'rejected', resolved_at = now(), target_game_id = 'f13c0000-0000-4000-8000-000000000013'
|
|
where id = (select (j->>'report_id')::uuid from res_mg)$$, '23514',
|
|
'new row for relation "game_reports" violates check constraint "game_reports_rejected_no_target"',
|
|
'KAT-04 Kriterium 5: auch die Tabelle erlaubt rejected mit Ziel nicht (Check game_reports_rejected_no_target)');
|
|
select lives_ok($$select public.admin_resolve_report((select (j->>'report_id')::uuid from res_mg), 'merged', 'f13c0000-0000-4000-8000-000000000013')$$,
|
|
'KAT-04 Kriterium 5: mit Verweis auf einen vorhandenen Eintrag wird die Meldung mit merged bearbeitet');
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000a1","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select results_eq($$select c.game_id, c.list from public.collection_items c where c.id = (select (j->>'collection_item_id')::uuid from res_mg)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000013'::uuid, 'wishlist')$$,
|
|
'KAT-04 Kriterium 5: der Listeneintrag zeigt direkt auf den vorhandenen Eintrag, ohne Aktion des Nutzers');
|
|
select results_eq($$select g.merged_into_id, g.hidden_at is not null from public.games g where g.id = (select (j->>'provisional_game_id')::uuid from res_mg)$$,
|
|
$$values ('f13c0000-0000-4000-8000-000000000013'::uuid, true)$$,
|
|
'KAT-04 Kriterium 5: der vorläufige Eintrag verweist auf den richtigen Eintrag und bleibt für Anna lesbar');
|
|
select is(pg_temp.mark_seen((select (j->>'report_id')::uuid from res_mg), now()), 1::bigint,
|
|
'KAT-04 Kriterium 5: Anna kann den Hinweis zur zusammengeführten Meldung wegklicken');
|
|
reset role;
|
|
select throws_ok($$insert into public.game_reports (owner_id, title, status, resolved_at, target_game_id)
|
|
values ('a1300000-0000-4000-8000-0000000000a1', 'X', 'rejected', now(), 'f13c0000-0000-4000-8000-000000000013')$$, '23514',
|
|
'new row for relation "game_reports" violates check constraint "game_reports_rejected_no_target"',
|
|
'KAT-04 Kriterium 5: Check game_reports_rejected_no_target greift auch beim Anlegen als postgres');
|
|
select lives_ok($$insert into public.game_reports (owner_id, title, status, resolved_at, created_at)
|
|
values ('a1300000-0000-4000-8000-0000000000a1', 'Abgelehnt ohne Ziel', 'rejected', now(), '2026-02-01')$$,
|
|
'KAT-04 Kriterium 6: rejected ohne Ziel ist zulässig');
|
|
|
|
-- ---------------------------------------------------------------------------------------------
|
|
-- 11. Datenschutz: Konto löschen (QUE-04, Klärung 10)
|
|
-- ---------------------------------------------------------------------------------------------
|
|
select set_config('request.jwt.claims', '{"sub":"a1300000-0000-4000-8000-0000000000d4","role":"authenticated"}', true);
|
|
set local role authenticated;
|
|
select lives_ok($$select public.report_missing_game('Zyxqtest Danas Spiel', null, null, null, 'owned')$$, 'Vorbedingung: Dana meldet ein Spiel');
|
|
reset role;
|
|
select is((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000d4'), 1::bigint, 'Vorbedingung: Dana hat eine Meldung');
|
|
select lives_ok($$delete from auth.users where id = 'a1300000-0000-4000-8000-0000000000d4'$$, 'QUE-04: Danas Konto lässt sich löschen');
|
|
select is((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000d4')
|
|
+ (select count(*) from public.games where owner_id = 'a1300000-0000-4000-8000-0000000000d4')
|
|
+ (select count(*) from public.collection_items where owner_id = 'a1300000-0000-4000-8000-0000000000d4'), 0::bigint,
|
|
'QUE-04: Meldungen, vorläufige Spiele und Listeneinträge sind mit dem Konto gelöscht');
|
|
select cmp_ok((select count(*) from public.game_reports where owner_id = 'a1300000-0000-4000-8000-0000000000a1'), '>', 0::bigint, 'QUE-04: Anna hat weiterhin ihre Meldungen');
|
|
|
|
select * from finish();
|
|
rollback;
|