API Reference mutare_ecto v#0.3.0

Copy Markdown View Source

Modules

A mutation-testing plugin for Ecto — a Mutare.Mutator that mutates the Ecto surface (Repo calls, changeset pipelines, and the query DSL) while respecting SQL semantics.

Positional binding-reorder mutants for every standalone/pipe query macro that takes a binding pattern list — the where/having condition macros, select, select_merge, order_by, group_by, distinct, join, preload, windows, …, and the free-standing dynamic/2 (the condition/clause/join/dynamic descriptors in Mutare.Ecto.Surface).

Drop a transparent step from a changeset pipeline — the changeset equivalent of removing a guard. cs |> validate_required([:name]) → cs. A surviving mutant means no test exercises the behaviour that step contributes. Matched by resolving the call to Ecto.Changeset (direct, aliased, or the common import Ecto.Changeset), so the form the call uses doesn't matter.

The per-argument routing classifier for the Ecto.Changeset pipeline stages the plugin mutates (Mutare.Ecto.Changeset.stages/0 — every validator, constraint, and Repo-time hook it can drop) — the changeset counterpart of the query classifier Mutare.Ecto.Host.Routing. Nothing here is hosted; the classifier only holds back from core's families the positions where a core swap would be a crash, not a mutant, so a run that lists the plugin next to :all reports no crasher in a changeset stage. The test of each pin is Ecto's own handling of the swapped value

Standalone/pipe clause-macro mutations — the composable counterparts of the whole-from family in Mutare.Ecto.Query. Where Query mutates from's keyword clauses, this mutates the same kinds of thing written as standalone calls or pipe stages

Drop a standalone/pipe query clause — the composable counterpart of Mutare.Ecto.Query's whole-from clause drops, analogous to Mutare.Ecto.Changeset's validator drop. Where Query removes a where:/limit:/group_by: clause from a from(…) keyword list, this removes the same kinds of clause written as a standalone call or pipe stage

In-fragment SQL mutations for a free-standing dynamic/1,2 call — the condition a user builds ahead of time and splices later

The plugin's own SQL-semantics mutation catalog for a query fragment — the boolean condition of a where/having clause or of a free-standing dynamic/1,2. Given a condition's AST, mutants/1 returns every single-point variant: the same condition with exactly one operator/predicate swapped, one variant per mutatable position. Each variant is a full, compile-safe alternative the SQL engine will actually run; the host (Mutare.Ecto.Host) weaves them behind a ^/dynamic selector so one of them bakes into the query per run, while Mutare.Ecto.Dynamic rebuilds a free-standing dynamic call whole (an ordinary expression position, needing no weave).

Builds selector-host targets for localized Ecto query conditions.

Classifies arguments of registered query macros for Mutare.CallRouting.route_arguments/1. Core follows these routes; Mutare.Ecto.Host supplies the targets at :hosted positions.

Whole-from query mutations — the ones expressible without Mutare's foreign-semantics DSL host. Each returns a whole mutated from(...) node, which Mutare's ordinary in-place selector wraps; the localized in-fragment mutations (operator swaps inside a where, via ^/dynamic) arrive with the host extensions.

Swap Ecto.Query.first/1,2 ↔ Ecto.Query.last/1,2 — the query "terminals" that restrict a query to a single edge of its ordering. first keeps the first row by the order (primary key ascending when none is given); last reverses it. Swapping them is a clean, same-arity rename (so it always compiles), behaviour-changing whenever the query can return more than one row — a survivor means no test pins which end the query is taking.

Swap the aggregate of a Repo.aggregate/3 call along an SQL-meaningful ladder — :sum↔:avg, :min↔:max. A surviving mutant means no test distinguishes, say, the sum of a column from its average: the aggregate is computed but its kind is unchecked.

Mutations on the persisting Repo writes — insert/update/delete/insert_or_update and their ! variants. Two families, both matched by resolving the call's module to one of the configured repo: modules (so direct, aliased, and use Ecto.Repo-defined forms all match)

The condition the host cannot weave, and its whole-call delivery.

Swap a validate_number/3 bound between its strict and non-strict form — the changeset counterpart of the in-query :comparison swap. validate_number(:age, greater_than: 0) → validate_number(:age, greater_than_or_equal_to: 0). "Is the bound itself tested?" — the two validations accept the same values except one exactly on the bound, so the mutant survives unless a test builds a changeset whose value equals it and asserts the validation result. Family :validation_boundary; equivalence-sensitive (Mutare.Ecto.Equivalence), because a kill needs exactly that fixture.