Custom Mutare mutators for the Phoenix server-side
surface — the Phoenix.Controller calls a controller action performs on the conn, the
Phoenix.Channel replies and outbound messages, Phoenix.PubSub, and Phoenix.Token —
plus macro routing that excludes Phoenix's compile-time macros from mutation to prevent
metamutant compilation failures.
This package builds on mutare_plug the way phoenix builds on plug: it depends on it,
so the Plug.Conn families (Mutare.Plug.all/0) are on your code path too, ready to
compose.
Usage
Add mutare_phoenix to your deps, then list the families in .mutare.exs and this module
under :extensions. Setting :mutators replaces Mutare's default set, so include the
:builtins family to keep the built-ins on:
# .mutare.exs
[
mutators: [:builtins] ++ Mutare.Plug.all() ++ Mutare.Phoenix.all(),
extensions: [Mutare.Phoenix]
]all/0 returns this package's seven families:
Mutare.Phoenix.Redirect—:redirect_status, swaps the explicit atom:statusoption ofPhoenix.Controller.redirect/2for a redirect-status sibling.Mutare.Phoenix.Body—:controller_body, blanks the body ofPhoenix.Controller.json/2(to%{}),text/2, andhtml/2(to"").Mutare.Phoenix.Download—:download_disposition, flips the explicit:dispositionoption ofPhoenix.Controller.send_download/3between:attachmentand:inline.Mutare.Phoenix.ChannelReply—:channel_reply, drops the reply element of aPhoenix.Channelcallback return ({:ok, reply, socket}→{:ok, socket},{:reply, reply, socket}→{:noreply, socket},{:stop, reason, reply, socket}→{:stop, reason, socket}), gated on@behaviour Phoenix.Channel.Mutare.Phoenix.ChannelMessage—:channel_message, removes aPhoenix.Channeloutbound message —broadcast/3and its!/_fromsiblings,push/3,reply/2— collapsing the call to:ok.Mutare.Phoenix.PubSub—:pubsub, removes aPhoenix.PubSubsubscribe,unsubscribe, or broadcast call, collapsing it to:ok.Mutare.Phoenix.Token—:token, swaps aPhoenix.Tokencall for its sibling scheme (sign↔encrypt,verify↔decrypt), blanks a generated token's payload tonil, and turns an explicitmax_age:into:infinity.
It does not include the mutare_plug families; compose Mutare.Plug.all/0 explicitly
as shown above. Each call family matches its call written directly
(Phoenix.Controller.redirect(conn, ...)), aliased, or bare-imported
(redirect(conn, ...), the form use MyAppWeb, :controller produces; broadcast(socket, ...), the form use Phoenix.Channel produces).
The :extensions entry
This module is also a Mutare.CallRouting extension. Listed under :extensions, it
routes Phoenix's compile-time-only macro calls :skip — the whole call is an inert leaf, so
Mutare neither descends into their arguments nor rewrites the calls themselves:
- the
Phoenix.RouterDSL (get/post/scope/…), because route definitions run once at compile time under Mutare's compile-once model — a mutation there could never activate; Phoenix.Component.sigil_H/2(~H), because HEEx sigil arguments must remain compile-time literals — left unregistered, Mutare's imported-call witness would splice an unreachablesigil_H(arg1, arg2)that Phoenix rejects at compile time, failing the whole metamutant build before poison recovery can isolate a single mutant.
Mutations around a ~H expression (for example a render/1 :return_value mutant)
remain available; only the sigil's own arguments are opaque.
Summary
Functions
This package's mutator families, for splicing into :mutators (see the module docs for
the Mutare.Plug.all/0 and :builtins pairing).
Functions
@spec all() :: [module()]
This package's mutator families, for splicing into :mutators (see the module docs for
the Mutare.Plug.all/0 and :builtins pairing).
iex> Mutare.Phoenix.all()
[Mutare.Phoenix.Redirect, Mutare.Phoenix.Body, Mutare.Phoenix.Download,
Mutare.Phoenix.ChannelReply, Mutare.Phoenix.ChannelMessage, Mutare.Phoenix.PubSub,
Mutare.Phoenix.Token]