package repositories import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/treytartt/honeydue-api/internal/models" "github.com/treytartt/honeydue-api/internal/testutil" ) func TestUserRepository_FindByUsername_CaseInsensitive(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "TestUser", "test@example.com", "Password123") // Should find with different cases found, err := repo.FindByUsername("testuser") require.NoError(t, err) assert.Equal(t, "TestUser", found.Username) found, err = repo.FindByUsername("TESTUSER") require.NoError(t, err) assert.Equal(t, "TestUser", found.Username) } func TestUserRepository_FindByUsername_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindByUsername("nonexistent") assert.Error(t, err) assert.ErrorIs(t, err, ErrUserNotFound) } func TestUserRepository_FindByEmail_CaseInsensitive(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "testuser", "Test@Example.com", "Password123") found, err := repo.FindByEmail("test@example.com") require.NoError(t, err) assert.Equal(t, "Test@Example.com", found.Email) } func TestUserRepository_FindByEmail_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindByEmail("nonexistent@example.com") assert.Error(t, err) assert.ErrorIs(t, err, ErrUserNotFound) } func TestUserRepository_ExistsByUsername_CaseInsensitive(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "TestUser", "test@example.com", "Password123") exists, err := repo.ExistsByUsername("TESTUSER") require.NoError(t, err) assert.True(t, exists) } func TestUserRepository_ExistsByEmail_CaseInsensitive(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "testuser", "Test@Example.com", "Password123") exists, err := repo.ExistsByEmail("test@example.com") require.NoError(t, err) assert.True(t, exists) } func TestUserRepository_GetOrCreateToken(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") // Create token token1, err := repo.GetOrCreateToken(user.ID) require.NoError(t, err) assert.NotEmpty(t, token1.Key) // Should return same token token2, err := repo.GetOrCreateToken(user.ID) require.NoError(t, err) assert.Equal(t, token1.Key, token2.Key) } func TestUserRepository_FindTokenByKey(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") token, err := repo.GetOrCreateToken(user.ID) require.NoError(t, err) found, err := repo.FindTokenByKey(token.Key) require.NoError(t, err) assert.Equal(t, token.Key, found.Key) assert.Equal(t, user.ID, found.UserID) } func TestUserRepository_FindTokenByKey_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindTokenByKey("nonexistent-token-key") assert.Error(t, err) assert.ErrorIs(t, err, ErrTokenNotFound) } func TestUserRepository_DeleteToken(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") token, err := repo.GetOrCreateToken(user.ID) require.NoError(t, err) err = repo.DeleteToken(token.Key) require.NoError(t, err) _, err = repo.FindTokenByKey(token.Key) assert.ErrorIs(t, err, ErrTokenNotFound) } func TestUserRepository_DeleteToken_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) err := repo.DeleteToken("nonexistent-key") assert.ErrorIs(t, err, ErrTokenNotFound) } func TestUserRepository_DeleteTokenByUserID(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") _, err := repo.GetOrCreateToken(user.ID) require.NoError(t, err) err = repo.DeleteTokenByUserID(user.ID) require.NoError(t, err) // Token should be gone var count int64 db.Model(&models.AuthToken{}).Where("user_id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) } func TestUserRepository_CreateToken(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") token, err := repo.CreateToken(user.ID) require.NoError(t, err) assert.NotEmpty(t, token.Key) assert.Equal(t, user.ID, token.UserID) } func TestUserRepository_UpdateLastLogin(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") err := repo.UpdateLastLogin(user.ID) require.NoError(t, err) found, err := repo.FindByID(user.ID) require.NoError(t, err) assert.NotNil(t, found.LastLogin) } func TestUserRepository_UpdateProfile(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") profile, err := repo.GetOrCreateProfile(user.ID) require.NoError(t, err) profile.Bio = "Test bio" profile.PhoneNumber = "+1-555-0100" err = repo.UpdateProfile(profile) require.NoError(t, err) updated, err := repo.GetOrCreateProfile(user.ID) require.NoError(t, err) assert.Equal(t, "Test bio", updated.Bio) assert.Equal(t, "+1-555-0100", updated.PhoneNumber) } func TestUserRepository_SetProfileVerified(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") // Create profile _, err := repo.GetOrCreateProfile(user.ID) require.NoError(t, err) err = repo.SetProfileVerified(user.ID, true) require.NoError(t, err) profile, err := repo.GetOrCreateProfile(user.ID) require.NoError(t, err) assert.True(t, profile.Verified) } func TestUserRepository_FindByIDWithProfile(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") // Create profile first profile := &models.UserProfile{ UserID: user.ID, Bio: "My bio", } err := db.Create(profile).Error require.NoError(t, err) found, err := repo.FindByIDWithProfile(user.ID) require.NoError(t, err) assert.Equal(t, user.ID, found.ID) require.NotNil(t, found.Profile) assert.Equal(t, "My bio", found.Profile.Bio) } func TestUserRepository_FindByIDWithProfile_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindByIDWithProfile(9999) assert.Error(t, err) assert.ErrorIs(t, err, ErrUserNotFound) } func TestUserRepository_ConfirmationCode_Lifecycle(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") // Create confirmation code expiresAt := time.Now().UTC().Add(1 * time.Hour) code, err := repo.CreateConfirmationCode(user.ID, "123456", expiresAt) require.NoError(t, err) assert.NotZero(t, code.ID) // Find it found, err := repo.FindConfirmationCode(user.ID, "123456") require.NoError(t, err) assert.Equal(t, code.ID, found.ID) // Mark as used err = repo.MarkConfirmationCodeUsed(code.ID) require.NoError(t, err) // Should not find used code _, err = repo.FindConfirmationCode(user.ID, "123456") assert.Error(t, err) } func TestUserRepository_ConfirmationCode_InvalidatesExisting(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") expiresAt := time.Now().UTC().Add(1 * time.Hour) // Create first code code1, err := repo.CreateConfirmationCode(user.ID, "111111", expiresAt) require.NoError(t, err) // Create second code (should invalidate first) _, err = repo.CreateConfirmationCode(user.ID, "222222", expiresAt) require.NoError(t, err) // First code should be used/invalidated var c models.ConfirmationCode db.First(&c, code1.ID) assert.True(t, c.IsUsed) } func TestUserRepository_Transaction(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "testuser", "test@example.com", "Password123") err := repo.Transaction(func(txRepo *UserRepository) error { found, err := txRepo.FindByID(user.ID) if err != nil { return err } found.FirstName = "Updated" return txRepo.Update(found) }) require.NoError(t, err) found, err := repo.FindByID(user.ID) require.NoError(t, err) assert.Equal(t, "Updated", found.FirstName) } func TestUserRepository_DB(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) assert.NotNil(t, repo.DB()) } func TestUserRepository_FindByAppleID(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "appleuser", "apple@test.com", "Password123") appleAuth := &models.AppleSocialAuth{ UserID: user.ID, AppleID: "apple_sub_123", Email: "apple@test.com", } require.NoError(t, db.Create(appleAuth).Error) found, err := repo.FindByAppleID("apple_sub_123") require.NoError(t, err) assert.Equal(t, user.ID, found.UserID) } func TestUserRepository_FindByAppleID_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindByAppleID("nonexistent_apple_id") assert.ErrorIs(t, err, ErrAppleAuthNotFound) } func TestUserRepository_FindByGoogleID(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "googleuser", "google@test.com", "Password123") googleAuth := &models.GoogleSocialAuth{ UserID: user.ID, GoogleID: "google_sub_123", Email: "google@test.com", } require.NoError(t, db.Create(googleAuth).Error) found, err := repo.FindByGoogleID("google_sub_123") require.NoError(t, err) assert.Equal(t, user.ID, found.UserID) } func TestUserRepository_FindByGoogleID_NotFound(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) _, err := repo.FindByGoogleID("nonexistent_google_id") assert.ErrorIs(t, err, ErrGoogleAuthNotFound) } func TestUserRepository_CreateAndUpdateAppleSocialAuth(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "appleuser", "apple@test.com", "Password123") auth := &models.AppleSocialAuth{ UserID: user.ID, AppleID: "apple_sub_456", Email: "apple@test.com", } err := repo.CreateAppleSocialAuth(auth) require.NoError(t, err) assert.NotZero(t, auth.ID) auth.Email = "updated@test.com" err = repo.UpdateAppleSocialAuth(auth) require.NoError(t, err) found, err := repo.FindByAppleID("apple_sub_456") require.NoError(t, err) assert.Equal(t, "updated@test.com", found.Email) } func TestUserRepository_CreateAndUpdateGoogleSocialAuth(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) user := testutil.CreateTestUser(t, db, "googleuser", "google@test.com", "Password123") auth := &models.GoogleSocialAuth{ UserID: user.ID, GoogleID: "google_sub_456", Email: "google@test.com", Name: "Test User", } err := repo.CreateGoogleSocialAuth(auth) require.NoError(t, err) assert.NotZero(t, auth.ID) auth.Name = "Updated Name" err = repo.UpdateGoogleSocialAuth(auth) require.NoError(t, err) found, err := repo.FindByGoogleID("google_sub_456") require.NoError(t, err) assert.Equal(t, "Updated Name", found.Name) } func TestUserRepository_SearchUsers(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "john_doe", "john@example.com", "Password123") testutil.CreateTestUser(t, db, "jane_smith", "jane@example.com", "Password123") testutil.CreateTestUser(t, db, "bob_builder", "bob@example.com", "Password123") users, total, err := repo.SearchUsers("john", 10, 0) require.NoError(t, err) assert.Equal(t, int64(1), total) assert.Len(t, users, 1) assert.Equal(t, "john_doe", users[0].Username) } func TestUserRepository_ListUsers(t *testing.T) { db := testutil.SetupTestDB(t) repo := NewUserRepository(db) testutil.CreateTestUser(t, db, "user1", "user1@example.com", "Password123") testutil.CreateTestUser(t, db, "user2", "user2@example.com", "Password123") testutil.CreateTestUser(t, db, "user3", "user3@example.com", "Password123") users, total, err := repo.ListUsers(2, 0) require.NoError(t, err) assert.Equal(t, int64(3), total) assert.Len(t, users, 2) // Limited to 2 users, total, err = repo.ListUsers(2, 2) require.NoError(t, err) assert.Equal(t, int64(3), total) assert.Len(t, users, 1) // Only 1 remaining }