refactor: reduce complexity in test/mock.sh and test/record.sh (#1096)

Extract assertion tracking and fixture detection logic in mock.sh:
- New _run_assertions_and_track() helper consolidates 20 lines of repeated assertions
- New _has_missing_fixture() helper checks mock log for fixture errors
- run_test() now 30 lines shorter, focusing on orchestration rather than details

Extract cloud endpoints data in record.sh:
- Replace 132-line case statement with data-driven approach
- Each cloud's endpoints now live in _ENDPOINTS_{cloud} variable
- get_endpoints() function reduced to 3 lines, delegates to variable lookup

Benefits:
- Reduced cognitive load: test logic separated from data
- Easier to add new clouds: just add _ENDPOINTS_* variable
- Better maintainability: centralized endpoint definitions

Tests: All 80 tests pass with fixtures enabled.

Co-authored-by: Spawn Refactor Service <refactor@spawn.service>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-02-14 04:12:54 -08:00 committed by GitHub
parent 8981376274
commit 6647f7ca05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 167 additions and 157 deletions

View file

@ -696,6 +696,37 @@ _categorize_failure() {
fi
}
# Run assertions for a script and track which categories failed.
# Outputs: _exit_failed, _api_failed, _ssh_failed, _env_failed (as 0/1)
_run_assertions_and_track() {
local exit_code="$1" cloud="$2"
local _ASSERT_DELTA=0
_tracked_assert assert_exit_code "${exit_code}" 0 "exits successfully"
_exit_failed=$_ASSERT_DELTA
_tracked_assert assert_cloud_api_calls "$cloud"
_api_failed=$_ASSERT_DELTA
_tracked_assert assert_log_contains "ssh " "uses SSH"
_ssh_failed=$_ASSERT_DELTA
_tracked_assert assert_env_injected "OPENROUTER_API_KEY"
_env_failed=$_ASSERT_DELTA
if [[ "${MOCK_VALIDATE_BODY:-}" == "1" ]]; then
assert_no_body_errors
fi
if [[ "${MOCK_TRACK_STATE:-}" == "1" ]]; then
assert_server_cleaned_up "$3"
fi
}
# Check for missing fixtures in the mock log.
_has_missing_fixture() {
grep -q "NO_FIXTURE:" "${MOCK_LOG}" 2>/dev/null && echo 1 || echo 0
}
run_test() {
local cloud="$1"
local agent="$2"
@ -731,37 +762,13 @@ run_test() {
fi
# Normal mode: run standard assertions and track failures per category
local _ASSERT_DELTA=0
local _exit_failed _api_failed _ssh_failed _env_failed
_tracked_assert assert_exit_code "${exit_code}" 0 "exits successfully"
_exit_failed=$_ASSERT_DELTA
_tracked_assert assert_cloud_api_calls "$cloud"
_api_failed=$_ASSERT_DELTA
_tracked_assert assert_log_contains "ssh " "uses SSH"
_ssh_failed=$_ASSERT_DELTA
_tracked_assert assert_env_injected "OPENROUTER_API_KEY"
_env_failed=$_ASSERT_DELTA
if [[ "${MOCK_VALIDATE_BODY:-}" == "1" ]]; then
assert_no_body_errors
fi
if [[ "${MOCK_TRACK_STATE:-}" == "1" ]]; then
assert_server_cleaned_up "${state_file}"
fi
# Check for missing fixtures
local _has_no_fixture=0
if grep -q "NO_FIXTURE:" "${MOCK_LOG}" 2>/dev/null; then
_has_no_fixture=1
fi
_run_assertions_and_track "${exit_code}" "${cloud}" "${state_file}"
# Record result with failure category
local pre_fail=$((FAILED - _pre_failed))
if [[ "$pre_fail" -gt 0 ]]; then
local _has_no_fixture
_has_no_fixture=$(_has_missing_fixture)
local _reason
_reason=$(_categorize_failure "$_has_no_fixture" "$_exit_failed" "$_api_failed" "$_ssh_failed" "$_env_failed")
record_test_result "${cloud}" "${agent}" "fail" "${_reason}"

View file

@ -34,138 +34,141 @@ PROMPT_FOR_CREDS=true
ALL_RECORDABLE_CLOUDS="hetzner digitalocean vultr linode lambda civo upcloud binarylane ovh scaleway genesiscloud kamatera latitude hyperstack atlanticnet hostkey cloudsigma webdock serverspace gcore"
# --- Endpoint registry ---
# Format: "fixture_name:endpoint"
# Declare endpoints as string literal for each cloud
# Format: "fixture_name:endpoint" (one per line, indented)
_ENDPOINTS_hetzner="
server_types:/server_types?per_page=50
locations:/locations
ssh_keys:/ssh_keys
servers:/servers
"
_ENDPOINTS_digitalocean="
account:/account
ssh_keys:/account/keys
droplets:/droplets
sizes:/sizes
regions:/regions
"
_ENDPOINTS_vultr="
account:/account
ssh_keys:/ssh-keys
instances:/instances
plans:/plans
regions:/regions
"
_ENDPOINTS_linode="
profile:/profile
ssh_keys:/profile/sshkeys
instances:/linode/instances
types:/linode/types
regions:/regions
"
_ENDPOINTS_lambda="
instances:/instances
ssh_keys:/ssh-keys
instance_types:/instance-types
"
_ENDPOINTS_civo="
regions:/regions
instances:/instances
sshkeys:/sshkeys
networks:/networks
disk_images:/disk_images
"
_ENDPOINTS_upcloud="
servers:/server
server_sizes:/server_size
"
_ENDPOINTS_binarylane="
sizes:/sizes
regions:/regions
servers:/servers
"
_ENDPOINTS_ovh="
flavors:/cloud/project/\${OVH_PROJECT_ID:-MISSING}/flavor
images:/cloud/project/\${OVH_PROJECT_ID:-MISSING}/image
ssh_keys:/cloud/project/\${OVH_PROJECT_ID:-MISSING}/sshkey
"
_ENDPOINTS_scaleway="
servers:/servers
images:/images?per_page=10
"
_ENDPOINTS_genesiscloud="
ssh_keys:/ssh-keys
instances:/instances
"
_ENDPOINTS_kamatera="
server_options:/service/server
"
_ENDPOINTS_latitude="
ssh_keys:/ssh_keys
plans:/plans
regions:/regions
"
_ENDPOINTS_hyperstack="
flavors:/core/flavors
ssh_keys:/core/keypairs
"
_ENDPOINTS_atlanticnet="
ssh_keys:list-sshkeys
plans:describe-plan
"
_ENDPOINTS_hostkey="
services:/v1/services
ssh_keys:/ssh_keys
"
_ENDPOINTS_cloudsigma="
balance:/balance/
keypairs:/keypairs/
servers:/servers/
"
_ENDPOINTS_webdock="
account:/account
publicKeys:/account/publicKeys
servers:/servers
profiles:/profiles
locations:/locations
"
_ENDPOINTS_serverspace="
project:/project
servers:/servers
ssh-keys:/ssh-keys
locations:/locations
images:/images
"
_ENDPOINTS_gcore="
projects:/cloud/v1/projects
ssh_keys:/cloud/v1/ssh_keys/\${GCORE_PROJECT_ID:-MISSING}
instances:/cloud/v1/instances/\${GCORE_PROJECT_ID:-MISSING}/\${GCORE_REGION:-ed-1}
images:/cloud/v1/images/\${GCORE_PROJECT_ID:-MISSING}/\${GCORE_REGION:-ed-1}
flavors:/cloud/v1/flavors/\${GCORE_PROJECT_ID:-MISSING}/\${GCORE_REGION:-ed-1}
"
get_endpoints() {
local cloud="$1"
case "$cloud" in
hetzner)
printf '%s\n' \
"server_types:/server_types?per_page=50" \
"locations:/locations" \
"ssh_keys:/ssh_keys" \
"servers:/servers"
;;
digitalocean)
printf '%s\n' \
"account:/account" \
"ssh_keys:/account/keys" \
"droplets:/droplets" \
"sizes:/sizes" \
"regions:/regions"
;;
vultr)
printf '%s\n' \
"account:/account" \
"ssh_keys:/ssh-keys" \
"instances:/instances" \
"plans:/plans" \
"regions:/regions"
;;
linode)
printf '%s\n' \
"profile:/profile" \
"ssh_keys:/profile/sshkeys" \
"instances:/linode/instances" \
"types:/linode/types" \
"regions:/regions"
;;
lambda)
printf '%s\n' \
"instances:/instances" \
"ssh_keys:/ssh-keys" \
"instance_types:/instance-types"
;;
civo)
printf '%s\n' \
"regions:/regions" \
"instances:/instances" \
"sshkeys:/sshkeys" \
"networks:/networks" \
"disk_images:/disk_images"
;;
upcloud)
printf '%s\n' \
"servers:/server" \
"server_sizes:/server_size"
;;
binarylane)
printf '%s\n' \
"sizes:/sizes" \
"regions:/regions" \
"servers:/servers"
;;
ovh)
printf '%s\n' \
"flavors:/cloud/project/${OVH_PROJECT_ID:-MISSING}/flavor" \
"images:/cloud/project/${OVH_PROJECT_ID:-MISSING}/image" \
"ssh_keys:/cloud/project/${OVH_PROJECT_ID:-MISSING}/sshkey"
;;
scaleway)
printf '%s\n' \
"servers:/servers" \
"images:/images?per_page=10"
;;
genesiscloud)
printf '%s\n' \
"ssh_keys:/ssh-keys" \
"instances:/instances"
;;
kamatera)
printf '%s\n' \
"server_options:/service/server"
;;
latitude)
printf '%s\n' \
"ssh_keys:/ssh_keys" \
"plans:/plans" \
"regions:/regions"
;;
hyperstack)
printf '%s\n' \
"flavors:/core/flavors" \
"ssh_keys:/core/keypairs"
;;
atlanticnet)
printf '%s\n' \
"ssh_keys:list-sshkeys" \
"plans:describe-plan"
;;
hostkey)
printf '%s\n' \
"services:/v1/services" \
"ssh_keys:/ssh_keys"
;;
cloudsigma)
printf '%s\n' \
"balance:/balance/" \
"keypairs:/keypairs/" \
"servers:/servers/"
;;
webdock)
printf '%s\n' \
"account:/account" \
"publicKeys:/account/publicKeys" \
"servers:/servers" \
"profiles:/profiles" \
"locations:/locations"
;;
serverspace)
printf '%s\n' \
"project:/project" \
"servers:/servers" \
"ssh-keys:/ssh-keys" \
"locations:/locations" \
"images:/images"
;;
gcore)
printf '%s\n' \
"projects:/cloud/v1/projects" \
"ssh_keys:/cloud/v1/ssh_keys/${GCORE_PROJECT_ID:-MISSING}" \
"instances:/cloud/v1/instances/${GCORE_PROJECT_ID:-MISSING}/${GCORE_REGION:-ed-1}" \
"images:/cloud/v1/images/${GCORE_PROJECT_ID:-MISSING}/${GCORE_REGION:-ed-1}" \
"flavors:/cloud/v1/flavors/${GCORE_PROJECT_ID:-MISSING}/${GCORE_REGION:-ed-1}"
;;
esac
local var_name="_ENDPOINTS_${cloud}"
if [[ -n "${!var_name:-}" ]]; then
printf '%s\n' "${!var_name}" | grep -v '^$'
fi
}
# --- Multi-credential cloud specs ---