domain: in-memory mock repo diverges from Postgres (reset-token action_type; missing EmailTaken) #63
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
rosa/vernier#63
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during a code review of the
domaincrate.Location:
crates/domain/src/mocks.rs:250andcrates/domain/src/mocks.rs:167/208Severity: Correctness — test/prod divergence (mock is feature-gated)
Problem
Two divergences let tests pass on behavior the real Postgres repo rejects:
delete_password_reset_tokens_for_userretains onuser_idonly, with noaction_typefilter, while Postgres filtersaction_type = PasswordReset. On the mock, a successful login also deletes a pending email-confirmation token, orphaning email confirmation.create_userchecks only username uniqueness (duplicate email silently overwritesemail_index, never returnsRegistrationError::EmailTaken), andupdate_usernever returnsEmailTaken, while Postgres enforces the unique email constraint.Failure scenario
Dev/test flows against the mock confirm behavior (log in before confirming email; register two accounts with the same email) that would error against Postgres, hiding real bugs.
Suggested fix
Bring the mock in line with the Postgres WHERE clauses/constraints: filter by
action_type, and enforce email uniqueness increate_user/update_user.Agent Brief
Category: bug
Summary: Bring the in-memory
MemoryAppRepomock in line with the Postgres adapter on confirmation-deletion scope and email-uniqueness enforcement, so tests can't pass on behavior the real repo rejects.Current behavior:
The
MemoryAppRepomock (feature-gated, used by tests) diverges from the PostgresAppRepositoryimplementation in four places, letting tests confirm behavior Postgres would reject:delete_password_reset_tokens_for_userdeletes all confirmations for the user, ignoringaction_type. Postgres deletes onlyPasswordResetconfirmations. On the mock, deleting reset tokens also destroys a pending email-confirmation token.update_user_passwordhas the same over-broad deletion: it removes all confirmations for the user, where Postgres deletes onlyPasswordResetones. (Not called out in the original report — same class of bug as #1 in a sibling method.)create_userchecks only username uniqueness; a duplicate email silently overwrites the email index and the call succeeds, where Postgres returnsRegistrationError::EmailTakenon the unique email constraint.update_usernever returnsUpdateUserError::EmailTaken; it checks only username uniqueness, where Postgres returnsEmailTakenon the unique email constraint.Desired behavior:
The mock matches the Postgres adapter's observable contract:
delete_password_reset_tokens_for_userandupdate_user_passwordare scoped toPasswordResetconfirmations only, leavingEmailConfirmation(and any other action type) confirmations intact. The mock already demonstrates the correct pattern inmint_password_reset_confirmation, which retains confirmations unless both the user matches and the action type isPasswordReset.create_userreturnsRegistrationError::EmailTakenwhen the email is already registered to another user, and does not mutate any state in that case.update_userreturnsUpdateUserError::EmailTakenwhen the requested email already belongs to a different user. It must still allow a user to keep their own unchanged email — mirror the existing username guard, which only rejects when the value actually changed and collides with another record.Key interfaces:
AppRepository::delete_password_reset_tokens_for_userandAppRepository::update_user_passwordonMemoryAppRepo— narrow the confirmation deletion to thePasswordResetaction type.AppRepository::create_useronMemoryAppRepo— enforce email uniqueness before inserting, returningRegistrationError::EmailTaken.AppRepository::update_useronMemoryAppRepo— enforce email uniqueness on change, returningUpdateUserError::EmailTaken, without falsely rejecting an unchanged email.ConfirmationAction::PasswordResetvariant and the confirmation'saction_type()accessor already exist and are used elsewhere in the mock.Acceptance criteria:
delete_password_reset_tokens_for_user, a pendingEmailConfirmationconfirmation for the same user still exists; reset confirmations are gone.update_user_password, a pendingEmailConfirmationconfirmation for the same user still exists; reset confirmations are gone.create_userwith an email already registered to another user returnsRegistrationError::EmailTakenand leaves existing state unchanged.update_usertargeting an email owned by a different user returnsUpdateUserError::EmailTaken.update_userthat keeps the user's own email unchanged succeeds (no falseEmailTaken).mise run format,mise run clippy, andmise run cipass.Out of scope: