Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/gamidoc/backend/internal/auth"
"github.com/gamidoc/backend/internal/bootstrap"
apphttp "github.com/gamidoc/backend/internal/http"
"github.com/gamidoc/backend/internal/migrate"
"github.com/gamidoc/backend/internal/pdf"
"github.com/gamidoc/backend/internal/project"
"github.com/gamidoc/backend/internal/recommendation"
Expand Down Expand Up @@ -51,6 +52,13 @@ func New(cfg config.Config) (*App, error) {
return nil, fmt.Errorf("postgres startup check failed: %w", err)
}

migrator := migrate.NewMigrator(pg, cfg.MigrationsDir)
if _, err := migrator.Up(startupCtx); err != nil {
_ = pg.Close()
_ = redisClient.Close()
return nil, fmt.Errorf("postgres migration failed: %w", err)
}

if err := redisClient.Ready(startupCtx); err != nil {
_ = pg.Close()
_ = redisClient.Close()
Expand Down
6 changes: 3 additions & 3 deletions internal/auth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (

appmiddleware "github.com/gamidoc/backend/internal/http/middleware"
"github.com/gamidoc/backend/internal/http/response"
"github.com/gamidoc/backend/internal/storage/postgres"
"github.com/gamidoc/backend/internal/token"
"github.com/gamidoc/backend/internal/user"
"github.com/go-chi/chi/v5"
)

Expand Down Expand Up @@ -69,7 +69,7 @@ func (h *Handler) login(w http.ResponseWriter, r *http.Request) {
result, err := h.service.Login(r.Context(), input)
if err != nil {
switch {
case errors.Is(err, ErrInvalidCredentials), errors.Is(err, postgres.ErrUserNotFound):
case errors.Is(err, ErrInvalidCredentials), errors.Is(err, user.ErrUserNotFound):
response.WriteError(w, http.StatusUnauthorized, "INVALID_CREDENTIALS", "Invalid credentials", nil)
default:
response.WriteError(w, http.StatusInternalServerError, "INTERNAL_SERVER_ERROR", "Internal server error", nil)
Expand All @@ -89,7 +89,7 @@ func (h *Handler) me(w http.ResponseWriter, r *http.Request) {

currentUser, err := h.service.Me(r.Context(), userID)
if err != nil {
if errors.Is(err, postgres.ErrUserNotFound) {
if errors.Is(err, user.ErrUserNotFound) {
response.WriteError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", nil)
return
}
Expand Down
6 changes: 6 additions & 0 deletions internal/auth/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func (s *Service) Register(ctx context.Context, input RegisterInput) (AuthResult
if err == nil {
return AuthResult{}, ErrEmailAlreadyExists
}
if !errors.Is(err, user.ErrUserNotFound) {
return AuthResult{}, err
}

hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
Expand Down Expand Up @@ -92,6 +95,9 @@ func (s *Service) Login(ctx context.Context, input LoginInput) (AuthResult, erro

foundUser, err := s.users.FindByEmail(ctx, email)
if err != nil {
if !errors.Is(err, user.ErrUserNotFound) {
return AuthResult{}, err
}
return AuthResult{}, ErrInvalidCredentials
}

Expand Down
35 changes: 30 additions & 5 deletions internal/auth/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import (
)

type fakeUserRepository struct {
usersByEmail map[string]user.User
usersByID map[string]user.User
createErr error
usersByEmail map[string]user.User
usersByID map[string]user.User
createErr error
findByEmailErr error
createCalls int
}

func (r *fakeUserRepository) Create(ctx context.Context, input user.User) (user.User, error) {
r.createCalls++
if r.createErr != nil {
return user.User{}, r.createErr
}
Expand All @@ -33,17 +36,20 @@ func (r *fakeUserRepository) Create(ctx context.Context, input user.User) (user.
}

func (r *fakeUserRepository) FindByEmail(ctx context.Context, email string) (user.User, error) {
if r.findByEmailErr != nil {
return user.User{}, r.findByEmailErr
}
u, ok := r.usersByEmail[email]
if !ok {
return user.User{}, errors.New("not found")
return user.User{}, user.ErrUserNotFound
}
return u, nil
}

func (r *fakeUserRepository) FindByID(ctx context.Context, id string) (user.User, error) {
u, ok := r.usersByID[id]
if !ok {
return user.User{}, errors.New("not found")
return user.User{}, user.ErrUserNotFound
}
return u, nil
}
Expand Down Expand Up @@ -73,6 +79,25 @@ func TestRegister(t *testing.T) {
}
}

func TestRegisterReturnsFindByEmailError(t *testing.T) {
lookupErr := errors.New("lookup failed")
repo := &fakeUserRepository{findByEmailErr: lookupErr}
tokens := token.NewManager("secret", time.Hour)
service := NewService(repo, tokens)

_, err := service.Register(context.Background(), RegisterInput{
Email: "test@example.com",
Password: "password123",
})
if !errors.Is(err, lookupErr) {
t.Fatalf("expected lookup error, got %v", err)
}

if repo.createCalls != 0 {
t.Fatalf("expected Create not to be called, got %d calls", repo.createCalls)
}
}

func TestLogin(t *testing.T) {
repo := &fakeUserRepository{
usersByEmail: map[string]user.User{},
Expand Down
5 changes: 2 additions & 3 deletions internal/http/router_test_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package http

import (
"context"
"errors"
"log/slog"
"net/http"
"os"
Expand Down Expand Up @@ -57,15 +56,15 @@ func (r *fakeUserRepository) Create(ctx context.Context, input user.User) (user.
func (r *fakeUserRepository) FindByEmail(ctx context.Context, email string) (user.User, error) {
u, ok := r.usersByEmail[email]
if !ok {
return user.User{}, errors.New("not found")
return user.User{}, user.ErrUserNotFound
}
return u, nil
}

func (r *fakeUserRepository) FindByID(ctx context.Context, id string) (user.User, error) {
u, ok := r.usersByID[id]
if !ok {
return user.User{}, errors.New("not found")
return user.User{}, user.ErrUserNotFound
}
return u, nil
}
Expand Down
6 changes: 2 additions & 4 deletions internal/storage/postgres/user_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"github.com/gamidoc/backend/internal/user"
)

var ErrUserNotFound = errors.New("user not found")

type UserRepository struct {
db *DB
}
Expand Down Expand Up @@ -55,7 +53,7 @@ func (r *UserRepository) FindByEmail(ctx context.Context, email string) (user.Us
err := row.Scan(&found.ID, &found.Email, &found.PasswordHash, &found.CreatedAt)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return user.User{}, ErrUserNotFound
return user.User{}, user.ErrUserNotFound
}
return user.User{}, err
}
Expand All @@ -78,7 +76,7 @@ func (r *UserRepository) FindByID(ctx context.Context, id string) (user.User, er
err := row.Scan(&found.ID, &found.Email, &found.PasswordHash, &found.CreatedAt)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return user.User{}, ErrUserNotFound
return user.User{}, user.ErrUserNotFound
}
return user.User{}, err
}
Expand Down
7 changes: 6 additions & 1 deletion internal/user/repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package user

import "context"
import (
"context"
"errors"
)

var ErrUserNotFound = errors.New("user not found")

type Repository interface {
Create(ctx context.Context, user User) (User, error)
Expand Down