:token — mutates the Phoenix.Token calls that generate and read signed or encrypted
tokens. Three kinds, each a variant label:
scheme swaps the call for its sibling scheme — sign/3,4 ↔ encrypt/3,4,
verify/3,4 ↔ decrypt/3,4 — so a token generated at the mutated site can no longer be
read by the corresponding verify or decrypt call:
Phoenix.Token.sign(endpoint, "user auth", user.id) -> Phoenix.Token.encrypt(...)
Phoenix.Token.verify(endpoint, "user auth", token) -> Phoenix.Token.decrypt(...)A survivor means no test ever verifies a token this site signed (or decrypts one it encrypted, or passes a real token to this reader) — the email is asserted to contain a link, say, and the link is never followed.
payload blanks the data a sign/encrypt call embeds, to nil:
Phoenix.Token.sign(endpoint, "user auth", user.id) -> Phoenix.Token.sign(endpoint, "user auth", nil)A survivor means the token still verifies but no test checks its payload — a
{:ok, _} = verify(...) that never compares the payload.
expiry disables the explicit max_age: of a verify/decrypt call:
Phoenix.Token.verify(endpoint, salt, token, max_age: 86_400) -> max_age: :infinityA survivor means no test checks that an expired token is rejected. Only an explicit
integer literal is mutated; a call relying on Phoenix's default (one day), or passing a
variable or :infinity, is left alone. The position is marked with the shared :timeout label
(Mutare.Mutator.argument_marks/1), so Mutare's built-in :integer family skips the
duration literal. The expiry mutation tests whether expiry is enforced; an off-by-one
change such as 86_400 → 86_401 would be difficult to detect reliably in a test.
The built-in :atom family also skips an explicit :infinity in this position.
Suppress one kind with # mutare:ignore[token:expiry], or the whole family with
# mutare:ignore[token]. This family does not mutate the salt: Mutare's built-in
:string family mutates literal salts, and a salt stored in a module attribute is
compile-time data that Mutare never mutates.
Only the defined arities match — 3 and 4 for all four calls — so a name-matched call of any
other arity (reachable only by an explicit qualifier) is left alone, keeping every
metamutant compiling. A piped call (endpoint |> Phoenix.Token.sign(salt, data)) is the
same call with the context argument (an endpoint, conn, or socket) first. Matches direct
(Phoenix.Token.sign(...)), aliased, and bare-imported calls.