package handler import ( "bytes" "context" "encoding/json" "log/slog" "errors" "net/http" "net/http/httptest" "testing" "gitea.michaelthomson.dev/mthomson/habits/internal/user/service" "github.com/gofrs/uuid/v5" ) type MockUserRegisterer struct { RegisterUserFunc func(ctx context.Context, email string, password string) (uuid.UUID, error) } func (m *MockUserRegisterer) Register(ctx context.Context, email string, password string) (uuid.UUID, error) { return m.RegisterUserFunc(ctx, email, password) } func TestCreateUser(t *testing.T) { logger := slog.Default() t.Run("create user", func(t *testing.T) { createUserRequest := RegisterRequest{Email: "test@test.com", Password: "password"} newUUID := NewUUID(t) service := MockUserRegisterer{ RegisterUserFunc: func(ctx context.Context, email string, password string) (uuid.UUID, error) { return newUUID, nil }, } handler := HandleRegisterUser(logger, &service) requestBody, err := json.Marshal(createUserRequest) if err != nil { t.Fatalf("Failed to marshal request %+v: %v", createUserRequest, err) } req := httptest.NewRequest(http.MethodPost, "/register", bytes.NewBuffer(requestBody)) res := httptest.NewRecorder() handler(res, req) AssertStatusCodes(t, res.Code, http.StatusOK) }) t.Run("returns 409 when user exists", func(t *testing.T) { createUserRequest := RegisterRequest{Email: "test@test.com", Password: "password"} newUUID := NewUUID(t) service := MockUserRegisterer{ RegisterUserFunc: func(ctx context.Context, email string, password string) (uuid.UUID, error) { return newUUID, service.ErrUserExists }, } handler := HandleRegisterUser(logger, &service) requestBody, err := json.Marshal(createUserRequest) if err != nil { t.Fatalf("Failed to marshal request %+v: %v", createUserRequest, err) } req := httptest.NewRequest(http.MethodPost, "/register", bytes.NewBuffer(requestBody)) res := httptest.NewRecorder() handler(res, req) AssertStatusCodes(t, res.Code, http.StatusConflict) }) t.Run("returns 400 with bad json", func(t *testing.T) { handler := HandleRegisterUser(logger, nil) badStruct := struct { Foo string }{ Foo: "bar", } requestBody, err := json.Marshal(badStruct) if err != nil { t.Fatalf("Failed to marshal request %+v: %v", badStruct, err) } req := httptest.NewRequest(http.MethodPost, "/register", bytes.NewBuffer(requestBody)) res := httptest.NewRecorder() handler(res, req) AssertStatusCodes(t, res.Code, http.StatusBadRequest) }) t.Run("returns 500 arbitrary errors", func(t *testing.T) { createUserRequest := RegisterRequest{Email: "test@test.com", Password: "password"} newUUID := NewUUID(t) service := MockUserRegisterer{ RegisterUserFunc: func(ctx context.Context, email string, password string) (uuid.UUID, error) { return newUUID, errors.New("foo bar") }, } handler := HandleRegisterUser(logger, &service) requestBody, err := json.Marshal(createUserRequest) if err != nil { t.Fatalf("Failed to marshal request %+v: %v", createUserRequest, err) } req := httptest.NewRequest(http.MethodPost, "/user", bytes.NewBuffer(requestBody)) res := httptest.NewRecorder() handler(res, req) AssertStatusCodes(t, res.Code, http.StatusInternalServerError) }) } func AssertStatusCodes(t testing.TB, got, want int) { t.Helper() if got != want { t.Errorf("got status code: %v, want status code: %v", want, got) } } func NewUUID(t testing.TB) uuid.UUID { t.Helper() uuid, err := uuid.NewV4() if err != nil { t.Errorf("error generation uuid: %v", err) } return uuid }