935f742ac9
Implemented comprehensive filtering capabilities for the habits list endpoint: - Filter by type (BOOLEAN, COUNTER, VALUE) - Filter by frequency (DAILY, WEEKLY, MONTHLY) - Filter by archived status - Text search in habit name and description - All filters can be combined - Filters work with pagination Technical changes: - Added FilterParams to GetUserHabitsQuery - Created HabitFilter struct in repository interface - Implemented dynamic SQL query building in SQLite repository - Updated HTTP handler to parse filter query parameters - Added comprehensive tests for repository and handler filtering - Updated all test mocks with new filter methods
225 lines
5.8 KiB
Go
225 lines
5.8 KiB
Go
package queries
|
|
|
|
import (
|
|
"apocapoc-api/internal/domain/repositories"
|
|
"apocapoc-api/internal/shared/pagination"
|
|
"context"
|
|
"testing"
|
|
|
|
"apocapoc-api/internal/domain/entities"
|
|
"apocapoc-api/internal/shared/errors"
|
|
)
|
|
|
|
type mockLoginUserRepo struct {
|
|
findByEmailFunc func(ctx context.Context, email string) (*entities.User, error)
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) FindByEmail(ctx context.Context, email string) (*entities.User, error) {
|
|
if m.findByEmailFunc != nil {
|
|
return m.findByEmailFunc(ctx, email)
|
|
}
|
|
return nil, errors.ErrNotFound
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) FindByID(ctx context.Context, id string) (*entities.User, error) {
|
|
return nil, errors.ErrNotFound
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) FindByVerificationToken(ctx context.Context, token string) (*entities.User, error) {
|
|
return nil, errors.ErrNotFound
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) Create(ctx context.Context, user *entities.User) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) Update(ctx context.Context, user *entities.User) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) Delete(ctx context.Context, id string) error {
|
|
return nil
|
|
}
|
|
|
|
type mockLoginPasswordHasher struct {
|
|
compareFunc func(hashedPassword, password string) error
|
|
}
|
|
|
|
func (m *mockLoginPasswordHasher) Hash(password string) (string, error) {
|
|
return "hashed_" + password, nil
|
|
}
|
|
|
|
func (m *mockLoginPasswordHasher) Compare(hashedPassword, password string) error {
|
|
if m.compareFunc != nil {
|
|
return m.compareFunc(hashedPassword, password)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestLoginUserHandler_Success(t *testing.T) {
|
|
user := entities.NewUser("test@example.com", "hashed_password")
|
|
user.ID = "user-123"
|
|
user.EmailVerified = true
|
|
|
|
repo := &mockLoginUserRepo{
|
|
findByEmailFunc: func(ctx context.Context, email string) (*entities.User, error) {
|
|
return user, nil
|
|
},
|
|
}
|
|
|
|
hasher := &mockLoginPasswordHasher{
|
|
compareFunc: func(hashedPassword, password string) error {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "test@example.com",
|
|
Password: "password123",
|
|
}
|
|
|
|
result, err := handler.Handle(context.Background(), query)
|
|
if err != nil {
|
|
t.Fatalf("Handle() unexpected error = %v", err)
|
|
}
|
|
|
|
if result.UserID != "user-123" {
|
|
t.Errorf("UserID = %v, want %v", result.UserID, "user-123")
|
|
}
|
|
|
|
if result.Email != "test@example.com" {
|
|
t.Errorf("Email = %v, want %v", result.Email, "test@example.com")
|
|
}
|
|
}
|
|
|
|
func TestLoginUserHandler_EmptyEmail(t *testing.T) {
|
|
repo := &mockLoginUserRepo{}
|
|
hasher := &mockLoginPasswordHasher{}
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "",
|
|
Password: "password123",
|
|
}
|
|
|
|
_, err := handler.Handle(context.Background(), query)
|
|
if err != errors.ErrInvalidInput {
|
|
t.Errorf("Handle() error = %v, want %v", err, errors.ErrInvalidInput)
|
|
}
|
|
}
|
|
|
|
func TestLoginUserHandler_EmptyPassword(t *testing.T) {
|
|
repo := &mockLoginUserRepo{}
|
|
hasher := &mockLoginPasswordHasher{}
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "test@example.com",
|
|
Password: "",
|
|
}
|
|
|
|
_, err := handler.Handle(context.Background(), query)
|
|
if err != errors.ErrInvalidInput {
|
|
t.Errorf("Handle() error = %v, want %v", err, errors.ErrInvalidInput)
|
|
}
|
|
}
|
|
|
|
func TestLoginUserHandler_UserNotFound(t *testing.T) {
|
|
repo := &mockLoginUserRepo{
|
|
findByEmailFunc: func(ctx context.Context, email string) (*entities.User, error) {
|
|
return nil, errors.ErrNotFound
|
|
},
|
|
}
|
|
|
|
hasher := &mockLoginPasswordHasher{}
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "nonexistent@example.com",
|
|
Password: "password123",
|
|
}
|
|
|
|
_, err := handler.Handle(context.Background(), query)
|
|
if err != errors.ErrNotFound {
|
|
t.Errorf("Handle() error = %v, want %v", err, errors.ErrNotFound)
|
|
}
|
|
}
|
|
|
|
func TestLoginUserHandler_InvalidPassword(t *testing.T) {
|
|
user := entities.NewUser("test@example.com", "hashed_password")
|
|
user.ID = "user-123"
|
|
user.EmailVerified = true
|
|
|
|
repo := &mockLoginUserRepo{
|
|
findByEmailFunc: func(ctx context.Context, email string) (*entities.User, error) {
|
|
return user, nil
|
|
},
|
|
}
|
|
|
|
hasher := &mockLoginPasswordHasher{
|
|
compareFunc: func(hashedPassword, password string) error {
|
|
return errors.ErrInvalidInput
|
|
},
|
|
}
|
|
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "test@example.com",
|
|
Password: "wrongpassword",
|
|
}
|
|
|
|
_, err := handler.Handle(context.Background(), query)
|
|
if err != errors.ErrNotFound {
|
|
t.Errorf("Handle() error = %v, want %v", err, errors.ErrNotFound)
|
|
}
|
|
}
|
|
|
|
func TestLoginUserHandler_EmailNotVerified(t *testing.T) {
|
|
user := entities.NewUser("test@example.com", "hashed_password")
|
|
user.ID = "user-123"
|
|
user.EmailVerified = false
|
|
|
|
repo := &mockLoginUserRepo{
|
|
findByEmailFunc: func(ctx context.Context, email string) (*entities.User, error) {
|
|
return user, nil
|
|
},
|
|
}
|
|
|
|
hasher := &mockLoginPasswordHasher{
|
|
compareFunc: func(hashedPassword, password string) error {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
handler := NewLoginUserHandler(repo, hasher)
|
|
|
|
query := LoginUserQuery{
|
|
Email: "test@example.com",
|
|
Password: "password123",
|
|
}
|
|
|
|
_, err := handler.Handle(context.Background(), query)
|
|
if err != errors.ErrEmailNotVerified {
|
|
t.Errorf("Handle() error = %v, want %v", err, errors.ErrEmailNotVerified)
|
|
}
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) FindActiveByUserIDWithPagination(ctx context.Context, userID string, params pagination.Params) ([]*entities.Habit, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) CountActiveByUserID(ctx context.Context, userID string) (int, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) FindByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter, paginationParams *pagination.Params) ([]*entities.Habit, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockLoginUserRepo) CountByUserIDFiltered(ctx context.Context, userID string, filter repositories.HabitFilter) (int, error) {
|
|
return 0, nil
|
|
}
|