:lv_stream — mutates Phoenix.LiveView stream operations. Three kinds, each a variant
label:
swap inverts an operation for its opposite:
stream_insert(socket, name, item) <-> stream_delete(socket, name, item)Inserting where the code meant to delete (or vice versa) leaves the wrong rows in the
phx-update="stream" container. A test that asserts a stream's contents after the event
kills it; one that only checks the event returned lets it survive. Only the 3-argument form
is swapped — stream_insert also exists at arity 4 but stream_delete does not, so a
stream_insert/4 gets the option mutations below instead, keeping every mutant compiling.
at swaps the at: option between its two boundary positions on stream_insert/4:
stream_insert(socket, name, item, at: 0) <-> stream_insert(socket, name, item, at: -1)Prepend vs append (-1, the default, appends). Tests overwhelmingly assert an item is
present, rarely where — a survivor means nothing pins the insertion position. Only the
two boundary literals swap; any other at: value (a computed index, at: 2) is left to
core's IntegerLiteral. The swap substitutes exactly the at: value node, so core's
Mutare.Transform.Overlap recognises it as covering and prunes IntegerLiteral's redundant
leaf mutants (0 → 1, 0 → -1) at that node — this family's position swap is the one
informative mutant there.
limit drops the limit: entry from stream/4 and stream_insert/4:
stream(socket, name, items, limit: 10) -> stream(socket, name, items)
stream_insert(socket, name, item, at: 0, limit: 10)
-> stream_insert(socket, name, item, at: 0)The unbounded stream is a valid-but-wrong program: without the limit the client container
grows without pruning. A survivor means no test fills past the limit and asserts the pruned
contents. (Presence-drop of the entry is not core-owned; the limit's numeric value stays
IntegerLiteral's — an off-by-one on the boundary is a genuinely different question.)
No reset: mutation, deliberately: dropping reset: true is equivalent to core
BooleanLiteral's true → false flip on the same literal (false is the default), so a
drop here would double-report the same mutant — an ownership violation. The same holds for
stream_insert/4's boolean update_only: option.
Suppress one kind with # mutare:ignore[lv_stream:swap] / [lv_stream:at] /
[lv_stream:limit], or the whole family with # mutare:ignore[lv_stream]. Pipe forms
(socket |> stream_insert(:songs, song)) count toward every arity gate.
Matches direct, aliased, and bare-imported (use-injected) calls. This family has a high
equivalent-mutant rate in apps that don't assert stream contents — drop it from your preset
if that is noisy.