package auth_test import ( "context" "fmt" "testing" "time" "github.com/food-ai/backend/internal/domain/auth" "github.com/food-ai/backend/internal/domain/auth/mocks" "github.com/food-ai/backend/internal/domain/user" umocks "github.com/food-ai/backend/internal/domain/user/mocks" ) func newTestService(verifier *mocks.MockTokenVerifier, repo *umocks.MockUserRepository) *auth.Service { jm := auth.NewJWTManager("test-secret", 15*time.Minute, 720*time.Hour) return auth.NewService(verifier, repo, jm) } func TestLogin_Success(t *testing.T) { verifier := &mocks.MockTokenVerifier{ VerifyTokenFn: func(ctx context.Context, idToken string) (string, string, string, string, error) { return "fb-uid", "test@example.com", "Test User", "https://avatar.url", nil }, } repo := &umocks.MockUserRepository{ UpsertByFirebaseUIDFn: func(ctx context.Context, uid, email, name, avatarURL string) (*user.User, error) { return &user.User{ID: "user-1", Email: email, Name: name, Plan: "free"}, nil }, SetRefreshTokenFn: func(ctx context.Context, id, token string, expiresAt time.Time) error { return nil }, } svc := newTestService(verifier, repo) resp, loginError := svc.Login(context.Background(), "firebase-token") if loginError != nil { t.Fatalf("unexpected error: %v", loginError) } if resp.AccessToken == "" { t.Error("expected non-empty access token") } if resp.RefreshToken == "" { t.Error("expected non-empty refresh token") } if resp.User.ID != "user-1" { t.Errorf("expected user ID 'user-1', got %q", resp.User.ID) } } func TestLogin_InvalidFirebaseToken(t *testing.T) { verifier := &mocks.MockTokenVerifier{ VerifyTokenFn: func(ctx context.Context, idToken string) (string, string, string, string, error) { return "", "", "", "", fmt.Errorf("invalid token") }, } repo := &umocks.MockUserRepository{} svc := newTestService(verifier, repo) _, loginError := svc.Login(context.Background(), "bad-token") if loginError == nil { t.Fatal("expected error for invalid firebase token") } } func TestLogin_UpsertError(t *testing.T) { verifier := &mocks.MockTokenVerifier{ VerifyTokenFn: func(ctx context.Context, idToken string) (string, string, string, string, error) { return "fb-uid", "test@example.com", "Test", "", nil }, } repo := &umocks.MockUserRepository{ UpsertByFirebaseUIDFn: func(ctx context.Context, uid, email, name, avatarURL string) (*user.User, error) { return nil, fmt.Errorf("db error") }, } svc := newTestService(verifier, repo) _, loginError := svc.Login(context.Background(), "token") if loginError == nil { t.Fatal("expected error for upsert failure") } } func TestLogin_SetRefreshTokenError(t *testing.T) { verifier := &mocks.MockTokenVerifier{ VerifyTokenFn: func(ctx context.Context, idToken string) (string, string, string, string, error) { return "fb-uid", "test@example.com", "Test", "", nil }, } repo := &umocks.MockUserRepository{ UpsertByFirebaseUIDFn: func(ctx context.Context, uid, email, name, avatarURL string) (*user.User, error) { return &user.User{ID: "user-1", Plan: "free"}, nil }, SetRefreshTokenFn: func(ctx context.Context, id, token string, expiresAt time.Time) error { return fmt.Errorf("db error") }, } svc := newTestService(verifier, repo) _, loginError := svc.Login(context.Background(), "token") if loginError == nil { t.Fatal("expected error for set refresh token failure") } } func TestRefresh_Success(t *testing.T) { repo := &umocks.MockUserRepository{ FindByRefreshTokenFn: func(ctx context.Context, token string) (*user.User, error) { return &user.User{ID: "user-1", Plan: "free"}, nil }, SetRefreshTokenFn: func(ctx context.Context, id, token string, expiresAt time.Time) error { return nil }, } verifier := &mocks.MockTokenVerifier{} svc := newTestService(verifier, repo) resp, refreshError := svc.Refresh(context.Background(), "valid-refresh-token") if refreshError != nil { t.Fatalf("unexpected error: %v", refreshError) } if resp.AccessToken == "" { t.Error("expected non-empty access token") } if resp.RefreshToken == "" { t.Error("expected non-empty refresh token") } } func TestRefresh_InvalidToken(t *testing.T) { repo := &umocks.MockUserRepository{ FindByRefreshTokenFn: func(ctx context.Context, token string) (*user.User, error) { return nil, fmt.Errorf("not found") }, } verifier := &mocks.MockTokenVerifier{} svc := newTestService(verifier, repo) _, refreshError := svc.Refresh(context.Background(), "bad-token") if refreshError == nil { t.Fatal("expected error for invalid refresh token") } } func TestRefresh_SetRefreshTokenError(t *testing.T) { repo := &umocks.MockUserRepository{ FindByRefreshTokenFn: func(ctx context.Context, token string) (*user.User, error) { return &user.User{ID: "user-1", Plan: "free"}, nil }, SetRefreshTokenFn: func(ctx context.Context, id, token string, expiresAt time.Time) error { return fmt.Errorf("db error") }, } verifier := &mocks.MockTokenVerifier{} svc := newTestService(verifier, repo) _, refreshError := svc.Refresh(context.Background(), "valid-token") if refreshError == nil { t.Fatal("expected error") } } func TestLogout_Success(t *testing.T) { repo := &umocks.MockUserRepository{ ClearRefreshTokenFn: func(ctx context.Context, id string) error { return nil }, } verifier := &mocks.MockTokenVerifier{} svc := newTestService(verifier, repo) logoutError := svc.Logout(context.Background(), "user-1") if logoutError != nil { t.Fatalf("unexpected error: %v", logoutError) } } func TestLogout_Error(t *testing.T) { repo := &umocks.MockUserRepository{ ClearRefreshTokenFn: func(ctx context.Context, id string) error { return fmt.Errorf("db error") }, } verifier := &mocks.MockTokenVerifier{} svc := newTestService(verifier, repo) logoutError := svc.Logout(context.Background(), "user-1") if logoutError == nil { t.Fatal("expected error") } } func TestLogin_ExpiresIn(t *testing.T) { verifier := &mocks.MockTokenVerifier{ VerifyTokenFn: func(ctx context.Context, idToken string) (string, string, string, string, error) { return "fb-uid", "test@example.com", "Test", "", nil }, } repo := &umocks.MockUserRepository{ UpsertByFirebaseUIDFn: func(ctx context.Context, uid, email, name, avatarURL string) (*user.User, error) { return &user.User{ID: "user-1", Plan: "free"}, nil }, SetRefreshTokenFn: func(ctx context.Context, id, token string, expiresAt time.Time) error { return nil }, } svc := newTestService(verifier, repo) resp, _ := svc.Login(context.Background(), "token") if resp.ExpiresIn != 900 { t.Errorf("expected expires_in 900, got %d", resp.ExpiresIn) } }