Files
RenewCMS/features/hasher_domain_interface.qa.md

4.3 KiB
Raw Permalink Blame History

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

  1. A fakeHasher implementing hash.Hasher:
    • Hash(value string) (string, error): by default returns "hashed:" + value, nil; configurable to return ("", someErr) unconditionally.
    • Compare(hashed, value string) error: by default returns nil when hashed == "hashed:"+value, else a sentinel mismatch error; configurable to always error, and to record whether it was called.
  2. Extend the existing mockUserRepo (from test/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)

  1. useCases.NewCreateUserUseCase(fakeRepo, fakeHasher), CreateUser(...): assert the User argument the fake repo's Create receives has Password == "hashed:s3cretPassw0rd" and a VerificationCode that starts with "hashed:"; assert the User returned by the use case has a VerificationCode that does not start with "hashed:" (it is the raw UUID, unchanged from current behaviour).
  2. Same use case with fakeHasher configured to always error on Hash: assert CreateUser returns that error and fakeRepo.Create was never called.
  3. useCases.NewAuthenticateUserUseCase(fakeRepo, fakeHasher) (new use case): seed fakeRepo with a user "alice" whose stored Password is "hashed:hunter2example".
    • Authenticate("alice", "hunter2example") with fakeHasher.Compare returning nil → assert the returned user matches and err is nil.
    • Authenticate("alice", "wrongpassword12") with fakeHasher.Compare returning an error → assert errors.Is(err, user.ErrInvalidCredentials) is true.
    • Authenticate("bob", "hunter2example") with no "bob" in the repo → assert errors.Is(err, user.ErrUserNotFound) is true and fakeHasher's Compare was never invoked (record a compareCalled flag).

HTTP checks (real BcryptHasher, in-memory fake repo, mirroring api/router.go)

  1. Call validator.InitValidator().
  2. Build hashing.NewBcryptHasher(), wire useCases.NewCreateUserUseCase(fakeRepo, bcryptHasher) and useCases.NewAuthenticateUserUseCase(fakeRepo, bcryptHasher) (plus the existing GetUserUseCase / ListUsersUseCase the auth handler still needs), build auth.NewHandler(token, get, list, create, authenticate) (constructor signature grows by the new use case) with a test jwtauth.New("HS256", []byte("test-secret"), nil), mount its routes unprotected on a fresh chi.NewRouter() as in api/router.go.
  3. Seed the fake user repo directly with a user "alice" whose Password field holds a real bcryptHasher.Hash("hunter2example") result (not bcrypt.GenerateFromPassword called from the test, so the test never imports bcrypt itself).
  4. POST /v1/auth/login with {"username":"alice","password":"wrongpassword12"} → assert status 403, body apperr.LogsErrorMessage + "\n", no Set-Cookie: jwt=... header.
  5. POST /v1/auth/login with {"username":"bob","password":"hunter2example"} (no such user) → assert identical status/body/no-cookie outcome to step 11 (regression, same as not_found_errors.qa.md steps 13–14).
  6. POST /v1/auth/register with {"username":"alice","password":"s3cretPassw0rd","email":"alice@example.com"} against an empty fake repo → assert status 200, a Set-Cookie: jwt=... header is present, and the Password the fake repo's Create received is neither empty nor equal to "s3cretPassw0rd"; assert bcryptHasher.Compare(storedPassword, "s3cretPassw0rd") returns nil.
  7. Rebuild the handler with a fakeHasher (from step 1) whose Hash always errors in place of bcryptHasher, same empty fake repo. POST /v1/auth/register with the same valid body → assert status 500, no Set-Cookie header, and the fake repo's Create was never called.

Manual verification (not exercised by the Go test)

  • None. This slice has no SPA-visible behaviour and no SMTP involvement.