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.
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 1m41s

This commit is contained in:
Torsten Schulz (local)
2026-04-20 15:35:08 +02:00
parent 0892e2db8b
commit a38beadb8e
3 changed files with 145 additions and 1 deletions

View File

@@ -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 = <CHARACTER_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 = <CHARACTER_ID>;
-- Spieler zeigt auf Erben
SELECT c.id, c.user_id
FROM falukant_data.character c
WHERE c.user_id = <USER_ID>;
```
```sql
-- Todes-Cleanup (sollte leer sein)
SELECT COUNT(*) FROM falukant_data.knowledge WHERE character_id = <CHARACTER_ID>;
SELECT COUNT(*) FROM falukant_data.debtors_prism WHERE character_id = <CHARACTER_ID>;
SELECT COUNT(*) FROM falukant_data.political_office WHERE character_id = <CHARACTER_ID>;
SELECT COUNT(*) FROM falukant_data.election_candidate WHERE character_id = <CHARACTER_ID>;
```
```sql
-- politische Ämter historisiert (falls vorher vorhanden)
SELECT *
FROM falukant_log.political_office_history
WHERE character_id = <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).

View File

@@ -46,7 +46,11 @@ use crate::worker::sql::{
QUERY_GET_USER_ID, QUERY_GET_USER_ID,
QUERY_DELETE_CHILD_RELATION, QUERY_DELETE_CHILD_RELATION,
QUERY_DELETE_CHILD_RELATION_BY_PARENT, QUERY_DELETE_CHILD_RELATION_BY_PARENT,
QUERY_DELETE_DEBTORS_PRISM,
QUERY_DELETE_CHARACTER, QUERY_DELETE_CHARACTER,
QUERY_DELETE_ELECTION_CANDIDATE,
QUERY_DELETE_KNOWLEDGE,
QUERY_DELETE_POLITICAL_OFFICE,
QUERY_GET_HEIR, QUERY_GET_HEIR,
QUERY_SET_CHARACTER_USER, QUERY_SET_CHARACTER_USER,
QUERY_GET_CURRENT_MONEY, QUERY_GET_CURRENT_MONEY,
@@ -1815,6 +1819,12 @@ impl EventsWorker {
broker: &MessageBroker, broker: &MessageBroker,
character_id: i32, character_id: i32,
) -> Result<(), DbError> { ) -> 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 // Diese Funktion verwendet die gleiche Logik wie CharacterCreationWorker
// Wir müssen die Queries aus character_creation.rs verwenden // Wir müssen die Queries aus character_creation.rs verwenden
let mut conn = pool 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.prepare("delete_character", QUERY_DELETE_CHARACTER)?;
conn.execute("delete_character", &[&character_id])?; conn.execute("delete_character", &[&character_id])?;
@@ -2040,6 +2060,13 @@ impl EventsWorker {
&format!("Erbe für Charakter {}", deceased_character_id), &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!( eprintln!(
"[EventsWorker] Erbe {} übernimmt Vermögen von Charakter {} (User {}): {:.2} (von {:.2} Gesamtvermögen, {} weitere Kinder)", "[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 heir_id, deceased_character_id, falukant_user_id, final_money, total_assets, child_count

View File

@@ -960,6 +960,13 @@ impl UserCharacterWorker {
} }
self.set_new_money(falukant_user_id, new_money)?; 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(()) Ok(())
} }