package main import ( "context" "encoding/json" "fmt" "testing" "time" ) func newTestGames(t *testing.T) Games { db := newTestDB(t) games, err := NewGames(context.Background(), db) if err != nil { t.Fatal(err) } return games } func TestGames(t *testing.T) { ctx := context.Background() t.Run("empty", func(t *testing.T) { games := newTestGames(t) if v, err := games.GamesForUser(ctx, ""); err != nil { t.Error("err getting games for empty user:", err) } else if len(v) > 0 { t.Error(v) } if v, err := games.GameByName(ctx, "", ""); err != nil { t.Error("err getting game by empty name for empty user:", err) } else if len(v) > 0 { t.Error(v) } if v, err := games.GameState(ctx, ""); err != nil { t.Error("err getting game state for empty:", err) } else if len(v.Players) > 0 || !v.Completed.IsZero() { t.Error(v) } }) t.Run("mvp", func(t *testing.T) { games := newTestGames(t) id, err := games.CreateGame(ctx, "g1") if err != nil { t.Fatal("err creating game:", err) } else if id2, err := games.CreateGame(ctx, "g1"); err != nil { t.Fatal("err creating game redundantly:", err) } else if id != id2 { t.Fatal("redundant create game didnt return same id:", id2) } if err := games.CreateEventPlayerJoin(ctx, id, "p0", "player zero"); err != nil { t.Fatal("err creating event player join:", err) } else if err := games.CreateEventPlayerLeave(ctx, id, "p0"); err != nil { t.Fatal("err creating event player leave:", err) } for i := 0; i < 4; i++ { p := fmt.Sprintf("p%d", i+1) if err := games.CreateEventPlayerJoin(ctx, id, p, "player "+p); err != nil { t.Fatal(p, "err creating event player join", err) } if name, err := games.UserName(ctx, p); err != nil { t.Fatal(p, "err getting user name", err) } else if name != "player "+p { t.Fatal("name wrong", name) } if err := games.UpdateUserName(ctx, p, "player! "+p); err != nil { t.Fatal(p, "failed to rename:", err) } else if name, err := games.UserName(ctx, p); err != nil { t.Fatal(p, "err getting user name", err) } else if name != "player! "+p { t.Fatal("updated name wrong", name) } } if events, err := games.GameEvents(ctx, id, time.Time{}); err != nil { t.Fatal("failed to get player join, leave events:", err) } else if len(events) != 6 { t.Error("wrong number of events:", len(events)) } now := time.Now() if err := games.CreateEventAssignmentRotation(ctx, id, "", "", "", 1); err != nil { t.Fatal("err creating rotation:", err) } if events, err := games.GameEvents(ctx, id, time.Time{}); err != nil { t.Fatal("failed to get player join, leave events:", err) } else if len(events) != 7 { t.Error("wrong number of events:", len(events)) } else if events, err = games.GameEvents(ctx, id, now); err != nil { t.Fatal("failed to get assignment rotation event:", err) } else if len(events) != 1 { t.Error("wrong number of events:", len(events)) } else if _, ok := events[0].(EventAssignmentRotation); !ok { t.Errorf("not an assignment rotation event: %T", events[0]) } if v, err := games.GamesForUser(ctx, "p1"); err != nil { t.Error("err getting games for user:", err) } else if len(v) < 1 { t.Error("no games found for user:", v) } else if v[0] != id { t.Error("wrong game found for user:", v) } if v, err := games.GameByName(ctx, "p1", "g1"); err != nil { t.Error("err getting game by name for user:", err) } else if v != id { t.Error("wrong game by name for user:", v) } if v, err := games.GameState(ctx, id); err != nil { t.Error("err getting game state:", err) } else if len(v.Players) != 4 || !v.Completed.IsZero() { t.Error("wrong game state:", v) } else { for i := 0; i < 4; i++ { p := fmt.Sprintf("p%d", i+1) if v.Players[p].Points() != 0 { t.Error("nonzero points after zero kills:", v.Players[p].Points()) } if v.Players[p].KillWords.Global.Word == "" { t.Error(p, "no killwords.global") } else if v.Players[p].KillWords.Assigned.IsZero() { t.Error(p, "no killwords.assigned") } else if v.Players[p].KillWords.Assignee == "" { t.Error(p, "no killwords.assignee") } else if len(v.Players[p].KillWords.Assignment.Public) == 0 { t.Error(p, "no killwords.assigment.public") } else if len(v.Players[p].KillWords.Assignment.Private) == 0 { t.Error(p, "no killwords.assigment.private") } } } if err := games.CreateEventGameComplete(ctx, id); err != nil { t.Fatal("err creating game complete:", err) } else if state, err := games.GameState(ctx, id); err != nil { t.Fatal("err fetching state after completing:", err) } else if state.Completed.IsZero() { t.Fatal("state.Completed is zero") } }) } func TestParseEvent(t *testing.T) { now := time.Now() cases := map[string]Event{ "player join": EventPlayerJoin{ Type: PlayerJoin, ID: "x", }, "player leave": EventPlayerLeave{ Type: PlayerLeave, ID: "x", }, "game complete": EventGameComplete{ Type: GameComplete, }, "assignment rotation": EventAssignmentRotation{ Type: AssignmentRotation, Killer: "x", Victim: "y", KillWord: KillWord{ Word: "word", Points: 1, }, AllKillWords: map[string]KillWords{ "x": KillWords{ Global: KillWord{ Word: "a", Points: -1, }, Assignee: "z", Assigned: now, Assignment: Assignment{ Public: []KillWord{{ Word: "word2", Points: 2, }}, Private: []KillWord{{ Word: "word3", Points: 3, }}, }, }, }, }, } for name, d := range cases { c := d t.Run(name, func(t *testing.T) { c := EventWithTime(c, now) b, _ := json.Marshal(c) got, err := parseEvent(b, now) if err != nil { t.Fatal(err) } gotb, _ := json.Marshal(got) if string(b) != string(gotb) { t.Errorf("expected (%T) %+v, but got (%T) %+v", c, c, got, got) } }) } }