All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.2.1 - 2026-10-03
Changed
- Requires Mutare 0.5 (
{:mutare, "~> 0.5.0"}).
0.2.0 - 2026-09-26
Changed
- Breaking: Mutare 0.4.1 or newer is required (
{:mutare, "~> 0.4.1"}). Mutare now offers a pipe stage to a mutator as the call it is sugar for, so the families no longer carry pipe-specific replacements:- A removed piped stage reads as what was piped into it:
new() |> header("X-Tag", v)→new()(washeader("X-Tag", v)→Elixir.Function.identity()). The same holds for theattachment/2,reply_to/2andput_provider_option/3removals. - A piped
Mailer.deliver()/deliver!()stage is replaced over the whole pipe:email |> Mailer.deliver()→{:ok, %{}}(wasElixir.Kernel.then(fn _ -> {:ok, %{}} end)). The email is still built. # mutare:ignoredirectives and--lineselections on a stage keep working as before.
- A removed piped stage reads as what was piped into it:
0.1.1 - 2026-09-07
Fixed
- No compiler warning on Elixir 1.20. The header family contained a catch-all clause that Elixir's type inference proves unreachable. Elixir 1.20 reports this while compiling the dependency (which failed the package's own warnings-as-errors CI). The clause is gone; behaviour is unchanged.
0.1.0 - 2026-09-07
Initial release.
Added
- Eight mutator families over the Swoosh email surface, each reported under
its own name and independently enable-able:
Mutare.Swoosh.Recipient(:swoosh_recipient) — swaps recipient classes (to/cc/bcc, andput_to/put_cc/put_bccamong themselves), deletes one recipient from literal recipient values, and weakensput_*→ plain (append) one-directionally — checking whether tests detect a failure to replace recipients (each such mutant includes an equivalence note: a kill may require pre-existing recipients).Mutare.Swoosh.Sender(:swoosh_sender) — swapsfrom↔reply_toand dropsreply_to(delete— replies silently go tofrom).Mutare.Swoosh.Subject(:swoosh_subject) — blanks the subject (subject(email, v)→subject(email, ""); thesubject:option innew/1is dropped).Mutare.Swoosh.Body(:swoosh_body) — swapshtml_body↔text_body.Mutare.Swoosh.Header(:swoosh_header) — removesheader/3and drops entries from aheaders:option innew/1.Mutare.Swoosh.Attachment(:swoosh_attachment) — swaps attachment disposition (type: :inline↔:attachment) and removes theattachment/2call /attachment:option entirely (delete).Mutare.Swoosh.ProviderOption(:swoosh_provider_option) — removesput_provider_option/3(provider template ids and dynamic template data are exactly the untested things).Mutare.Swoosh.Deliver(:swoosh_deliver) — skips delivery: replacesMailer.deliver/1,2with a non-delivering{:ok, %{}}(anddeliver!/1,2with%{}), checking whether tests assert that the email was actually sent. Configurable with the application's mailer(s):{Mutare.Swoosh.Deliver, mailer: MyApp.Mailer}, orMutare.Swoosh.all(mailer: MyApp.Mailer); produces no mutations without it.
- Every email-field family mutates both the pipeline call and the matching
Swoosh.Email.new/1option; calls match written qualified, aliased, imported, or piped. - Ignore-variant labels throughout, so
# mutare:ignore[family:label]can suppress one kind of mutant (e.g.[swoosh_recipient:append],[swoosh_deliver:deliver!]). Mutare.Swoosh.all/0for splicing all families into a:mutatorslist;Mutare.Swoosh.all/1to configure the deliver family'smailer:in place.