Skip to content

feat(assert): assert_command_available #1027

Description

@Chemaclass

Problem

assert_command_not_found exists (src/assert/core.sh) but has no positive counterpart. Asserting that a dependency is installed — the first thing an installer test, a Docker image test or a bootstrap test wants to check — has to go through the helper:

assert_true "bashunit::is_command_available jq"

which reports the failure as a boolean assertion over an opaque string rather than naming the missing command. The check itself already exists as bashunit::is_command_available (src/api/globals.sh); only the assertion wrapper is missing.

Every other assertion family in bashunit ships both directions.

Proposal

assert_command_available jq
  • Passes when the command resolves in PATH (builtins and functions included, consistent with bashunit::is_command_available).
  • Fails with a message naming the command, in the same voice as assert_command_not_found.
  • Implemented on top of bashunit::is_command_available so there is one definition of "available", not two.

Also consider aliasing to match whichever name reads better next to the existing assert_command_not_found — but ship exactly one name, not two.

Acceptance criteria

  • assert_command_available bash passes
  • assert_command_available definitely_not_a_command fails with the command name in the message
  • Behaviour for shell builtins and shell functions matches bashunit::is_command_available and is documented
  • A missing argument is a usage error (feat(assert): a missing argument reports a failed assertion, not a usage error #983 precedent)
  • Symmetric with assert_command_not_found: the two never both pass or both fail for the same input
  • Implemented via bashunit::is_command_available, no second definition of availability

Repo checklist (agent)

  • TDD: RED → GREEN → REFACTOR. Use /add-assertion if helpful.
  • Bash 3.0+ only: no printf -v, no +=, no declare -A, no [[ ]], no ${var,,}, no &>>.
  • Assertions are bare-named (assert_x), helpers are bashunit::-namespaced. Start every assertion with bashunit::assert::should_skip && return 0 and report through bashunit::assertion_failed / bashunit::assertion_passed, matching its neighbours in the same file.
  • A missing required argument must go through bashunit::assert::usage_error, not compare against an empty string (feat(assert): a missing argument reports a failed assertion, not a usage error #983).
  • Tests: mirror the src layout — tests/unit/assert/<file>_test.sh. There is no assert_fails; test failure output by comparing against print_failed_test, as the existing tests do.
  • Docs: add the entry to docs/assertions.md, then regenerate the bashunit doc acceptance snapshot (editing that file breaks it).
  • Update the assertion count in README.md ("It ships 73 assertions…" — currently already stale at 74) and add a CHANGELOG.md line under ## Unreleased.
  • Gates: make sa, make lint, ./bashunit tests/, ./bashunit --parallel tests/. Never run shfmt -w.
  • One issue = one PR.

Metadata

Metadata

Assignees

Labels

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions