원클릭으로
add-bats-test
"
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
"
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
"
"
"
"
"
"
tests/unit/agent_run.bats — canonical example of agent function teststests/unit/config_budget.bats — example of pure function tests (no mock claude)tests/helpers/test_helper.bash — _common_setup, make_claude_envelope APItests/helpers/mock_claude.bash — setup_mock_claude, create_dispatch_response| Function under test | Test file location |
|---|---|
lib/*.sh function (no claude call) | tests/unit/<subsystem>.bats |
lib/*.sh function (calls run_agent) | tests/unit/<subsystem>.bats + mock claude |
| Phase entry function | tests/integration/<phase>.bats |
scripts/validate-*.sh | tests/scripts/validate_<name>.bats |
| Budget/state edge case | tests/edge/<domain>.bats |
Every unit test file must follow this exact setup order:
#!/usr/bin/env bats
# Tests for <function-name> in lib/<file>.sh
setup() {
load '../helpers/test_helper'
_common_setup # creates TEST_TMPDIR, exports defaults, sources all 9 libs
load '../helpers/mock_claude'
setup_mock_claude # installs mock binary on PATH (needed only if testing claude calls)
}
teardown() {
_common_teardown
}
If the functions under test never call run_agent or claude -p directly, omit the mock_claude lines.
Do not call setup_mock_claude before _common_setup — the mock binary writes to $TEST_TMPDIR/mock_bin/ which doesn't exist until _common_setup creates it.
@test "<function>: writes findings on success" {
# Arrange: create mock response
make_claude_envelope \
"$(cat "$BATS_TEST_DIRNAME/../fixtures/findings/<agent>.json")" \
"0.15" > "$MOCK_CLAUDE_RESPONSE"
# Act
run run_agent "<name>" "@${SCRIPT_DIR}/prompts/<name>.md" \
"${SCRIPT_DIR}/schemas/<name>.json" \
"Read,Bash(find:*)" "haiku" "gather"
# Assert
assert_success
assert_file_exists "$WORK_DIR/findings/<name>.json"
run jq -e '.key' "$WORK_DIR/findings/<name>.json"
assert_success
}
Every agent must be skippable when findings exist and FORCE=false:
@test "<function>: skips when findings exist and FORCE=false" {
# Pre-create findings
echo '{"findings":[]}' > "$WORK_DIR/findings/<name>.json"
FORCE=false run run_agent "<name>" "prompt" "schema" "tools" "haiku"
assert_success
# Verify mock was not called
run grep -c 'CALL:' "$MOCK_CLAUDE_LOG"
assert_output "0"
}
@test "<function>: rejects when budget exhausted" {
# Pre-populate costs to exhaust budget
echo "gather|prior-agent|$(echo "$TOTAL_BUDGET + 1" | bc)" \
> "$WORK_DIR/costs/prior-agent.cost"
run run_agent "<name>" "prompt" "schema" "tools" "haiku"
assert_failure
}
For agents that should record costs:
@test "<function>: records cost to cost file" {
make_claude_envelope '{}' '0.42' > "$MOCK_CLAUDE_RESPONSE"
run run_agent "<name>" "prompt" "${SCRIPT_DIR}/schemas/<name>.json" "tools"
assert_success
assert_file_exists "$WORK_DIR/costs/<name>.cost"
run grep '0.42' "$WORK_DIR/costs/<name>.cost"
assert_success
}
_common_setup: config.sh reads ${VAR:-default} at source time. Setting TOTAL_BUDGET after _common_setup has no effect..20 not 0.20 for values < 1. Assert both forms: [[ "$x" == ".20" || "$x" == "0.20" ]]_common_setup exports empty color codes (RED='') — ANSI codes are stripped for clean assert_output --partial matching.ULTRAINIT_MAX_RETRIES=1 is set in _common_setup — tests won't wait for 3 retries.make test-unit
Or for a single file:
docker run --rm -v "$(pwd)":/workspace -w /workspace \
ghcr.io/joelbarmettleruzh/ultrainit-test:latest \
bats tests/unit/my-new-test.bats
Passing raw fixture JSON without make_claude_envelope — run_agent extracts .structured_output from the response. Without the envelope wrapper, it writes "null" to findings silently.
Loading mock_claude before _common_setup — the mock binary is written to $TEST_TMPDIR/mock_bin/ which _common_setup creates. Wrong order causes setup_mock_claude to fail.
Not setting budget env vars before sourcing — _common_setup sources all libs. Variables read at source time can't be changed after sourcing. Set overrides before calling _common_setup.
Using assert_output without --partial for multi-line output — validator output includes dynamic metrics. Use assert_output --partial 'VERDICT: PASS' not exact match.