Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

test/cases/31_command_coverage.sh

An object of kind test

What it is

URI
majordomus://test/test/cases/31_command_coverage.sh
Kind
test
Identity
test/cases/31_command_coverage.sh
Path
test/cases/31_command_coverage.sh
Provenance
declared
Media type
text/plain
As a capability
test.test/cases/31_command_coverage.sh

Front matter

{}

Content

# majordomus-covers: none
# claims: command-coverage
#
# Test coverage is computed, not remembered. This case joins the public command surface in
# share/commands.yaml to the coverage each test case declares about itself, and fails when a
# command is missing a layer it is owed. No command is named here: a command added to the
# registry is required to have coverage from that moment, and a maintainer who adds one
# without tests is told which layer is missing rather than discovering it later.
#
# Coverage is declared rather than inferred. It cannot be read reliably from the source: the
# negative assertions for handover and checkpoint are written as `bash -c "... | '$MJ' cmd"`
# because those commands read stdin, and the surface case asserts a failure for every command
# through a loop variable. Both are invisible to any scan. A filename tells you even less.
#
# Layers:
#   behaviour   the case exercises the command's normal path through the real binary
#   negative    the case asserts a named failure mode of that command, with its exit code
#   lifecycle   the case walks a multi-command workflow to an accepted or refused end
#
# Every case in this suite is end to end by construction: test/run.sh builds a fresh git
# repository in a temporary directory and invokes bin/majordomus in it, so there is no
# separate end-to-end layer to declare and no second runner to keep in step.
. "$ROOT/test/lib.sh"
MJ_BIN_DIR="$ROOT/bin"; MJ_LIB_DIR="$ROOT/lib"; export MJ_BIN_DIR MJ_LIB_DIR
# shellcheck source=../../lib/common.sh
. "$ROOT/lib/common.sh"
# shellcheck source=../../lib/commands.sh
. "$ROOT/lib/commands.sh"

REG="$ROOT/share/commands.yaml"
FLAT="$T/commands.flat"
mj_yaml_flatten "$REG" > "$FLAT" || { echo "    share/commands.yaml does not parse"; exit 1; }

# ---- the surface that is owed coverage, read from the registry
public=""
i=0
while [ -n "$(sed -n "s|^commands\.$i\.id=||p" "$FLAT")" ]; do
  if [ "$(sed -n "s|^commands\.$i\.visibility=||p" "$FLAT")" = public ]; then
    public="$public $(sed -n "s|^commands\.$i\.id=||p" "$FLAT")"
  fi
  i=$((i+1))
done
[ -n "$public" ] || { echo "    the registry declares no public command"; exit 1; }

# ---- the coverage the suite declares about itself
# One line per claim: "<command> <layer> <case>". Built by reading the headers, so a case
# that stops declaring a command stops covering it.
CLAIMS="$T/claims.txt"; : > "$CLAIMS"
declared_cases=0
for f in "$ROOT"/test/cases/*.sh; do
  case_name="$(basename "$f" .sh)"
  head_covers="$(sed -n 's/^# majordomus-covers: *//p' "$f" | head -1)"
  [ -n "$head_covers" ] || continue
  declared_cases=$((declared_cases + 1))
  [ "$head_covers" = none ] && continue
  for c in $head_covers; do printf '%s behaviour %s\n' "$c" "$case_name" >> "$CLAIMS"; done
  for c in $(sed -n 's/^# majordomus-negative: *//p' "$f" | head -1); do
    printf '%s negative %s\n' "$c" "$case_name" >> "$CLAIMS"
  done
  life="$(sed -n 's/^# majordomus-lifecycle: *//p' "$f" | head -1)"
  [ -n "$life" ] && printf -- '- lifecycle:%s %s\n' "$life" "$case_name" >> "$CLAIMS"
done
[ "$declared_cases" -ge 15 ] || { echo "    only $declared_cases cases declare coverage; the headers are not being read"; exit 1; }

