Reads a <chart>.fixtures.json sidecar (ADR-0003) and produces the same
StatifierUI.Fixtures struct the behaviour-based delivery path produces.
Uses the stdlib JSON module (Elixir 1.18) to decode, never jason -
jason reaches this repository only through the optional
phoenix_live_view dependency and must not become load-bearing for core
fixture loading.
Every top-level key beyond "version", "scenarios", "events",
"datasets", and "expressions" is ignored with a :unknown_key
diagnostic (ADR-0006's extension-friendly requirement), and every value
under "scenarios", "events", and "datasets" is decoded through
StatifierUI.Value.decode/1.
A $duration cannot appear inside "scenarios" or "datasets": it decodes
to an atom-keyed map, and a datamodel forbids atom keys at every level (the
engine's own invariant, tightened in StatifierUI.Fixtures). Such a
sidecar is rejected with {:duration_in_scenario, path} or
{:duration_in_dataset, path}. Durations under "events" are fine - an
_event.data payload has no key constraint.
An "expressions" entry is an object rather than a free value, so it is
decoded on its own: "source" passes through untouched (it is source text,
never a tagged value), each "expect" value is decoded through
StatifierUI.Value.decode/1 (a duration is fine there - "expect" values
are predicator values, not datamodel keys), and any other key in the entry
is copied through verbatim rather than rejected or flagged, per ADR-0006's
ignore-unknown-keys discipline extended to this depth.
Summary
Functions
Converts an already JSON-decoded sidecar map into a validated
StatifierUI.Fixtures struct.
Reads, parses, and validates a sidecar file at path, returning the same
struct StatifierUI.Fixtures.from_source/1 produces.
Derives the sidecar path for chart_path and loads it.
Derives a sidecar path from a chart path, per ADR-0003's naming:
authorization.scxml becomes authorization.fixtures.json. A path with no
extension has .fixtures.json appended.
Functions
@spec from_json( map(), keyword() ) :: {:ok, StatifierUI.Fixtures.t()} | {:error, term()}
Converts an already JSON-decoded sidecar map into a validated
StatifierUI.Fixtures struct.
Options:
:source- the file the map was read from, recorded on each diagnostic and named in each logged warning.load/1passes it; omitting it is for a map that never was a file.
@spec load(Path.t()) :: {:ok, StatifierUI.Fixtures.t()} | {:error, term()}
Reads, parses, and validates a sidecar file at path, returning the same
struct StatifierUI.Fixtures.from_source/1 produces.
Every error names path, because a corpus run loads many sidecars and a
bare POSIX reason does not say which one failed.
@spec load_for_chart(Path.t()) :: {:ok, StatifierUI.Fixtures.t()} | {:error, term()}
Derives the sidecar path for chart_path and loads it.
A missing sidecar is {:error, {:sidecar_not_found, path}}, not an empty
bundle - whether "no fixtures" is acceptable is a caller-level decision,
and a typo'd chart path must not look like a chart without fixtures.
Derives a sidecar path from a chart path, per ADR-0003's naming:
authorization.scxml becomes authorization.fixtures.json. A path with no
extension has .fixtures.json appended.
Examples
iex> StatifierUI.Fixtures.Sidecar.sidecar_path("authorization.scxml")
"authorization.fixtures.json"
iex> StatifierUI.Fixtures.Sidecar.sidecar_path("authorization")
"authorization.fixtures.json"