package db import ( "farga/db/types" "farga/lib" "testing" "github.com/google/uuid" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // opens an in-memory sqlite database with the same schema as production. func testDB(t *testing.T) *DB { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Fatalf("open sqlite: %v", err) } sqlDB, err := db.DB() if err != nil { t.Fatal(err) } sqlDB.SetMaxOpenConns(1) // keep the in-memory database on a single connection db.Exec("PRAGMA foreign_keys = ON;") migrate(db) return &DB{db} } func newTestUser(t *testing.T, handle string) *types.User { t.Helper() return &types.User{ Handle: handle, Name: "Test User", Email: handle + "@example.com", PasswordHash: "hash", // registration always creates a UserConfig, so do the same here. Config: types.UserConfig{GitDefaultBranch: "master"}, } } func TestUsers(t *testing.T) { db := testDB(t) hash, err := lib.GenerateHash("hunter2") if err != nil { t.Fatalf("GenerateHash: %v", err) } user := newTestUser(t, "poyo") user.PasswordHash = hash if err := db.NewUser(user); err != nil { t.Fatalf("NewUser: %v", err) } if user.ID == uuid.Nil { t.Error("NewUser did not assign an ID") } // login got, err := db.LoginUser("poyo@example.com", "hunter2") if err != nil { t.Fatalf("LoginUser: %v", err) } if got.ID != user.ID { t.Errorf("LoginUser returned user %v, want %v", got.ID, user.ID) } if _, err := db.LoginUser("poyo@example.com", "wrong"); err != IncorrectPassword { t.Errorf("LoginUser(wrong password) = %v, want IncorrectPassword", err) } if _, err := db.LoginUser("nobody@example.com", "hunter2"); err != UserNotFound { t.Errorf("LoginUser(unknown email) = %v, want UserNotFound", err) } // lookups byID, err := db.User(user.ID) if err != nil { t.Fatalf("User: %v", err) } if byID.Handle != "poyo" { t.Errorf("User().Handle = %q, want poyo", byID.Handle) } byHandle, err := db.UserHandle("poyo") if err != nil { t.Fatalf("UserHandle: %v", err) } if byHandle.ID != user.ID { t.Errorf("UserHandle returned %v, want %v", byHandle.ID, user.ID) } if _, err := db.UserHandle("nope"); err == nil { t.Error("UserHandle(unknown) = nil error, want error") } // hackatime token if err := db.SaveHackatimeToken(user.ID, "secret-token"); err != nil { t.Fatalf("SaveHackatimeToken: %v", err) } withToken, err := db.User(user.ID) if err != nil { t.Fatal(err) } if withToken.HackatimeToken != "secret-token" { t.Errorf("HackatimeToken = %q, want secret-token", withToken.HackatimeToken) } } func TestUserConfig(t *testing.T) { db := testDB(t) user := newTestUser(t, "poyo") if err := db.NewUser(user); err != nil { t.Fatalf("NewUser: %v", err) } if err := db.UpdateUserConfig(user.ID, types.UserConfig{GitDefaultBranch: "main"}); err != nil { t.Fatalf("UpdateUserConfig: %v", err) } cfg, err := db.UserConfig(user.ID) if err != nil { t.Fatalf("UserConfig: %v", err) } if cfg.GitDefaultBranch != "main" { t.Errorf("GitDefaultBranch = %q, want main", cfg.GitDefaultBranch) } } func TestSessions(t *testing.T) { db := testDB(t) user := newTestUser(t, "poyo") if err := db.NewUser(user); err != nil { t.Fatalf("NewUser: %v", err) } // anonymous session anon, err := db.NewSession(nil) if err != nil { t.Fatalf("NewSession(nil): %v", err) } if anon.UserID != nil { t.Errorf("anonymous session UserID = %v, want nil", anon.UserID) } userSession, err := db.NewSession(&user.ID) if err != nil { t.Fatalf("NewSession(user): %v", err) } if userSession.UserID == nil || *userSession.UserID != user.ID { t.Errorf("user session UserID = %v, want %v", userSession.UserID, user.ID) } got, err := db.Session(userSession.ID) if err != nil { t.Fatalf("Session: %v", err) } if got.ID != userSession.ID { t.Errorf("Session = %v, want %v", got.ID, userSession.ID) } // unknown session if _, err := db.Session(uuid.Must(uuid.NewV7())); err == nil { t.Error("Session(unknown) = nil error, want error") } if err := db.DeleteSession(userSession.ID.String()); err != nil { t.Fatalf("DeleteSession: %v", err) } if _, err := db.Session(userSession.ID); err == nil { t.Error("Session after DeleteSession = nil error, want error") } }