todo/internal/auth/handler/login_test.go
Michael Thomson e55d419d44
All checks were successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
auth services, middleware, and other stuff
2025-05-22 13:55:43 -04:00

169 lines
4.1 KiB
Go

package handler
import (
"bytes"
"context"
"encoding/json"
"log/slog"
"errors"
"net/http"
"net/http/httptest"
"testing"
"gitea.michaelthomson.dev/mthomson/habits/internal/auth/service"
)
type MockLoginer struct {
LoginFunc func(ctx context.Context, email string, password string) (string, error)
}
func (m *MockLoginer) Login(ctx context.Context, email string, password string) (string, error) {
return m.LoginFunc(ctx, email, password)
}
func TestLogin(t *testing.T) {
logger := slog.Default()
t.Run("returns 200 for existing user with correct credentials", func(t *testing.T) {
loginRequest := LoginRequest{Email: "test@test.com", Password: "password"}
token := "examplejwt"
service := MockLoginer{
LoginFunc: func(ctx context.Context, email string, password string) (string, error) {
return token, nil
},
}
handler := HandleLogin(logger, &service)
requestBody, err := json.Marshal(loginRequest)
if err != nil {
t.Fatalf("Failed to marshal request %+v: %v", loginRequest, err)
}
req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(requestBody))
res := httptest.NewRecorder()
handler(res, req)
AssertStatusCodes(t, res.Code, http.StatusOK)
AssertToken(t, res, token)
})
t.Run("returns 401 for existing user with incorrect credentials", func(t *testing.T) {
loginRequest := LoginRequest{Email: "test@test.com", Password: "password"}
service := MockLoginer{
LoginFunc: func(ctx context.Context, email string, password string) (string, error) {
return "", service.ErrUnauthorized
},
}
handler := HandleLogin(logger, &service)
requestBody, err := json.Marshal(loginRequest)
if err != nil {
t.Fatalf("Failed to marshal request %+v: %v", loginRequest, err)
}
req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(requestBody))
res := httptest.NewRecorder()
handler(res, req)
AssertStatusCodes(t, res.Code, http.StatusUnauthorized)
})
t.Run("returns 401 for non-existing user", func(t *testing.T) {
loginRequest := LoginRequest{Email: "test@test.com", Password: "password"}
service := MockLoginer{
LoginFunc: func(ctx context.Context, email string, password string) (string, error) {
return "", service.ErrNotFound
},
}
handler := HandleLogin(logger, &service)
requestBody, err := json.Marshal(loginRequest)
if err != nil {
t.Fatalf("Failed to marshal request %+v: %v", loginRequest, err)
}
req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(requestBody))
res := httptest.NewRecorder()
handler(res, req)
AssertStatusCodes(t, res.Code, http.StatusUnauthorized)
})
t.Run("returns 400 with bad json", func(t *testing.T) {
handler := HandleLogin(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, "/login", 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) {
loginRequest := LoginRequest{Email: "test@test.com", Password: "password"}
service := MockLoginer{
LoginFunc: func(ctx context.Context, email string, password string) (string, error) {
return "", errors.New("foo bar")
},
}
handler := HandleLogin(logger, &service)
requestBody, err := json.Marshal(loginRequest)
if err != nil {
t.Fatalf("Failed to marshal request %+v: %v", loginRequest, err)
}
req := httptest.NewRequest(http.MethodPost, "/login", 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 AssertToken(t testing.TB, res *httptest.ResponseRecorder, want string) {
t.Helper()
got := res.Result().Cookies()[0].Value
if got != want {
t.Errorf("got cookie: %q, want cookie: %q", got, want)
}
}