From 39b1a6a1e8fd0edff41df5feaf0094146bd70762 Mon Sep 17 00:00:00 2001 From: Bel LaPointe <153096461+breel-render@users.noreply.github.com> Date: Sun, 15 Dec 2024 01:15:36 -0700 Subject: [PATCH] a test! --- cmd/server/db.go | 5 -- cmd/server/db_test.go | 95 ++++++++++++++++++++++++++++++++++ cmd/server/games.go | 54 +++++++++++++++----- cmd/server/games_test.go | 108 +++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 + 6 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 cmd/server/db_test.go create mode 100644 cmd/server/games_test.go diff --git a/cmd/server/db.go b/cmd/server/db.go index ca10fdd..a0223d6 100644 --- a/cmd/server/db.go +++ b/cmd/server/db.go @@ -3,7 +3,6 @@ package main import ( "context" "database/sql" - "io" "sync" "time" @@ -107,7 +106,3 @@ func (db DB) dial(ctx context.Context) (*sql.DB, error) { } return c, nil } - -func (db DB) GetParty(id string) (string, error) { - return "", io.EOF -} diff --git a/cmd/server/db_test.go b/cmd/server/db_test.go new file mode 100644 index 0000000..bff35bd --- /dev/null +++ b/cmd/server/db_test.go @@ -0,0 +1,95 @@ +package main + +import ( + "context" + "database/sql" + "path" + "testing" + "time" +) + +func newTestDB(t *testing.T) DB { + ctx, can := context.WithTimeout(context.Background(), time.Minute) + defer can() + conn := path.Join(t.TempDir(), "db") + + db, err := NewDB(ctx, "sqlite", conn) + if err != nil { + t.Fatal(err) + } + return db +} + +func TestDB(t *testing.T) { + ctx := context.Background() + db := newTestDB(t) + + t.Run("with lock", func(t *testing.T) { + var called [2]bool + if err := db.WithLock(func() error { + for i := range called { + if err := db.Query(ctx, func(rows *sql.Rows) error { + return rows.Scan(&called[i]) + }, `SELECT true`); err != nil { + t.Fatal(err) + } + } + return nil + }); err != nil { + t.Fatal(err) + } + if !called[0] { + t.Error(0) + } + if !called[1] { + t.Error(1) + } + }) + + t.Run("exec, query", func(t *testing.T) { + if err := db.Exec(ctx, ` + CREATE TABLE IF NOT EXISTS my_table ( + text TEXT, + datetime DATETIME, + number NUMBER + ) + `); err != nil { + t.Fatal(err) + } + + if err := db.Exec(ctx, ` + INSERT INTO my_table ( + text, + datetime, + number + ) VALUES (?, ?, ?) + `, "text", time.Now(), 1); err != nil { + t.Fatal(err) + } + + var text string + var datetime time.Time + var number int + if err := db.Query(ctx, func(rows *sql.Rows) error { + return rows.Scan(&text, &datetime, &number) + }, ` + SELECT + text, + datetime, + number + FROM my_table + `); err != nil { + t.Fatal(err) + } + + if text != "text" { + t.Error(text) + } + if datetime.IsZero() { + t.Error(datetime) + } + if number != 1 { + t.Error(number) + } + }) +} diff --git a/cmd/server/games.go b/cmd/server/games.go index f919992..8851968 100644 --- a/cmd/server/games.go +++ b/cmd/server/games.go @@ -8,6 +8,8 @@ import ( "io" "slices" "time" + + "github.com/google/uuid" ) type Games struct { @@ -61,15 +63,15 @@ func (games Games) GameByName(ctx context.Context, uid, name string) (string, er err := games.db.Query(ctx, func(rows *sql.Rows) error { return rows.Scan(&result) }, ` - SELECT - players.games_uuid - FROM - players - JOIN games ON players.game_uuid=games.game_uuid - WHERE players.user_uuid=? AND games.name=? - ORDER BY games.timestamp DESC - LIMIT 1 - `, uid, name) + SELECT + players.game_uuid + FROM + players + JOIN games ON players.game_uuid=games.uuid + WHERE players.user_uuid=? AND games.name=? + ORDER BY games.updated DESC + LIMIT 1 + `, uid, name) return result, err } @@ -209,6 +211,32 @@ func (games Games) GameState(ctx context.Context, id string) (GameState, error) return result, err } +func (games Games) CreateGame(ctx context.Context, name string) (string, error) { + var exists string + if err := games.db.Query(ctx, + func(rows *sql.Rows) error { + return rows.Scan(&exists) + }, + ` + SELECT uuid + FROM games + WHERE name=? AND completed IS NULL + `, name); err != nil { + return "", err + } else if exists != "" { + return exists, nil + } + + id := uuid.New().String() + return id, games.db.Exec(ctx, ` + INSERT INTO games ( + uuid, + updated, + name + ) VALUES (?, ?, ?) + `, id, time.Now(), name) +} + func (games Games) CreateEventPlayerJoin(ctx context.Context, id string, player string) error { return games.createEvent(ctx, id, EventPlayerJoin{ID: player}) } @@ -217,10 +245,6 @@ func (games Games) CreateEventPlayerLeave(ctx context.Context, id string, player return games.createEvent(ctx, id, EventPlayerLeave{ID: player}) } -func (games Games) CreateEventGameComplete(ctx context.Context, id string) error { - return games.createEvent(ctx, id, EventGameComplete{}) -} - func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string, killer, killed, killWord string) error { // TODO gather current assignees // TODO get victim's target @@ -230,6 +254,10 @@ func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string, //return games.createEvent(ctx, id, v) } +func (games Games) CreateEventGameComplete(ctx context.Context, id string) error { + return games.createEvent(ctx, id, EventGameComplete{}) +} + func (games Games) createEvent(ctx context.Context, id string, v any) error { payload, err := json.Marshal(v) if err != nil { diff --git a/cmd/server/games_test.go b/cmd/server/games_test.go new file mode 100644 index 0000000..9d55e0b --- /dev/null +++ b/cmd/server/games_test.go @@ -0,0 +1,108 @@ +package main + +import ( + "context" + "fmt" + "testing" +) + +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"); 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); err != nil { + t.Fatal(p, "err creating event player join", err) + } + } + + if err := games.CreateEventAssignmentRotation(ctx, id, "", "", ""); err != nil { + t.Fatal("err creating rotation:", err) + } + + 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].KillWords.Global == "" { + 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") + } + } + } + }) +} diff --git a/go.mod b/go.mod index e2b43e3..47af02c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/glebarez/sqlite v1.11.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-isatty v0.0.17 // indirect diff --git a/go.sum b/go.sum index 7ba098b..9a428db 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GM github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=