# `Mix.Statifier.Corpus.Registry`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.6.0/lib/mix/statifier/corpus/registry.ex#L1)

Derives `conformance/registry.json`, statifier-ex's claim against its own
corpus, from the regression ratchet (ADR-0070 decisions 3 and 4).

`test/passing_tests.json` stays ADR-0006's ratchet file and `mix
test.baseline` the only thing that grows it; the emitter calls `derive/4`
with the ratchet's SCION and W3C paths and writes what `encode/1` returns.
Nothing else writes the registry. The `internal_tests` globs never reach
this module: they name this repository's unit tests, not corpus cases.

Each ratchet path names the generated test module of exactly one corpus
case, and that case becomes one entry `{case_id, suite}`. A path that names
no case, or a path two cases share, stops the derivation naming it. A case
in the corpus but not in the ratchet has no entry: its absence is the claim
that statifier-ex does not pass it. A case with no generated test module -
a `statifier` case - has no ratchet path, so it enters the registry only
once the ratchet can name it.

Claims are per suite, with no tiers: `scion`, `w3c-mandatory`,
`w3c-optional` and `statifier`, the W3C suite split by each case's
conformance class. A suite with no entries is not claimed, and a registry
with no entries is refused: a claim of nothing is a defect.

The file is sorted - claims by name, entries by suite then case id - with
one entry per line, so ratcheting a case in is a one-line diff.

# `t`

```elixir
@type t() :: %{required(String.t()) =&gt; term()}
```

A registry document, in `conformance/schema/registry.json`'s shape.

# `claim`

```elixir
@spec claim(corpus_case :: map()) :: String.t()
```

The claim a corpus case's entry counts toward.

## Examples

    iex> Mix.Statifier.Corpus.Registry.claim(%{"suite" => "w3c", "conformance" => "mandatory"})
    "w3c-mandatory"

    iex> Mix.Statifier.Corpus.Registry.claim(%{"suite" => "scion"})
    "scion"

# `derive`

```elixir
@spec derive(
  cases :: [map()],
  ratchet :: Enumerable.t(),
  corpus_hash :: String.t(),
  generated_path :: (map() -&gt; Path.t() | nil)
) :: {:ok, t()} | {:error, String.t()}
```

Derives the registry from `cases` (the corpus), `ratchet` (the ratchet's
SCION and W3C paths, globs already expanded), the corpus hash the cases
were written under, and `generated_path`, which names a case's generated
test module or returns `nil` when it has none.

## Examples

    iex> cases = [
    ...>   %{"id" => "scion/ads/view", "suite" => "scion"},
    ...>   %{"id" => "w3c/test9", "suite" => "w3c", "conformance" => "optional"},
    ...>   %{"id" => "w3c/test8", "suite" => "w3c", "conformance" => "mandatory"}
    ...> ]
    iex> path = fn c -> "test/" <> c["id"] <> "_test.exs" end
    iex> {:ok, registry} =
    ...>   Mix.Statifier.Corpus.Registry.derive(
    ...>     cases, ["test/w3c/test9_test.exs", "test/scion/ads/view_test.exs"], "sha256:00", path)
    iex> registry["claims"]
    ["scion", "w3c-optional"]
    iex> registry["entries"]
    [%{"case_id" => "scion/ads/view", "suite" => "scion"}, %{"case_id" => "w3c/test9", "suite" => "w3c"}]

# `encode`

```elixir
@spec encode(registry :: t()) :: String.t()
```

Encodes a registry: pretty, with the claims on one line and one compact
entry per line.

# `stale`

```elixir
@spec stale(committed :: binary(), cases :: [map()]) :: [String.t()]
```

Checks a committed registry against the corpus `cases`, returning one
sentence per problem: no entries at all, or an entry whose case the corpus
lacks or holds under another suite.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
