package user import ( "context" "os" "testing" "time" "dance-lessons-coach/pkg/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // createTestConfig creates a test configuration with telemetry disabled func createTestConfig() *config.Config { return &config.Config{ Telemetry: config.TelemetryConfig{ Enabled: false, Persistence: config.PersistenceTelemetryConfig{ Enabled: false, }, }, } } func TestSQLiteRepository(t *testing.T) { t.Run("CRUD operations", func(t *testing.T) { // Create a temporary database dbPath := "test_db.sqlite" defer os.Remove(dbPath) cfg := createTestConfig() repo, err := NewSQLiteRepository(dbPath, cfg) require.NoError(t, err) defer repo.Close() ctx := context.Background() // Test CreateUser user := &User{ Username: "testuser", PasswordHash: "hashedpassword", Description: ptrString("Test user"), CurrentGoal: ptrString("Learn to dance"), IsAdmin: false, } err = repo.CreateUser(ctx, user) require.NoError(t, err) assert.NotZero(t, user.ID) // Test GetUserByUsername retrievedUser, err := repo.GetUserByUsername(ctx, "testuser") require.NoError(t, err) assert.NotNil(t, retrievedUser) assert.Equal(t, "testuser", retrievedUser.Username) // Test UserExists exists, err := repo.UserExists(ctx, "testuser") require.NoError(t, err) assert.True(t, exists) // Test UpdateUser retrievedUser.Description = ptrString("Updated description") err = repo.UpdateUser(ctx, retrievedUser) require.NoError(t, err) // Verify update updatedUser, err := repo.GetUserByUsername(ctx, "testuser") require.NoError(t, err) assert.Equal(t, "Updated description", *updatedUser.Description) // Test AllowPasswordReset err = repo.AllowPasswordReset(ctx, "testuser") require.NoError(t, err) // Verify password reset flag userWithReset, err := repo.GetUserByUsername(ctx, "testuser") require.NoError(t, err) assert.True(t, userWithReset.AllowPasswordReset) // Test CompletePasswordReset err = repo.CompletePasswordReset(ctx, "testuser", "newhashedpassword") require.NoError(t, err) // Verify password reset completion userAfterReset, err := repo.GetUserByUsername(ctx, "testuser") require.NoError(t, err) assert.Equal(t, "newhashedpassword", userAfterReset.PasswordHash) assert.False(t, userAfterReset.AllowPasswordReset) // Test DeleteUser err = repo.DeleteUser(ctx, userAfterReset.ID) require.NoError(t, err) // Verify deletion deletedUser, err := repo.GetUserByUsername(ctx, "testuser") require.NoError(t, err) assert.Nil(t, deletedUser) }) } func TestAuthService(t *testing.T) { t.Run("Password hashing and authentication", func(t *testing.T) { // Create a temporary database dbPath := "test_auth_db.sqlite" defer os.Remove(dbPath) cfg := createTestConfig() repo, err := NewSQLiteRepository(dbPath, cfg) require.NoError(t, err) defer repo.Close() ctx := context.Background() // Create user service jwtConfig := JWTConfig{ Secret: "test-secret", ExpirationTime: time.Hour, Issuer: "test-issuer", } userService := NewUserService(repo, jwtConfig, "admin123") // Test password hashing password := "testpassword123" hashedPassword, err := userService.HashPassword(ctx, password) require.NoError(t, err) assert.NotEmpty(t, hashedPassword) // Create a test user user := &User{ Username: "testuser", PasswordHash: hashedPassword, } err = repo.CreateUser(ctx, user) require.NoError(t, err) // Test successful authentication authenticatedUser, err := userService.Authenticate(ctx, "testuser", password) require.NoError(t, err) assert.NotNil(t, authenticatedUser) assert.Equal(t, "testuser", authenticatedUser.Username) // Test failed authentication with wrong password _, err = userService.Authenticate(ctx, "testuser", "wrongpassword") assert.Error(t, err) assert.Equal(t, "invalid credentials", err.Error()) // Test JWT generation token, err := userService.GenerateJWT(ctx, authenticatedUser) require.NoError(t, err) assert.NotEmpty(t, token) // Test JWT validation validatedUser, err := userService.ValidateJWT(ctx, token) require.NoError(t, err) assert.NotNil(t, validatedUser) assert.Equal(t, authenticatedUser.ID, validatedUser.ID) // Test admin authentication adminUser, err := userService.AdminAuthenticate(ctx, "admin123") require.NoError(t, err) assert.NotNil(t, adminUser) assert.True(t, adminUser.IsAdmin) assert.Equal(t, "admin", adminUser.Username) // Test failed admin authentication _, err = userService.AdminAuthenticate(ctx, "wrongadminpassword") assert.Error(t, err) assert.Equal(t, "invalid admin credentials", err.Error()) }) } func TestPasswordResetService(t *testing.T) { t.Run("Password reset workflow", func(t *testing.T) { // Create a temporary database dbPath := "test_reset_db.sqlite" defer os.Remove(dbPath) cfg := createTestConfig() repo, err := NewSQLiteRepository(dbPath, cfg) require.NoError(t, err) defer repo.Close() ctx := context.Background() // Create user service jwtConfig := JWTConfig{ Secret: "test-secret", ExpirationTime: time.Hour, Issuer: "test-issuer", } userService := NewUserService(repo, jwtConfig, "admin123") // Create a test user password := "oldpassword123" hashedPassword, err := userService.HashPassword(ctx, password) require.NoError(t, err) user := &User{ Username: "resetuser", PasswordHash: hashedPassword, } err = repo.CreateUser(ctx, user) require.NoError(t, err) // Test password reset request err = userService.RequestPasswordReset(ctx, "resetuser") require.NoError(t, err) // Verify user is flagged for reset userAfterRequest, err := repo.GetUserByUsername(ctx, "resetuser") require.NoError(t, err) assert.True(t, userAfterRequest.AllowPasswordReset) // Test password reset completion newPassword := "newpassword123" err = userService.CompletePasswordReset(ctx, "resetuser", newPassword) require.NoError(t, err) // Verify password was updated and reset flag was cleared userAfterReset, err := repo.GetUserByUsername(ctx, "resetuser") require.NoError(t, err) assert.False(t, userAfterReset.AllowPasswordReset) // Verify new password works by authenticating with the new password authenticatedUser, err := userService.Authenticate(ctx, "resetuser", newPassword) require.NoError(t, err) assert.NotNil(t, authenticatedUser) assert.Equal(t, "resetuser", authenticatedUser.Username) }) } // Helper function to create string pointers func ptrString(s string) *string { return &s }