From 788c4efd9e7000615d688d3aa882c166864d9cde Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Thu, 3 Jul 2025 15:00:06 +0300 Subject: [PATCH] Feat: word card repo --- migrations/001_initial_schema.up.sql | 14 +++--- repos/main.go | 1 + repos/rooms.go | 4 -- repos/rooms_test.go | 71 +++++++++++++++++++++++++++- repos/word_cards.go | 59 +++++++++++++++++++++++ 5 files changed, 137 insertions(+), 12 deletions(-) create mode 100644 repos/word_cards.go diff --git a/migrations/001_initial_schema.up.sql b/migrations/001_initial_schema.up.sql index 6200e2a..dc866c5 100644 --- a/migrations/001_initial_schema.up.sql +++ b/migrations/001_initial_schema.up.sql @@ -24,7 +24,7 @@ CREATE TABLE players ( team TEXT NOT NULL DEFAULT '', -- 'red' or 'blue' role TEXT NOT NULL DEFAULT '', -- 'guesser' or 'mime' is_bot BOOLEAN NOT NULL DEFAULT FALSE, - FOREIGN KEY (room_id) REFERENCES rooms(id) + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE ); CREATE TABLE word_cards ( @@ -34,7 +34,7 @@ CREATE TABLE word_cards ( color TEXT NOT NULL DEFAULT '', revealed BOOLEAN NOT NULL DEFAULT FALSE, mime_view BOOLEAN NOT NULL DEFAULT FALSE, - FOREIGN KEY (room_id) REFERENCES rooms(id) + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE ); CREATE TABLE card_marks ( @@ -42,7 +42,7 @@ CREATE TABLE card_marks ( card_id INTEGER NOT NULL, username TEXT NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, - FOREIGN KEY (card_id) REFERENCES word_cards(id) + FOREIGN KEY (card_id) REFERENCES word_cards(id) ON DELETE CASCADE ); CREATE TABLE actions ( @@ -55,7 +55,7 @@ CREATE TABLE actions ( word_color TEXT NOT NULL DEFAULT '', number_associated TEXT NOT NULL DEFAULT '', -- for clues created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (room_id) REFERENCES rooms(id) + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE ); CREATE TABLE settings ( @@ -65,7 +65,7 @@ CREATE TABLE settings ( room_pass TEXT NOT NULL DEFAULT '', turn_time INTEGER NOT NULL DEFAULT 60, -- seconds created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (room_id) REFERENCES rooms(id) + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE ); CREATE TABLE sessions( @@ -74,5 +74,5 @@ CREATE TABLE sessions( lifetime INTEGER NOT NULL DEFAULT 3600, token_key TEXT NOT NULL DEFAULT '' UNIQUE, -- encoded value username TEXT NOT NULL, - FOREIGN KEY (username) REFERENCES players(username) -); + FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE +); \ No newline at end of file diff --git a/repos/main.go b/repos/main.go index 30ba722..30bb096 100644 --- a/repos/main.go +++ b/repos/main.go @@ -16,6 +16,7 @@ type AllRepos interface { ActionsRepo PlayersRepo SessionsRepo + WordCardsRepo } type RepoProvider struct { diff --git a/repos/rooms.go b/repos/rooms.go index 7ff841e..89cb5d7 100644 --- a/repos/rooms.go +++ b/repos/rooms.go @@ -53,10 +53,6 @@ func (p *RepoProvider) RoomCreate(ctx context.Context, r *models.Room) error { func (p *RepoProvider) RoomDeleteByID(ctx context.Context, id string) error { db := getDB(ctx, p.DB) _, err := db.ExecContext(ctx, `DELETE FROM rooms WHERE id = ?`, id) - if err != nil { - return err - } - _, err = db.ExecContext(ctx, `DELETE FROM settings WHERE room_id = ?`, id) return err } diff --git a/repos/rooms_test.go b/repos/rooms_test.go index b022e16..b5316de 100644 --- a/repos/rooms_test.go +++ b/repos/rooms_test.go @@ -15,6 +15,10 @@ func setupTestDB(t *testing.T) (*sqlx.DB, func()) { db, err := sqlx.Connect("sqlite3", ":memory:") assert.NoError(t, err) + // Enable foreign key constraints for SQLite + _, err = db.Exec("PRAGMA foreign_keys = ON;") + assert.NoError(t, err) + schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, @@ -33,6 +37,47 @@ func setupTestDB(t *testing.T) (*sqlx.DB, func()) { room_link TEXT NOT NULL DEFAULT '' ); + CREATE TABLE IF NOT EXISTS players ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + room_id TEXT, + username TEXT NOT NULL UNIQUE, + team TEXT NOT NULL DEFAULT '', + role TEXT NOT NULL DEFAULT '', + is_bot BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS word_cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + room_id TEXT NOT NULL, + word TEXT NOT NULL, + color TEXT NOT NULL DEFAULT '', + revealed BOOLEAN NOT NULL DEFAULT FALSE, + mime_view BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS card_marks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + card_id INTEGER NOT NULL, + username TEXT NOT NULL, + active BOOLEAN NOT NULL DEFAULT TRUE, + FOREIGN KEY (card_id) REFERENCES word_cards(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS actions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + room_id TEXT NOT NULL, + actor TEXT NOT NULL, + actor_color TEXT NOT NULL DEFAULT '', + action_type TEXT NOT NULL, + word TEXT NOT NULL DEFAULT '', + word_color TEXT NOT NULL DEFAULT '', + number_associated TEXT NOT NULL DEFAULT '', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS settings ( id INTEGER PRIMARY KEY AUTOINCREMENT, room_id TEXT NOT NULL, @@ -40,7 +85,16 @@ func setupTestDB(t *testing.T) (*sqlx.DB, func()) { room_pass TEXT NOT NULL DEFAULT '', turn_time INTEGER NOT NULL DEFAULT 60, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (room_id) REFERENCES rooms(id) + FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS sessions( + id INTEGER PRIMARY KEY AUTOINCREMENT, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + lifetime INTEGER NOT NULL DEFAULT 3600, + token_key TEXT NOT NULL DEFAULT '' UNIQUE, + username TEXT NOT NULL, + FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE ); ` _, err = db.Exec(schema) @@ -236,6 +290,17 @@ func TestRoomsRepo_DeleteRoomByID(t *testing.T) { _, err = db.Exec(`INSERT INTO settings (room_id, language, room_pass, turn_time) VALUES (?, ?, ?, ?)`, room.ID, room.Settings.Language, room.Settings.RoomPass, room.Settings.RoundTime) assert.NoError(t, err) + // Insert a word card for the room + wordCard := &models.WordCard{ + RoomID: room.ID, + Word: "test_word", + Color: models.WordColorBlue, + Revealed: false, + Mime: false, + } + _, err = db.Exec(`INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, wordCard.RoomID, wordCard.Word, wordCard.Color, wordCard.Revealed, wordCard.Mime) + assert.NoError(t, err) + err = repo.RoomDeleteByID(context.Background(), room.ID) assert.NoError(t, err) @@ -247,6 +312,10 @@ func TestRoomsRepo_DeleteRoomByID(t *testing.T) { err = db.Get(&count, "SELECT COUNT(*) FROM settings WHERE room_id = ?", room.ID) assert.NoError(t, err) assert.Equal(t, 0, count) + + err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", room.ID) + assert.NoError(t, err) + assert.Equal(t, 0, count) } func TestRoomsRepo_UpdateRoom(t *testing.T) { diff --git a/repos/word_cards.go b/repos/word_cards.go new file mode 100644 index 0000000..b1ba286 --- /dev/null +++ b/repos/word_cards.go @@ -0,0 +1,59 @@ +package repos + +import ( + "context" + "gralias/models" + + "github.com/jmoiron/sqlx" +) + +type WordCardsRepo interface { + WordCardsCreate(ctx context.Context, card *models.WordCard) error + WordCardsGetByRoomID(ctx context.Context, roomID string) ([]models.WordCard, error) + WordCardGetByWordAndRoomID(ctx context.Context, word, roomID string) (*models.WordCard, error) + WordCardReveal(ctx context.Context, word, roomID string) error + WordCardsRevealAll(ctx context.Context, roomID string) error + WordCardsDeleteByRoomID(ctx context.Context, roomID string) error +} + +func (p *RepoProvider) WordCardsCreate(ctx context.Context, card *models.WordCard) error { + db := getDB(ctx, p.DB) + _, err := db.ExecContext(ctx, `INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, card.RoomID, card.Word, card.Color, card.Revealed, card.Mime) + return err +} + +func (p *RepoProvider) WordCardsGetByRoomID(ctx context.Context, roomID string) ([]models.WordCard, error) { + cards := []models.WordCard{} + err := sqlx.SelectContext(ctx, p.DB, &cards, `SELECT * FROM word_cards WHERE room_id = ?`, roomID) + if err != nil { + return nil, err + } + return cards, nil +} + +func (p *RepoProvider) WordCardGetByWordAndRoomID(ctx context.Context, word, roomID string) (*models.WordCard, error) { + card := &models.WordCard{} + err := sqlx.GetContext(ctx, p.DB, card, `SELECT * FROM word_cards WHERE word = ? AND room_id = ?`, word, roomID) + if err != nil { + return nil, err + } + return card, nil +} + +func (p *RepoProvider) WordCardReveal(ctx context.Context, word, roomID string) error { + db := getDB(ctx, p.DB) + _, err := db.ExecContext(ctx, `UPDATE word_cards SET revealed = TRUE WHERE word = ? AND room_id = ?`, word, roomID) + return err +} + +func (p *RepoProvider) WordCardsRevealAll(ctx context.Context, roomID string) error { + db := getDB(ctx, p.DB) + _, err := db.ExecContext(ctx, `UPDATE word_cards SET revealed = TRUE WHERE room_id = ?`, roomID) + return err +} + +func (p *RepoProvider) WordCardsDeleteByRoomID(ctx context.Context, roomID string) error { + db := getDB(ctx, p.DB) + _, err := db.ExecContext(ctx, `DELETE FROM word_cards WHERE room_id = ?`, roomID) + return err +} \ No newline at end of file