aboutsummaryrefslogtreecommitdiff
path: root/internal/game/journal_damage_regression_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 21:36:36 -0400
committerhistoria <[not public]>2026-09-18 21:36:36 -0400
commita8a67482bbff8297a96adff65d2fd7029ed998cc (patch)
treee84f8ba636c8f2edb4d2ff8b45d7cff7298fa7cc /internal/game/journal_damage_regression_test.go
parentff04d26ce90d6b411a04a2fb59f7c24ceefb161f (diff)
downloadthehouseoficarus-a8a67482bbff8297a96adff65d2fd7029ed998cc.tar.gz
refactor: replace economy journal with single-file pending-wager wallet
Diffstat (limited to 'internal/game/journal_damage_regression_test.go')
-rw-r--r--internal/game/journal_damage_regression_test.go123
1 files changed, 0 insertions, 123 deletions
diff --git a/internal/game/journal_damage_regression_test.go b/internal/game/journal_damage_regression_test.go
deleted file mode 100644
index d8577fe..0000000
--- a/internal/game/journal_damage_regression_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package game
-
-// Journal-damage regressions: a malformed record followed by other records
-// must abort the fold (the journal is preserved for inspection — records
-// beyond the damage may be owed money), an open failure is never "an empty
-// healthy journal", and compaction can never run over an untrusted fold.
-
-import (
- "os"
- "testing"
-)
-
-// TestMalformedJournalRecordAbortsFold: a malformed line followed by a valid
-// record makes loadFolded fail — skipping the line and continuing would let
-// compensation and compaction run over records that may owe money.
-func TestMalformedJournalRecordAbortsFold(t *testing.T) {
- j := newEconomyJournal(t.TempDir())
- original := []byte("{\"id\":\"damaged\",\"kind\":\"payout\"\n{\"id\":\"valid\",\"kind\":\"payout\",\"status\":\"open\",\"credit\":20}\n")
- if err := os.WriteFile(j.path, original, 0600); err != nil {
- t.Fatal(err)
- }
- if _, err := j.loadFolded(); err == nil {
- t.Fatal("malformed mid-file record silently accepted")
- }
- // Compaction must abort and preserve the journal verbatim.
- j.compact()
- after, err := os.ReadFile(j.path)
- if err != nil {
- t.Fatal(err)
- }
- if string(after) != string(original) {
- t.Errorf("compaction rewrote a damaged journal: %s", after)
- }
-}
-
-// TestTornFinalJournalLineIsTolerated: a torn LAST line (crash mid-append)
-// is skipped — every valid record before it still folds.
-func TestTornFinalJournalLineIsTolerated(t *testing.T) {
- j := newEconomyJournal(t.TempDir())
- data := []byte("{\"id\":\"a\",\"kind\":\"wager\",\"status\":\"open\"}\n{\"id\":\"b\",\"kind\":\"payo")
- if err := os.WriteFile(j.path, data, 0600); err != nil {
- t.Fatal(err)
- }
- folded, err := j.loadFolded()
- if err != nil {
- t.Fatalf("torn final line rejected a healthy fold: %v", err)
- }
- if len(folded) != 1 || folded[0].rec.ID != "a" {
- t.Fatalf("unexpected fold: %+v", folded)
- }
-}
-
-// TestJournalOpenErrorIsNotAnEmptyFold: an open failure (permissions, a
-// symlink loop) must be an error, never "no journal" — recovery would
-// otherwise treat owed money as never-charged.
-func TestJournalOpenErrorIsNotAnEmptyFold(t *testing.T) {
- j := newEconomyJournal(t.TempDir())
- if err := os.Symlink(j.path, j.path); err != nil {
- t.Fatal(err)
- }
- if _, err := j.loadFolded(); err == nil {
- t.Error("ELOOP open failure reported as an empty healthy journal")
- }
-}
-
-// TestOpenFailureAbortsRecovery: recoverEconomy over a journal that cannot
-// be fully read (malformed record with later records present) must abort
-// without paying owed money or compacting.
-func TestOpenFailureAbortsRecovery(t *testing.T) {
- h := newHarness(t)
- h.seedAccount("alice")
- p := h.seedCharacter("alice", "alice")
- h.g.RunOnOwner(func() {
- p.Credits = 100
- if err := h.g.AccountStore.SaveCharacter(p); err != nil {
- t.Error(err)
- return
- }
- // Corrupt the journal mid-file (a damaged record followed by an
- // owed payout — appends after a torn merged line), then attempt
- // recovery.
- f, err := os.OpenFile(h.g.Economy.path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
- if err != nil {
- t.Error(err)
- return
- }
- if _, werr := f.WriteString("{\"id\":\"damaged\",\"kind\":\"payout\"\n"); werr != nil {
- t.Error(werr)
- }
- f.Close()
- if _, err := h.g.Economy.start("payout", p, 0, 20, 100, 120, 0, 0); err != nil {
- t.Errorf("owed payout could not be journaled: %v", err)
- return
- }
- h.g.recoverEconomy()
- // The damaged fold aborted recovery: no compensation ran and no
- // compaction erased the records.
- loaded, err := h.g.AccountStore.LoadCharacter("alice")
- if err != nil {
- t.Error(err)
- return
- }
- if loaded.Credits != 100 {
- t.Errorf("recovery compensated over a damaged journal: credits=%d, want 100 (preserved for inspection)", loaded.Credits)
- }
- data, err := os.ReadFile(h.g.Economy.path)
- if err != nil {
- t.Fatal(err)
- }
- if !containsBytes(data, "kind\":\"payout\"") {
- t.Error("recovery compacted a damaged journal, erasing records preserved for inspection")
- }
- })
-}
-
-func containsBytes(data []byte, s string) bool {
- for i := 0; i+len(s) <= len(data); i++ {
- if string(data[i:i+len(s)]) == s {
- return true
- }
- }
- return false
-}