Files
RenewCMS/features/hasher_domain_interface.qa.md
T

4.4 KiB
Raw 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") twice with no "bob" in the repo → assert errors.Is(err, user.ErrUserNotFound) is true each time, that fakeHasher's Compare ran once per attempt (against the decoy hash), and that Hash ran exactly once overall (the decoy is computed lazily and reused).

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.