refac: password and code hashing & checking go through a domain hasher interface used by the use cases instead of bcrypt in the user repository and the auth handler
This commit is contained in:
1 parent
8460a078e3
commit
ff8cd5f0ec
16 files changed
+725
-88
No files matched your search
@@ -0,0 +1,362 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"RenewCMS/api/handlers/auth"
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/hashing"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
var errStubHashFailure = errors.New("stub hash failure")
|
||||
|
||||
type spyHasher struct {
|
||||
stubHasher
|
||||
compareCalls int
|
||||
}
|
||||
|
||||
func (s *spyHasher) Compare(hashed string, value string) error {
|
||||
s.compareCalls++
|
||||
return s.stubHasher.Compare(hashed, value)
|
||||
}
|
||||
|
||||
func TestCreateUserUseCaseHashesPasswordAndVerificationCode(t *testing.T) {
|
||||
t.Run("stores hashed password and hashed verification code, returns raw code", func(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
result, err := uc.CreateUser(useCases.CreateUserCommand{
|
||||
Username: "alice",
|
||||
Password: "s3cretPassw0rd",
|
||||
Email: "alice@example.com",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
stored := repo.users[result.ID]
|
||||
if stored.Password != "hashed:s3cretPassw0rd" {
|
||||
t.Errorf("expected stored password %q, got %q", "hashed:s3cretPassw0rd", stored.Password)
|
||||
}
|
||||
if stored.VerificationCode == "" || stored.VerificationCode == result.VerificationCode {
|
||||
t.Errorf("expected stored verification code to be hashed, got %q", stored.VerificationCode)
|
||||
}
|
||||
if stored.VerificationCode != "hashed:"+result.VerificationCode {
|
||||
t.Errorf("expected stored verification code %q, got %q", "hashed:"+result.VerificationCode, stored.VerificationCode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a hashing failure persists nothing", func(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{hashErr: errStubHashFailure})
|
||||
|
||||
_, err := uc.CreateUser(useCases.CreateUserCommand{
|
||||
Username: "alice",
|
||||
Password: "s3cretPassw0rd",
|
||||
Email: "alice@example.com",
|
||||
})
|
||||
|
||||
if !errors.Is(err, errStubHashFailure) {
|
||||
t.Errorf("expected the hashing error, got %v", err)
|
||||
}
|
||||
if len(repo.users) != 0 {
|
||||
t.Errorf("expected no user stored, got %d", len(repo.users))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateUserUseCaseHashesUpdatedValues(t *testing.T) {
|
||||
cases := []struct {
|
||||
method string
|
||||
plaintext string
|
||||
run func(uc *useCases.UpdateUserUseCase, plaintext string) (user.User, error)
|
||||
stored func(u user.User) string
|
||||
}{
|
||||
{
|
||||
method: "UpdatePassword",
|
||||
plaintext: "newPassw0rd",
|
||||
run: func(uc *useCases.UpdateUserUseCase, plaintext string) (user.User, error) {
|
||||
return uc.UpdatePassword(1, plaintext)
|
||||
},
|
||||
stored: func(u user.User) string { return u.Password },
|
||||
},
|
||||
{
|
||||
method: "UpdatePasswordResetCode",
|
||||
plaintext: "reset-code-123",
|
||||
run: func(uc *useCases.UpdateUserUseCase, plaintext string) (user.User, error) {
|
||||
return uc.UpdatePasswordResetCode(1, plaintext)
|
||||
},
|
||||
stored: func(u user.User) string { return u.PasswordResetCode },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.method+" stores the hashed value", func(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
repo.users[1] = user.User{ID: 1}
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
_, err := tc.run(uc, tc.plaintext)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
||||
expected := "hashed:" + tc.plaintext
|
||||
if got := tc.stored(repo.users[1]); got != expected {
|
||||
t.Errorf("expected stored value %q, got %q", expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run(tc.method+" a hashing failure persists nothing", func(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
repo.users[1] = user.User{ID: 1}
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{hashErr: errStubHashFailure})
|
||||
|
||||
_, err := tc.run(uc, tc.plaintext)
|
||||
if !errors.Is(err, errStubHashFailure) {
|
||||
t.Errorf("expected the hashing error, got %v", err)
|
||||
}
|
||||
if got := tc.stored(repo.users[1]); got != "" {
|
||||
t.Errorf("expected the repository update to never be called, got %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticateUserUseCase(t *testing.T) {
|
||||
newRepo := func() *mockUserRepo {
|
||||
repo := newMockUserRepo()
|
||||
repo.users[1] = user.User{ID: 1, Username: "alice", Password: "hashed:hunter2example"}
|
||||
return repo
|
||||
}
|
||||
|
||||
t.Run("correct password returns the user with no error", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewAuthenticateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
result, err := uc.Authenticate("alice", "hunter2example")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if result.ID != 1 {
|
||||
t.Errorf("expected user id 1, got %d", result.ID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrong password returns user.ErrInvalidCredentials", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewAuthenticateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
_, err := uc.Authenticate("alice", "wrongpassword12")
|
||||
if !errors.Is(err, user.ErrInvalidCredentials) {
|
||||
t.Errorf("expected user.ErrInvalidCredentials, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unknown username never calls the hasher", func(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
hasher := &spyHasher{}
|
||||
uc := useCases.NewAuthenticateUserUseCase(repo, hasher)
|
||||
|
||||
_, err := uc.Authenticate("bob", "hunter2example")
|
||||
if !errors.Is(err, user.ErrUserNotFound) {
|
||||
t.Errorf("expected user.ErrUserNotFound, got %v", err)
|
||||
}
|
||||
if hasher.compareCalls != 0 {
|
||||
t.Errorf("expected the hasher's Compare to never be called, got %d calls", hasher.compareCalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBcryptHasherHashAndCompare(t *testing.T) {
|
||||
hasher := hashing.NewBcryptHasher()
|
||||
|
||||
t.Run("hashing then comparing the same value succeeds", func(t *testing.T) {
|
||||
hashed, err := hasher.Hash("hunter2example")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if err := hasher.Compare(hashed, "hunter2example"); err != nil {
|
||||
t.Errorf("expected the comparison to succeed, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("comparing against the wrong value fails", func(t *testing.T) {
|
||||
hashed, err := hasher.Hash("hunter2example")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if err := hasher.Compare(hashed, "wrongpassword12"); err == nil {
|
||||
t.Errorf("expected the comparison to fail")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("hashing a value over 72 bytes fails", func(t *testing.T) {
|
||||
tooLong := strings.Repeat("a", 73)
|
||||
if _, err := hasher.Hash(tooLong); err == nil {
|
||||
t.Errorf("expected an error for a value over 72 bytes")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newHasherRouter(userRepo *mockUserRepo, hasher *stubHasher) *chi.Mux {
|
||||
token := jwtauth.New("HS256", []byte("test-secret"), nil)
|
||||
authHandler := auth.NewHandler(
|
||||
token,
|
||||
useCases.NewGetUserUseCase(userRepo),
|
||||
useCases.NewListUsersUseCase(userRepo),
|
||||
useCases.NewCreateUserUseCase(userRepo, hasher),
|
||||
useCases.NewAuthenticateUserUseCase(userRepo, hasher),
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Route("/v1/auth", func(r chi.Router) {
|
||||
r.Post("/login", authHandler.Login)
|
||||
r.Post("/register", authHandler.Register)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func newRealHasherRouter(userRepo *mockUserRepo) *chi.Mux {
|
||||
token := jwtauth.New("HS256", []byte("test-secret"), nil)
|
||||
hasher := hashing.NewBcryptHasher()
|
||||
authHandler := auth.NewHandler(
|
||||
token,
|
||||
useCases.NewGetUserUseCase(userRepo),
|
||||
useCases.NewListUsersUseCase(userRepo),
|
||||
useCases.NewCreateUserUseCase(userRepo, hasher),
|
||||
useCases.NewAuthenticateUserUseCase(userRepo, hasher),
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Route("/v1/auth", func(r chi.Router) {
|
||||
r.Post("/login", authHandler.Login)
|
||||
r.Post("/register", authHandler.Register)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func TestLoginOverHttpCannotDistinguishBadPasswordFromUnknownUsername(t *testing.T) {
|
||||
validator.InitValidator()
|
||||
|
||||
newAliceRepo := func() *mockUserRepo {
|
||||
repo := newMockUserRepo()
|
||||
hashed, err := hashing.NewBcryptHasher().Hash("hunter2example")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash password: %v", err)
|
||||
}
|
||||
repo.users[1] = user.User{ID: 1, Username: "alice", Password: hashed}
|
||||
return repo
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
}{
|
||||
{"wrong password for existing user", "alice", "wrongpassword12"},
|
||||
{"nonexistent username", "bob", "hunter2example"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
router := newRealHasherRouter(newAliceRepo())
|
||||
body, err := json.Marshal(map[string]string{"username": tc.username, "password": tc.password})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal body: %v", err)
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/auth/login", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusForbidden {
|
||||
t.Fatalf("expected status 403, got %d", rec.Code)
|
||||
}
|
||||
if rec.Body.String() != apperr.LogsErrorMessage+"\n" {
|
||||
t.Errorf("unexpected body: %q", rec.Body.String())
|
||||
}
|
||||
if rec.Header().Get("Set-Cookie") != "" {
|
||||
t.Errorf("expected no Set-Cookie header, got %q", rec.Header().Get("Set-Cookie"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisteringAUserStoresTheHashedPasswordNeverThePlaintext(t *testing.T) {
|
||||
validator.InitValidator()
|
||||
|
||||
repo := newMockUserRepo()
|
||||
router := newRealHasherRouter(repo)
|
||||
|
||||
body, err := json.Marshal(map[string]string{
|
||||
"username": "alice",
|
||||
"password": "s3cretPassw0rd",
|
||||
"email": "alice@example.com",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal body: %v", err)
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/auth/register", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if rec.Header().Get("Set-Cookie") == "" {
|
||||
t.Errorf("expected a jwt cookie to be set")
|
||||
}
|
||||
|
||||
stored, err := repo.GetByUsername("alice")
|
||||
if err != nil {
|
||||
t.Fatalf("expected the user to be stored, got %v", err)
|
||||
}
|
||||
if stored.Password == "s3cretPassw0rd" {
|
||||
t.Errorf("expected the stored password to not be the plaintext")
|
||||
}
|
||||
|
||||
hasher := hashing.NewBcryptHasher()
|
||||
if err := hasher.Compare(stored.Password, "s3cretPassw0rd"); err != nil {
|
||||
t.Errorf("expected the stored password to match the plaintext once compared, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHashingFailureDuringRegistrationSurfacesAs500AndSetsNoCookie(t *testing.T) {
|
||||
validator.InitValidator()
|
||||
|
||||
repo := newMockUserRepo()
|
||||
router := newHasherRouter(repo, &stubHasher{hashErr: errStubHashFailure})
|
||||
|
||||
body, err := json.Marshal(map[string]string{
|
||||
"username": "alice",
|
||||
"password": "s3cretPassw0rd",
|
||||
"email": "alice@example.com",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal body: %v", err)
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/auth/register", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("expected status 500, got %d", rec.Code)
|
||||
}
|
||||
if rec.Header().Get("Set-Cookie") != "" {
|
||||
t.Errorf("expected no Set-Cookie header, got %q", rec.Header().Get("Set-Cookie"))
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
domainArticle "RenewCMS/internal/domain/article"
|
||||
domainUser "RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/hashing"
|
||||
"RenewCMS/internal/infrastructure/persistence/models"
|
||||
persistence "RenewCMS/internal/infrastructure/persistence/repositories"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
@@ -20,7 +21,6 @@ import (
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -130,11 +130,13 @@ func newNotFoundErrorsRouter(articleRepo *mockArticleRepo, userRepo *mockUserRep
|
||||
useCases.NewListArticlesUseCase(articleRepo),
|
||||
useCases.NewDeleteArticleUseCase(articleRepo),
|
||||
)
|
||||
hasher := hashing.NewBcryptHasher()
|
||||
authHandler := auth.NewHandler(
|
||||
token,
|
||||
useCases.NewGetUserUseCase(userRepo),
|
||||
useCases.NewListUsersUseCase(userRepo),
|
||||
useCases.NewCreateUserUseCase(userRepo),
|
||||
useCases.NewCreateUserUseCase(userRepo, hasher),
|
||||
useCases.NewAuthenticateUserUseCase(userRepo, hasher),
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
@@ -235,11 +237,11 @@ func TestNotFoundErrorsHttp(t *testing.T) {
|
||||
|
||||
newAliceUserRepo := func() *mockUserRepo {
|
||||
repo := newMockUserRepo()
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte("hunter2example"), 12)
|
||||
hashed, err := hashing.NewBcryptHasher().Hash("hunter2example")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash password: %v", err)
|
||||
}
|
||||
repo.users[1] = domainUser.User{ID: 1, Username: "alice", Password: string(hashed)}
|
||||
repo.users[1] = domainUser.User{ID: 1, Username: "alice", Password: hashed}
|
||||
return repo
|
||||
}
|
||||
|
||||
|
||||
@@ -433,7 +433,7 @@ func TestDeleteImageRequiresExistence(t *testing.T) {
|
||||
|
||||
func TestCreateUserAssignsVerificationCode(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
uc := useCases.NewCreateUserUseCase(repo)
|
||||
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
before := time.Now()
|
||||
result, err := uc.CreateUser(useCases.CreateUserCommand{
|
||||
@@ -480,7 +480,7 @@ func TestCreateUserAssignsVerificationCode(t *testing.T) {
|
||||
func TestCreateUserPropagatesRepositoryFailure(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
repo.fail.failNext("Create")
|
||||
uc := useCases.NewCreateUserUseCase(repo)
|
||||
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
_, err := uc.CreateUser(useCases.CreateUserCommand{
|
||||
Username: "alice",
|
||||
@@ -590,7 +590,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdateVerificationStatus_id1", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
result, err := uc.UpdateVerificationStatus(1, true)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
@@ -602,7 +602,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdateVerificationStatus_id9", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
_, err := uc.UpdateVerificationStatus(9, true)
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Errorf("expected gorm.ErrRecordNotFound, got %v", err)
|
||||
@@ -611,7 +611,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePassword_id1", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
result, err := uc.UpdatePassword(1, "newpassword123")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
@@ -623,7 +623,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePassword_id9", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
_, err := uc.UpdatePassword(9, "newpassword123")
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Errorf("expected gorm.ErrRecordNotFound, got %v", err)
|
||||
@@ -632,7 +632,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePasswordResetCode_id1", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
result, err := uc.UpdatePasswordResetCode(1, "reset-code-123")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
@@ -644,7 +644,7 @@ func TestUpdateUserDelegatesEachOperation(t *testing.T) {
|
||||
|
||||
t.Run("UpdatePasswordResetCode_id9", func(t *testing.T) {
|
||||
repo := newRepo()
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
_, err := uc.UpdatePasswordResetCode(9, "reset-code-123")
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
t.Errorf("expected gorm.ErrRecordNotFound, got %v", err)
|
||||
|
||||
+21
-2
@@ -3,12 +3,31 @@ package test
|
||||
import (
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"errors"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type stubHasher struct {
|
||||
hashErr error
|
||||
}
|
||||
|
||||
func (s *stubHasher) Hash(value string) (string, error) {
|
||||
if s.hashErr != nil {
|
||||
return "", s.hashErr
|
||||
}
|
||||
return "hashed:" + value, nil
|
||||
}
|
||||
|
||||
func (s *stubHasher) Compare(hashed string, value string) error {
|
||||
if hashed == "hashed:"+value {
|
||||
return nil
|
||||
}
|
||||
return errors.New("hash mismatch")
|
||||
}
|
||||
|
||||
type mockUserRepo struct {
|
||||
users map[uint32]user.User
|
||||
nextID uint32
|
||||
@@ -134,7 +153,7 @@ var _ user.Repository = &mockUserRepo{}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
uc := useCases.NewCreateUserUseCase(repo)
|
||||
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
cmd := useCases.CreateUserCommand{
|
||||
Username: "testuser",
|
||||
@@ -157,7 +176,7 @@ func TestCreateUser(t *testing.T) {
|
||||
func TestUpdateUserVerification(t *testing.T) {
|
||||
repo := newMockUserRepo()
|
||||
repo.users[1] = user.User{ID: 1, IsVerified: false}
|
||||
uc := useCases.NewUpdateUserUseCase(repo)
|
||||
uc := useCases.NewUpdateUserUseCase(repo, &stubHasher{})
|
||||
|
||||
res, err := uc.UpdateVerificationStatus(1, true)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user