covers() { grep -qE "^$1 $2 " "$CLAIMS"; }          # command, layer
cases_for() { grep -E "^$1 $2 " "$CLAIMS" | awk '{print $3}' | LC_ALL=C sort -u | tr '\n' ' '; }

# ---- a declared cover must name something that exists. A header pointing at a command that
#      was renamed or removed is a broken reference, not harmless documentation.
#
#      Two vocabularies, resolved by the one function the doctor check uses
#      (mj_covers_resolve in lib/commands.sh), so the two cannot disagree: a bare name is a
#      public command, and `gate:`, `script:`, `workflow:` and `capability:` name the other kinds of
#      thing a case can be about. Before those existed, a case covering a gate or a script had
#      no word for its own subject and `none` was its only legal value — 74 of 215 cases on
#      2026-09-15.
#
#      A prefixed name is checked for existence and is deliberately NOT counted as command
#      coverage: the obligations below still read bare names only, so widening the vocabulary
#      cannot discharge the narrower duty that every public command carries two cases.
for c in $(awk '$1 != "-" {print $1}' "$CLAIMS" | LC_ALL=C sort -u); do
  rc=0; why="$(mj_covers_resolve "$ROOT" "$c")" || rc=$?
  case "$rc" in
    0) continue ;;
    1) echo "    $why"; exit 1 ;;
    3) echo "    '$c' cannot be resolved: this tree carries no $why"; exit 1 ;;
  esac
  printf '%s\n' $public | grep -qx "$c" || {
    echo "    a test case declares coverage of '$c', which is not a public command"
    echo "    reproduce: grep -rn 'majordomus-covers\\|majordomus-negative' test/cases/ | grep $c"
    exit 1; }
done

# ---- the requirement: every public command is exercised and refuted
missing=0
for c in $public; do
  covers "$c" behaviour || { echo "    $c: no case declares behaviour coverage"; missing=1; }
  covers "$c" negative  || { echo "    $c: no case declares a negative case"; missing=1; }
done
[ "$missing" = 0 ] || {
  echo "    add the case, then declare it in that case's header:"
  echo "      # majordomus-covers: <command>      the normal path"
  echo "      # majordomus-negative: <command>    a named failure mode"
  exit 1; }

# ---- the workflow layers. One accepted path and one refusal path across command
#      boundaries, because a contract that holds for each command separately can still fail
#      to hold across a sequence of them.
grep -q '^- lifecycle:accepted ' "$CLAIMS" || {
  echo "    no case declares '# majordomus-lifecycle: accepted' — a full workflow to an accepted finish"; exit 1; }
grep -q '^- lifecycle:refused ' "$CLAIMS" || {
  echo "    no case declares '# majordomus-lifecycle: refused' — a workflow that violates the"
  echo "    contract, is refused, is repaired, and is then accepted"; exit 1; }

# ---- the matrix, computed. Printed rather than written down anywhere.
printf '\n    %-12s %-28s %s\n' command behaviour negative
for c in $public; do
  printf '    %-12s %-28s %s\n' "$c" "$(cases_for "$c" behaviour)" "$(cases_for "$c" negative)"
done
printf '    lifecycle accepted: %s\n' "$(grep '^- lifecycle:accepted ' "$CLAIMS" | awk '{print $3}' | tr '\n' ' ')"
printf '    lifecycle refused:  %s\n' "$(grep '^- lifecycle:refused ' "$CLAIMS" | awk '{print $3}' | tr '\n' ' ')"

# ---- the check is not vacuous: a command with no coverage must be detected. The probe adds
#      a command to the surface and asserts that the requirement above rejects it.
probe_public="$public nosuchcommand"
probe_missing=0
for c in $probe_public; do
  covers "$c" behaviour || probe_missing=1
done
[ "$probe_missing" = 1 ] || { echo "    the coverage check is vacuous: an uncovered command passed it"; exit 1; }

printf '    %s public commands, every one exercised and refuted\n' "$(printf '%s\n' $public | wc -w | tr -d ' ')"