clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

feat/app-decomposition
puzzletea / session / session_test.go
session_test.go 3.6K
1package session23import (4 "path/filepath"5 "testing"6 "time"78 "github.com/FelineStateMachine/puzzletea/games/lightsout"9 "github.com/FelineStateMachine/puzzletea/store"10)1112func TestNormalizeSeed(t *testing.T) {13 tests := []struct {14 input string15 want string16 }{17 {input: "daily-challenge", want: "daily-challenge"},18 {input: "DailyChallenge", want: "dailyChallenge"},19 {input: "Week 01-2026 - #01", want: "week 01-2026 - #01"},20 {input: "seed-123", want: "seed-123"},21 }2223 for _, tt := range tests {24 if got := NormalizeSeed(tt.input); got != tt.want {25 t.Fatalf("NormalizeSeed(%q) = %q, want %q", tt.input, got, tt.want)26 }27 }28}2930func TestSeededNameDeterministic(t *testing.T) {31 seed := "zebra-seed"32 if got, want := SeededName(seed), SeededName(seed); got != want {33 t.Fatalf("SeededName(%q) = %q then %q", seed, got, want)34 }35}3637func TestSeededNameForGameDeterministicAndDistinct(t *testing.T) {38 seed := "zebra-seed"39 got := SeededNameForGame(seed, "Sudoku")40 if want := SeededNameForGame(seed, "Sudoku"); got != want {41 t.Fatalf("SeededNameForGame returned %q then %q", got, want)42 }43 if got == SeededName(seed) {44 t.Fatalf("SeededNameForGame(%q) should differ from SeededName(%q)", seed, seed)45 }46 if got == SeededNameForGame(seed, "Nonogram") {47 t.Fatal("different selected games should produce different seeded names")48 }49}5051func TestImportRecordReappliesTitle(t *testing.T) {52 g, err := lightsout.New(3, 3)53 if err != nil {54 t.Fatal(err)55 }56 save, err := g.GetSave()57 if err != nil {58 t.Fatal(err)59 }6061 loaded, err := ImportRecord(&store.GameRecord{62 Name: "saved-game",63 GameType: "Lights Out",64 SaveState: string(save),65 })66 if err != nil {67 t.Fatalf("ImportRecord returned error: %v", err)68 }69 if loaded == nil {70 t.Fatal("ImportRecord returned nil game")71 }72}7374func TestCreateRecordAndResumeAbandonedDeterministicRecord(t *testing.T) {75 s := openSessionTestStore(t)7677 g, err := lightsout.New(3, 3)78 if err != nil {79 t.Fatal(err)80 }81 rec, err := CreateRecord(s, g, "demo", "Lights Out", "Easy", store.NormalRunMetadata())82 if err != nil {83 t.Fatalf("CreateRecord returned error: %v", err)84 }85 if rec.ID == 0 {86 t.Fatal("CreateRecord did not populate ID")87 }8889 rec.Status = store.StatusAbandoned90 if err := s.UpdateStatus(rec.ID, store.StatusAbandoned); err != nil {91 t.Fatal(err)92 }93 if err := ResumeAbandonedDeterministicRecord(s, rec); err != nil {94 t.Fatalf("ResumeAbandonedDeterministicRecord returned error: %v", err)95 }9697 saved, err := s.GetDailyGame(rec.Name)98 if err != nil {99 t.Fatal(err)100 }101 if saved == nil {102 t.Fatal("expected saved record to exist")103 }104 if saved.Status != store.StatusInProgress {105 t.Fatalf("status = %q, want %q", saved.Status, store.StatusInProgress)106 }107}108109func TestCreateRecordUsesExplicitRunMetadata(t *testing.T) {110 s := openSessionTestStore(t)111112 g, err := lightsout.New(3, 3)113 if err != nil {114 t.Fatal(err)115 }116117 day := time.Date(2026, 2, 14, 15, 30, 0, 0, time.Local)118 rec, err := CreateRecord(s, g, "plain-name", "Lights Out", "Easy", store.DailyRunMetadata(day))119 if err != nil {120 t.Fatalf("CreateRecord returned error: %v", err)121 }122 if rec.RunKind != store.RunKindDaily {123 t.Fatalf("RunKind = %q, want %q", rec.RunKind, store.RunKindDaily)124 }125 if rec.RunDate == nil {126 t.Fatal("RunDate = nil, want daily date")127 }128 if got, want := rec.RunDate.Format("2006-01-02"), "2026-02-14"; got != want {129 t.Fatalf("RunDate = %q, want %q", got, want)130 }131}132133func openSessionTestStore(t *testing.T) *store.Store {134 t.Helper()135 s, err := store.Open(filepath.Join(t.TempDir(), "session.db"))136 if err != nil {137 t.Fatal(err)138 }139 t.Cleanup(func() { _ = s.Close() })140 return s141}