Temper.Doctor (temper v0.3.0)

Copy Markdown View Source

Preflight checks behind mix temper.doctor, diagnosing the setup mistakes that fail silently: a formatter that never registers, a history_path invisible outside the test env, a suite that records nothing, records without a usable commit SHA.

gather/2 is the boundary: it evaluates the project's config for the test env (via Config.Reader — config files are data, nothing is applied), loads each umbrella child's real dependency list, scans test_helper.exs files for mentions, and reads the recorded history. evaluate/1 and render/1 are pure: facts in, checks out; checks in, report out.

The checks follow one evidence policy: a check passes only on evaluated evidence (a computed config value, a loaded project's deps) or behavioral evidence (recorded history). Source-code scans cannot decide what code does at runtime, so a scan result is never worth more than a warning.

Summary

Types

One check result: what was checked, how it went, and how to fix it.

Everything evaluate/1 needs, gathered from one project root.

Functions

Runs every applicable check against the gathered facts.

Gathers the facts the checks run on, relative to root.

Whether any check failed — the mix task's exit status.

Renders the checks as the terminal report, summary line included.

Types

check()

@type check() :: %{
  title: String.t(),
  status: :ok | :warn | :fail,
  detail: String.t(),
  hint: String.t() | nil
}

One check result: what was checked, how it went, and how to fix it.

facts()

@type facts() :: %{
  config: %{
    status: :ok | :missing | {:error, String.t()},
    formatters: [module()] | nil,
    history_path: String.t() | nil
  },
  helper_mentions: [Path.t()],
  umbrella: :not_umbrella | %{children: [Path.t()], without_dep: [Path.t()]},
  current_history_path: String.t() | nil,
  history: %{glob: Path.t(), result: Temper.History.Reader.result()},
  runs: %{last_test_run: integer() | nil, last_recorded: integer() | nil}
}

Everything evaluate/1 needs, gathered from one project root.

Functions

evaluate(facts)

@spec evaluate(facts()) :: [check()]

Runs every applicable check against the gathered facts.

The umbrella check only appears for umbrella projects; the SHA check only when there are records to inspect.

gather(root \\ ".", opts \\ [])

@spec gather(
  Path.t(),
  keyword()
) :: facts()

Gathers the facts the checks run on, relative to root.

:runs carries two POSIX mtimes: the newest ExUnit failures manifest (mix test rewrites it on every run, recording or not) and the newest history file. Comparing them tells whether the last test run actually recorded — nil when the file does not exist.

Options:

  • :history — read this path or glob instead of the configured or default one (mirrors the mix tasks' --history)

problems?(checks)

@spec problems?([check()]) :: boolean()

Whether any check failed — the mix task's exit status.

render(checks)

@spec render([check()]) :: String.t()

Renders the checks as the terminal report, summary line included.