4.8 KiB
4.8 KiB
QA procedure: hasher_domain_interface
Package: test (black-box, alongside the existing article_test.go / user_test.go /
use_case_baseline_test.go / not_found_errors_test.go).
Fakes needed
- A
fakeHasherimplementinghash.Hasher:Hash(value string) (string, error): by default returns"hashed:" + value, nil; configurable to return("", someErr)unconditionally.Compare(hashed, value string) error: by default returnsnilwhenhashed == "hashed:"+value, else a sentinel mismatch error; configurable to always error, and to record whether it was called.
- Extend the existing
mockUserRepo(fromtest/user_test.go/not_found_errors_test.go) with call-recording flags already used elsewhere in the package (createCalled,updatePasswordCalled, etc.) if not already present, so tests can assert a hashing failure short-circuits before the repository is touched.
Use-case-level checks (fake hasher + fake repo, no HTTP)
useCases.NewCreateUserUseCase(fakeRepo, fakeHasher),CreateUser(...): assert theUserargument the fake repo'sCreatereceives hasPassword == "hashed:s3cretPassw0rd"and aVerificationCodethat starts with"hashed:"; assert theUserreturned by the use case has aVerificationCodethat does not start with"hashed:"(it is the raw UUID, unchanged from current behaviour).- Same use case with
fakeHasherconfigured to always error onHash: assertCreateUserreturns that error andfakeRepo.Createwas never called. useCases.NewUpdateUserUseCase(fakeRepo, fakeHasher), callUpdatePassword(1, "newPassw0rd")and separatelyUpdatePasswordResetCode(1, "reset-code-123"): assert the fake repo receives"hashed:newPassw0rd"/"hashed:reset-code-123", not the plaintext.- Same two methods with
fakeHasher.Hasherroring: assert the use case returns the error and the corresponding fake-repo method (UpdatePassword/UpdatePasswordResetCode) was never called. useCases.NewAuthenticateUserUseCase(fakeRepo, fakeHasher)(new use case): seedfakeRepowith a user "alice" whose storedPasswordis"hashed:hunter2example".Authenticate("alice", "hunter2example")withfakeHasher.Comparereturningnil→ assert the returned user matches anderrisnil.Authenticate("alice", "wrongpassword12")withfakeHasher.Comparereturning an error → asserterrors.Is(err, user.ErrInvalidCredentials)istrue.Authenticate("bob", "hunter2example")with no "bob" in the repo → asserterrors.Is(err, user.ErrUserNotFound)istrueandfakeHasher'sComparewas never invoked (record acompareCalledflag).
HTTP checks (real BcryptHasher, in-memory fake repo, mirroring api/router.go)
- Call
validator.InitValidator(). - Build
hashing.NewBcryptHasher(), wireuseCases.NewCreateUserUseCase(fakeRepo, bcryptHasher)anduseCases.NewAuthenticateUserUseCase(fakeRepo, bcryptHasher)(plus the existingGetUserUseCase/ListUsersUseCasethe auth handler still needs), buildauth.NewHandler(token, get, list, create, authenticate)(constructor signature grows by the new use case) with a testjwtauth.New("HS256", []byte("test-secret"), nil), mount its routes unprotected on a freshchi.NewRouter()as inapi/router.go. - Seed the fake user repo directly with a user "alice" whose
Passwordfield holds a realbcryptHasher.Hash("hunter2example")result (notbcrypt.GenerateFromPasswordcalled from the test, so the test never imports bcrypt itself). POST /v1/auth/loginwith{"username":"alice","password":"wrongpassword12"}→ assert status403, bodyapperr.LogsErrorMessage + "\n", noSet-Cookie: jwt=...header.POST /v1/auth/loginwith{"username":"bob","password":"hunter2example"}(no such user) → assert identical status/body/no-cookie outcome to step 11 (regression, same asnot_found_errors.qa.mdsteps 13–14).POST /v1/auth/registerwith{"username":"alice","password":"s3cretPassw0rd","email":"alice@example.com"}against an empty fake repo → assert status200, aSet-Cookie: jwt=...header is present, and thePasswordthe fake repo'sCreatereceived is neither empty nor equal to"s3cretPassw0rd"; assertbcryptHasher.Compare(storedPassword, "s3cretPassw0rd")returnsnil.- Rebuild the handler with a
fakeHasher(from step 1) whoseHashalways errors in place ofbcryptHasher, same empty fake repo.POST /v1/auth/registerwith the same valid body → assert status500, noSet-Cookieheader, and the fake repo'sCreatewas never called.
Manual verification (not exercised by the Go test)
- None. This slice has no SPA-visible behaviour and no SMTP involvement.