package repos import ( "context" "gralias/models" "os" "testing" "time" "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" _ "github.com/mattn/go-sqlite3" ) func TestWordCardsRepo_Create(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) ctx := context.Background() roomID := "test_room_1" // Insert a room first, as word_cards has a foreign key to rooms _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) // Test single card creation card1 := &models.WordCard{ RoomID: roomID, Word: "apple", Color: models.WordColorRed, Revealed: false, Mime: false, } _, err = db.Exec(`INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, card1.RoomID, card1.Word, card1.Color, card1.Revealed, card1.Mime) assert.NoError(t, err) var count int err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 1, count) // Test batch card creation with transaction commit tx, err := db.BeginTxx(ctx, nil) assert.NoError(t, err) card2 := &models.WordCard{ RoomID: roomID, Word: "banana", Color: models.WordColorBlue, Revealed: false, Mime: false, } card3 := &models.WordCard{ RoomID: roomID, Word: "cherry", Color: models.WordColorBlack, Revealed: false, Mime: false, } _, err = tx.Exec(`INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, card2.RoomID, card2.Word, card2.Color, card2.Revealed, card2.Mime) assert.NoError(t, err) _, err = tx.Exec(`INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, card3.RoomID, card3.Word, card3.Color, card3.Revealed, card3.Mime) assert.NoError(t, err) // Before commit, count should not reflect changes if using a transaction context err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 1, count) // Should still be 1 if not committed err = tx.Commit() assert.NoError(t, err) // After commit, count should reflect changes err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 3, count) // Test transaction rollback tx2, err := db.BeginTxx(ctx, nil) assert.NoError(t, err) card4 := &models.WordCard{ RoomID: roomID, Word: "date", Color: models.WordColorWhite, Revealed: false, Mime: false, } _, err = tx2.Exec(`INSERT INTO word_cards (room_id, word, color, revealed, mime_view) VALUES (?, ?, ?, ?, ?)`, card4.RoomID, card4.Word, card4.Color, card4.Revealed, card4.Mime) assert.NoError(t, err) err = tx2.Rollback() assert.NoError(t, err) // After rollback, count should not reflect changes err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 3, count) } func TestWordCardsRepo_GetByWordAndRoomID(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) repo := &RepoProvider{DB: db} ctx := context.Background() roomID := "test_room_3" _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) card := &models.WordCard{ RoomID: roomID, Word: "gamma", Color: models.WordColorRed, Revealed: false, Mime: false, } err = repo.WordCardsCreate(ctx, card) assert.NoError(t, err) retrievedCard, err := repo.WordCardGetByWordAndRoomID(ctx, "gamma", roomID) assert.NoError(t, err) assert.NotNil(t, retrievedCard) assert.Equal(t, "gamma", retrievedCard.Word) assert.Equal(t, roomID, retrievedCard.RoomID) // Test non-existent card _, err = repo.WordCardGetByWordAndRoomID(ctx, "non_existent", roomID) assert.Error(t, err) } func TestWordCardsRepo_Reveal(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) repo := &RepoProvider{DB: db} ctx := context.Background() roomID := "test_room_4" _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) card := &models.WordCard{ RoomID: roomID, Word: "delta", Color: models.WordColorRed, Revealed: false, Mime: false, } err = repo.WordCardsCreate(ctx, card) assert.NoError(t, err) // Verify initial state var revealed bool err = db.Get(&revealed, "SELECT revealed FROM word_cards WHERE word = ? AND room_id = ?", "delta", roomID) assert.NoError(t, err) assert.False(t, revealed) // Reveal the card err = repo.WordCardReveal(ctx, "delta", roomID) assert.NoError(t, err) // Verify revealed state err = db.Get(&revealed, "SELECT revealed FROM word_cards WHERE word = ? AND room_id = ?", "delta", roomID) assert.NoError(t, err) assert.True(t, revealed) } func TestWordCardsRepo_RevealAll(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) repo := &RepoProvider{DB: db} ctx := context.Background() roomID := "test_room_5" _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) cardsToInsert := []*models.WordCard{ { RoomID: roomID, Word: "echo", Color: models.WordColorRed, Revealed: false, Mime: false, }, { RoomID: roomID, Color: models.WordColorBlue, Revealed: false, Mime: false, }, } for _, card := range cardsToInsert { err = repo.WordCardsCreate(ctx, card) assert.NoError(t, err) } // Verify initial state var count int err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ? AND revealed = FALSE", roomID) assert.NoError(t, err) assert.Equal(t, 2, count) // Reveal all cards err = repo.WordCardsRevealAll(ctx, roomID) assert.NoError(t, err) // Verify all cards are revealed err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ? AND revealed = FALSE", roomID) assert.NoError(t, err) assert.Equal(t, 0, count) } func TestWordCardsRepo_DeleteByRoomID(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) repo := &RepoProvider{DB: db} ctx := context.Background() roomID := "test_room_6" _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) cardsToInsert := []*models.WordCard{ { RoomID: roomID, Word: "golf", Color: models.WordColorRed, Revealed: false, Mime: false, }, { RoomID: roomID, Word: "hotel", Color: models.WordColorBlue, Revealed: false, Mime: false, }, } for _, card := range cardsToInsert { err = repo.WordCardsCreate(ctx, card) assert.NoError(t, err) } // Verify initial state var count int err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 2, count) // Delete cards by room ID err = repo.WordCardsDeleteByRoomID(ctx, roomID) assert.NoError(t, err) // Verify cards are deleted err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 0, count) } func TestWordCardsRepo_CascadeDeleteRoom(t *testing.T) { // Setup temporary file-based SQLite database for this test tempFile, err := os.CreateTemp("", "test_db_*.db") assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) db, err := sqlx.Connect("sqlite3", tempFile.Name()) assert.NoError(t, err) defer db.Close() // Enable foreign key constraints _, err = db.Exec("PRAGMA foreign_keys = ON;") assert.NoError(t, err) // Apply schema schema := ` CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, creator_name TEXT NOT NULL ); 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 ); ` _, err = db.Exec(schema) assert.NoError(t, err) repo := &RepoProvider{DB: db} ctx := context.Background() roomID := "test_room_7" _, err = db.Exec(`INSERT INTO rooms (id, created_at, creator_name) VALUES (?, ?, ?)`, roomID, time.Now(), "test_creator") assert.NoError(t, err) card := &models.WordCard{ RoomID: roomID, Word: "india", Color: models.WordColorRed, Revealed: false, Mime: false, } err = repo.WordCardsCreate(ctx, card) assert.NoError(t, err) var count int err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 1, count) _, err = db.Exec(`DELETE FROM rooms WHERE id = ?`, roomID) assert.NoError(t, err) err = db.Get(&count, "SELECT COUNT(*) FROM word_cards WHERE room_id = ?", roomID) assert.NoError(t, err) assert.Equal(t, 0, count) }