Mutare.Phoenix (mutare_phoenix v0.3.1)

Copy Markdown View Source

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 :status option of Phoenix.Controller.redirect/2 for a redirect-status sibling.
  • Mutare.Phoenix.Body — :controller_body, blanks the body of Phoenix.Controller.json/2 (to %{}), text/2, and html/2 (to "").
  • Mutare.Phoenix.Download — :download_disposition, flips the explicit :disposition option of Phoenix.Controller.send_download/3 between :attachment and :inline.
  • Mutare.Phoenix.ChannelReply — :channel_reply, drops the reply element of a Phoenix.Channel callback 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 a Phoenix.Channel outbound message — broadcast/3 and its !/_from siblings, push/3, reply/2 — collapsing the call to :ok.
  • Mutare.Phoenix.PubSub — :pubsub, removes a Phoenix.PubSub subscribe, unsubscribe, or broadcast call, collapsing it to :ok.
  • Mutare.Phoenix.Token — :token, swaps a Phoenix.Token call for its sibling scheme (sign ↔ encrypt, verify ↔ decrypt), blanks a generated token's payload to nil, and turns an explicit max_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.Router DSL (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 unreachable sigil_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

all()

@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]