diff options
| author | historia <[not public]> | 2026-09-18 21:36:36 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-18 21:36:36 -0400 |
| commit | a8a67482bbff8297a96adff65d2fd7029ed998cc (patch) | |
| tree | e84f8ba636c8f2edb4d2ff8b45d7cff7298fa7cc /internal/game/economy_recovery_identity_test.go | |
| parent | ff04d26ce90d6b411a04a2fb59f7c24ceefb161f (diff) | |
| download | thehouseoficarus-a8a67482bbff8297a96adff65d2fd7029ed998cc.tar.gz | |
refactor: replace economy journal with single-file pending-wager wallet
Diffstat (limited to 'internal/game/economy_recovery_identity_test.go')
| -rw-r--r-- | internal/game/economy_recovery_identity_test.go | 128 |
1 files changed, 0 insertions, 128 deletions
diff --git a/internal/game/economy_recovery_identity_test.go b/internal/game/economy_recovery_identity_test.go deleted file mode 100644 index 04d6fed..0000000 --- a/internal/game/economy_recovery_identity_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package game - -// Identity-resolution regressions for economy recovery: a record carrying a -// stable character ID must never be resolved — paid or resolved — through a -// display name that a different character later reused, and money recovery -// must run only AFTER lifecycle-journal rollback (a rolled-back rename -// restores an older character before-image that must not erase compensation -// already applied). - -import ( - "os" - "path/filepath" - "testing" - - "thehouseoficarus/internal/persistence" -) - -// TestRecoveryDoesNotPayReusedCharacterName: an owed payout journaled for -// identity A (named "alice") must not be paid to identity B after B reuses -// the name, and A's unresolved liability must stay open for operator -// inspection instead of being silently settled against B's balance. -func TestRecoveryDoesNotPayReusedCharacterName(t *testing.T) { - h := newHarness(t) - h.seedAccount("alice") - p := h.seedCharacter("alice", "alice") - h.g.RunOnOwner(func() { - p.ID = "original-character" - p.Credits = 90 - if err := h.g.AccountStore.SaveCharacter(p); err != nil { - t.Error(err) - return - } - // A failed payout save left the owed money journaled for recovery. - if _, err := h.g.Economy.start("payout", p, 0, 20, 90, 110, 0, 0); err != nil { - t.Error(err) - return - } - // Delete the original character, then reuse its name for a - // different identity. - p.ID = "replacement-character" - if err := h.g.AccountStore.SaveCharacter(p); err != nil { - t.Error(err) - return - } - h.g.recoverEconomy() - }) - loaded, err := h.g.AccountStore.LoadCharacter("alice") - if err != nil { - t.Fatal(err) - } - if loaded.Credits != 90 { - t.Errorf("replacement identity received someone else's payout: credits=%d, want 90", loaded.Credits) - } - if open := h.g.Economy.mustLoadOpen(t); len(open) != 1 { - t.Errorf("original identity's unresolved liability was discarded: open records=%d", len(open)) - } -} - -// TestEconomyRecoveryRunsAfterLifecycleRollback: a leftover lifecycle -// transaction (a rename that never reached its commit point) restores the -// character's before-image at startup, and must do so BEFORE money -// compensation — otherwise the compensation's settled/compacted records are -// gone and the rollback silently erases a payout that was already applied. -func TestEconomyRecoveryRunsAfterLifecycleRollback(t *testing.T) { - h := newHarness(t) - h.seedAccount("alice") - p := h.seedCharacter("alice", "alice") - h.g.RunOnOwner(func() { - p.ID = "stable-character" - p.Credits = 90 - if err := h.g.AccountStore.SaveCharacter(p); err != nil { - t.Error(err) - return - } - // A failed payout save left the owed money journaled for recovery. - if _, err := h.g.Economy.start("payout", p, 0, 20, 90, 110, 0, 0); err != nil { - t.Error(err) - return - } - // Simulate a crash mid-rename: apply the move and the renamed - // character file directly, then leave the lifecycle journal on - // disk so the next startup rolls it back. - oldPath := h.g.AccountStore.CharPath("alice") - newPath := h.g.AccountStore.CharPath("renamed") - txn := persistence.NewTransaction(h.g.journalDir()) - if err := txn.Move(oldPath, newPath); err != nil { - t.Error(err) - return - } - p.Name = "renamed" - data, err := h.g.AccountStore.MarshalCharacter(p) - if err != nil { - t.Error(err) - return - } - if err := txn.Replace(newPath, data, 0600); err != nil { - t.Error(err) - return - } - journal, err := txn.MarshalJournal() - if err != nil { - t.Error(err) - return - } - if err := os.WriteFile(filepath.Join(h.g.journalDir(), "txn-audit.json"), journal, 0600); err != nil { - t.Error(err) - return - } - if err := os.Rename(oldPath, newPath); err != nil { - t.Error(err) - return - } - if err := h.g.AccountStore.SaveCharacter(p); err != nil { - t.Error(err) - return - } - }) - // A fresh Game over the saved files simulates the restart. - restarted := harnessFromDir(t, h.dir) - loaded, err := restarted.g.AccountStore.LoadCharacter("alice") - if err != nil { - t.Fatal(err) - } - if loaded.Credits != 110 { - t.Errorf("lifecycle rollback erased recovered payout: credits=%d, want 110; open liabilities=%d", - loaded.Credits, len(restarted.g.Economy.mustLoadOpen(t))) - } -} |
