Skip to content

Commit

Permalink
⚡️ Implemented /auth/signup/v1 endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
lhbelfanti committed Jan 12, 2025
1 parent 69b4062 commit 4828186
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 0 deletions.
7 changes: 7 additions & 0 deletions cmd/api/auth/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@ var (
FailedToSignUpBecauseTheUserAlreadyExists = errors.New("user already exists")
FailedToGenerateHashFromPassword = errors.New("failed to generate hash from password")
FailedToInsertUserIntoDatabase = errors.New("failed to insert user into database")
MissingUsername = errors.New("missing username")
MissingPassword = errors.New("missing password")
)

const (
InvalidRequestBody = "Invalid request body"
FailedToSignUp = "Failed to sign up"
)
54 changes: 54 additions & 0 deletions cmd/api/auth/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package auth

import (
"ahbcc/internal/log"
"encoding/json"
"net/http"

"ahbcc/cmd/api/users"
)

// SignUpHandlerV1 HTTP Handler of the endpoint /auth/signup
func SignUpHandlerV1(signUp SignUp) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

var user users.UserDTO
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
log.Error(ctx, err.Error())
http.Error(w, InvalidRequestBody, http.StatusBadRequest)
return
}
ctx = log.With(ctx, log.Param("username", user.Username))

err = validateBody(user)
if err != nil {
log.Error(ctx, err.Error())
http.Error(w, InvalidRequestBody, http.StatusBadRequest)
}

err = signUp(ctx, user)
if err != nil {
log.Error(ctx, err.Error())
http.Error(w, FailedToSignUp, http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("User successfully signed up"))
}
}

// validateBody validates that mandatory fields are present
func validateBody(user users.UserDTO) error {
if user.Username == "" {
return MissingUsername
}

if user.Password == "" {
return MissingPassword
}

return nil
}
90 changes: 90 additions & 0 deletions cmd/api/auth/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package auth_test

import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"

"ahbcc/cmd/api/auth"
"ahbcc/cmd/api/users"
)

func TestSignUpHandlerV1_success(t *testing.T) {
mockSignUp := auth.MockSignUp(nil)
mockResponseWriter := httptest.NewRecorder()
mockUser := users.MockUserDTO()
mockBody, _ := json.Marshal(mockUser)
mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/auth/signup/v1", bytes.NewReader(mockBody))

signUpHandlerV1 := auth.SignUpHandlerV1(mockSignUp)

signUpHandlerV1(mockResponseWriter, mockRequest)

want := http.StatusOK
got := mockResponseWriter.Result().StatusCode

assert.Equal(t, want, got)
}

func TestSignUpHandlerV1_failsWhenTheBodyCantBeParsed(t *testing.T) {
mockSignUp := auth.MockSignUp(nil)
mockResponseWriter := httptest.NewRecorder()
mockBody, _ := json.Marshal(`{"wrong": "body"}`)
mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/auth/signup/v1", bytes.NewReader(mockBody))

signUpHandlerV1 := auth.SignUpHandlerV1(mockSignUp)

signUpHandlerV1(mockResponseWriter, mockRequest)

want := http.StatusBadRequest
got := mockResponseWriter.Result().StatusCode

assert.Equal(t, want, got)
}

func TestSignUpHandlerV1_failsWhenValidateBodyThrowsError(t *testing.T) {
mockSignUp := auth.MockSignUp(nil)
mockResponseWriter := httptest.NewRecorder()

for _, test := range []struct {
mockUser users.UserDTO
}{
{mockUser: users.UserDTO{Username: "username"}},
{mockUser: users.UserDTO{Password: "password"}},
} {
mockBody, _ := json.Marshal(test.mockUser)
mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/auth/signup/v1", bytes.NewReader(mockBody))

signUpHandlerV1 := auth.SignUpHandlerV1(mockSignUp)

signUpHandlerV1(mockResponseWriter, mockRequest)

want := http.StatusBadRequest
got := mockResponseWriter.Result().StatusCode

assert.Equal(t, want, got)
}
}

func TestSignUpHandlerV1_failsWhenSignUpThrowsError(t *testing.T) {
mockSignUp := auth.MockSignUp(errors.New("failed to sign up"))
mockResponseWriter := httptest.NewRecorder()
mockUser := users.MockUserDTO()
mockBody, _ := json.Marshal(mockUser)
mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/auth/signup/v1", bytes.NewReader(mockBody))

signUpHandlerV1 := auth.SignUpHandlerV1(mockSignUp)

signUpHandlerV1(mockResponseWriter, mockRequest)

want := http.StatusInternalServerError
got := mockResponseWriter.Result().StatusCode

assert.Equal(t, want, got)
}
14 changes: 14 additions & 0 deletions cmd/api/auth/mocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package auth

import (
"context"

"ahbcc/cmd/api/users"
)

// MockSignUp mocks SignUp function
func MockSignUp(err error) SignUp {
return func(ctx context.Context, user users.UserDTO) error {
return err
}
}
7 changes: 7 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (

"github.com/rs/zerolog"

"ahbcc/cmd/api/auth"
"ahbcc/cmd/api/migrations"
"ahbcc/cmd/api/ping"
"ahbcc/cmd/api/search/criteria"
"ahbcc/cmd/api/tweets"
"ahbcc/cmd/api/tweets/quotes"
"ahbcc/cmd/api/users"
"ahbcc/internal/database"
_http "ahbcc/internal/http"
"ahbcc/internal/log"
Expand Down Expand Up @@ -52,6 +54,10 @@ func main() {
insertAppliedMigration := migrations.MakeInsertAppliedMigration(db)
runMigrations := migrations.MakeRun(db, createMigrationsTable, isMigrationApplied, insertAppliedMigration)

userExists := users.MakeUserExists(db)
insertUser := users.MakeInsert(db)
signUp := auth.MakeSignUp(userExists, insertUser)

insertSingleQuote := quotes.MakeInsertSingle(db)
deleteOrphanQuotes := quotes.MakeDeleteOrphans(db)
insertTweets := tweets.MakeInsert(db, insertSingleQuote, deleteOrphanQuotes)
Expand All @@ -78,6 +84,7 @@ func main() {
router := http.NewServeMux()
router.HandleFunc("GET /ping/v1", ping.HandlerV1())
router.HandleFunc("POST /migrations/run/v1", migrations.RunHandlerV1(runMigrations))
router.HandleFunc("POST /auth/signup/v1", auth.SignUpHandlerV1(signUp))
router.HandleFunc("POST /tweets/v1", tweets.InsertHandlerV1(insertTweets))
router.HandleFunc("POST /criteria/{criteria_id}/enqueue/v1", criteria.EnqueueHandlerV1(enqueueCriteria))
router.HandleFunc("POST /criteria/init/v1", criteria.InitHandlerV1(initCriteria))
Expand Down

0 comments on commit 4828186

Please sign in to comment.