From a38beadb8ee199831fcb72f83e8d52101c1d5a7d Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 20 Apr 2026 15:35:08 +0200 Subject: [PATCH] Enhance character deletion process in EventsWorker and UserCharacterWorker: Added SQL queries to delete associated data such as knowledge, debtors prism, political offices, and election candidates upon character deletion. Implemented event publishing for character death and status updates after inheritance changes, improving event handling and UI responsiveness. --- docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md | 110 +++++++++++++++++++ src/worker/events.rs | 29 ++++- src/worker/user_character.rs | 7 ++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md diff --git a/docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md b/docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md new file mode 100644 index 0000000..3c2debb --- /dev/null +++ b/docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md @@ -0,0 +1,110 @@ +# Falukant Smoke-Test: Tod -> Erbwechsel -> UI-Status + +Dieser Smoke-Test validiert den kritischen Ablauf: + +- Spieler-Charakter stirbt +- Erbe wird gesetzt +- alte charakterbezogene Daten sind bereinigt +- UI bekommt verlässlich den Status-Refresh + +## Ziel + +Verhindern, dass nach Tod/Erbe ein Mischzustand entsteht +(z. B. Erben-Bild, aber alte Statusdaten des verstorbenen Charakters). + +## Voraussetzungen + +- Daemon läuft mit aktuellen Änderungen. +- Zugriff auf DB (psql). +- Zugriff auf WebSocket-Eventstream oder Daemon-Log. +- Ein Test-User mit: + - aktivem Spieler-Charakter (`user_id` gesetzt), + - mindestens einem potenziellen Erben in `child_relation`. + +## Testfall A: Tod über `UserCharacterWorker` (health <= 0) + +1. Testkandidaten wählen: + +```sql +SELECT c.id AS character_id, c.user_id +FROM falukant_data.character c +WHERE c.user_id IS NOT NULL +ORDER BY c.updated_at DESC +LIMIT 20; +``` + +2. Einen Charakter künstlich auf `health = 0` setzen: + +```sql +UPDATE falukant_data.character +SET health = 0, updated_at = NOW() +WHERE id = ; +``` + +3. Worker laufen lassen (Death-Check oder nächster Verarbeitungstakt). + +4. WebSocket/Log prüfen, erwartet: + - `CharacterDeath` mit altem `character_id` + - `falukantUpdateStatus` mit `user_id` des betroffenen Spielers + +5. DB prüfen, erwartet: + +```sql +-- alter Charakter weg +SELECT 1 +FROM falukant_data.character +WHERE id = ; + +-- Spieler zeigt auf Erben +SELECT c.id, c.user_id +FROM falukant_data.character c +WHERE c.user_id = ; +``` + +```sql +-- Todes-Cleanup (sollte leer sein) +SELECT COUNT(*) FROM falukant_data.knowledge WHERE character_id = ; +SELECT COUNT(*) FROM falukant_data.debtors_prism WHERE character_id = ; +SELECT COUNT(*) FROM falukant_data.political_office WHERE character_id = ; +SELECT COUNT(*) FROM falukant_data.election_candidate WHERE character_id = ; +``` + +```sql +-- politische Ämter historisiert (falls vorher vorhanden) +SELECT * +FROM falukant_log.political_office_history +WHERE character_id = +ORDER BY end_date DESC +LIMIT 5; +``` + +Passkriterium: +- alter Charakter gelöscht, +- Nachfolger gesetzt, +- Cleanup-Counts = 0, +- `falukantUpdateStatus` gesendet. + +## Testfall B: Tod über `EventsWorker` + +Der gleiche Prüfsatz wie in Testfall A, aber mit Tod aus Event-Pfad. + +Hinweis: +- Falls kein reproduzierbares Zufallsereignis vorliegt, kann derselbe technische Endzustand + pragmatisch über A validiert werden. +- Zusätzlicher Fokus hier: auch im Event-Pfad müssen Cleanup + Status-Refresh identisch sein. + +## UI-Schnellcheck + +Nach Eintreffen von `falukantUpdateStatus`: + +- Avatar/Bild entspricht dem Erben. +- Statusdaten (Familie/Politik/bezogene Anzeigen) zeigen keine Relikte des Verstorbenen. +- Kein inkonsistenter Mischzustand nach manuellem Reload. + +## Regression-Checkliste + +- [ ] `CharacterDeath` kommt genau für den verstorbenen Charakter. +- [ ] `falukantUpdateStatus` kommt nach Erbwechsel für den betroffenen User. +- [ ] `knowledge`/`debtors_prism`/`political_office`/`election_candidate` sind bereinigt. +- [ ] `political_office_history` enthält entfernte Ämter (falls vorhanden). +- [ ] UI bleibt konsistent (Bild + Status gehören zum selben Charakter). diff --git a/src/worker/events.rs b/src/worker/events.rs index 54068c7..0344159 100644 --- a/src/worker/events.rs +++ b/src/worker/events.rs @@ -46,7 +46,11 @@ use crate::worker::sql::{ QUERY_GET_USER_ID, QUERY_DELETE_CHILD_RELATION, QUERY_DELETE_CHILD_RELATION_BY_PARENT, + QUERY_DELETE_DEBTORS_PRISM, QUERY_DELETE_CHARACTER, + QUERY_DELETE_ELECTION_CANDIDATE, + QUERY_DELETE_KNOWLEDGE, + QUERY_DELETE_POLITICAL_OFFICE, QUERY_GET_HEIR, QUERY_SET_CHARACTER_USER, QUERY_GET_CURRENT_MONEY, @@ -1815,6 +1819,12 @@ impl EventsWorker { broker: &MessageBroker, character_id: i32, ) -> Result<(), DbError> { + let death_event = format!( + r#"{{"event":"CharacterDeath","character_id":{}}}"#, + character_id + ); + broker.publish(death_event); + // Diese Funktion verwendet die gleiche Logik wie CharacterCreationWorker // Wir müssen die Queries aus character_creation.rs verwenden let mut conn = pool @@ -1922,7 +1932,17 @@ impl EventsWorker { } } - // 5) Charakter löschen + // 5) Weitere charaktergebundene Daten löschen (wie UserCharacterWorker) + conn.prepare("delete_knowledge", QUERY_DELETE_KNOWLEDGE)?; + conn.prepare("delete_debtors_prism", QUERY_DELETE_DEBTORS_PRISM)?; + conn.prepare("delete_political_office", QUERY_DELETE_POLITICAL_OFFICE)?; + conn.prepare("delete_election_candidate", QUERY_DELETE_ELECTION_CANDIDATE)?; + conn.execute("delete_knowledge", &[&character_id])?; + conn.execute("delete_debtors_prism", &[&character_id])?; + conn.execute("delete_political_office", &[&character_id])?; + conn.execute("delete_election_candidate", &[&character_id])?; + + // 6) Charakter löschen conn.prepare("delete_character", QUERY_DELETE_CHARACTER)?; conn.execute("delete_character", &[&character_id])?; @@ -2040,6 +2060,13 @@ impl EventsWorker { &format!("Erbe für Charakter {}", deceased_character_id), )?; + // Expliziter Status-Refresh für die UI nach Erbwechsel. + let status = format!( + r#"{{"event":"falukantUpdateStatus","user_id":{}}}"#, + falukant_user_id + ); + broker.publish(status); + eprintln!( "[EventsWorker] Erbe {} übernimmt Vermögen von Charakter {} (User {}): {:.2} (von {:.2} Gesamtvermögen, {} weitere Kinder)", heir_id, deceased_character_id, falukant_user_id, final_money, total_assets, child_count diff --git a/src/worker/user_character.rs b/src/worker/user_character.rs index 6e7100c..efb4565 100644 --- a/src/worker/user_character.rs +++ b/src/worker/user_character.rs @@ -960,6 +960,13 @@ impl UserCharacterWorker { } self.set_new_money(falukant_user_id, new_money)?; + // Nach Erbwechsel den Status sicher neu laden lassen. + let update_status = format!( + r#"{{"event":"falukantUpdateStatus","user_id":{}}}"#, + falukant_user_id + ); + self.base.broker.publish(update_status); + Ok(()) }