From 6a1a8d639be616bcef75eff8c9618e53a3d4785f Mon Sep 17 00:00:00 2001 From: juvdiaz Date: Tue, 30 Jun 2026 08:48:30 -0600 Subject: [PATCH] Expand high value Jeannie unit tests --- tests/jeannie-unit | 146 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/tests/jeannie-unit b/tests/jeannie-unit index 116484e..6408129 100755 --- a/tests/jeannie-unit +++ b/tests/jeannie-unit @@ -6,11 +6,12 @@ LAB_DIR="${REPO_ROOT}/.lab" TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/jeannie-unit.XXXXXX")" BACKUP_DIR="${TMP_DIR}/backup" FAILURES=0 +LAB_STATE_FILES="pimox-workers.tsv cluster-workers.auto.tfvars.json manual-workers.tsv" mkdir -p "${BACKUP_DIR}" "${LAB_DIR}" "${TMP_DIR}/bin" cleanup() { - for file in pimox-workers.tsv cluster-workers.auto.tfvars.json; do + for file in ${LAB_STATE_FILES}; do rm -f "${LAB_DIR}/${file}" if [[ -f "${BACKUP_DIR}/${file}" ]]; then cp "${BACKUP_DIR}/${file}" "${LAB_DIR}/${file}" @@ -20,7 +21,7 @@ cleanup() { } trap cleanup EXIT -for file in pimox-workers.tsv cluster-workers.auto.tfvars.json; do +for file in ${LAB_STATE_FILES}; do if [[ -f "${LAB_DIR}/${file}" ]]; then cp "${LAB_DIR}/${file}" "${BACKUP_DIR}/${file}" fi @@ -39,6 +40,10 @@ pimox02 192.168.100.67 jv pimox-worker-02 /home/jv/.ssh/id_ed25519 pimox03 192.168.100.76 jv pimox-worker-03 /home/jv/.ssh/id_ed25519 EOF +cat >"${LAB_DIR}/manual-workers.tsv" <<'EOF' +manual01 192.168.100.99 jv manual-worker-01 /home/jv/.ssh/id_ed25519 +EOF + cat >"${LAB_DIR}/cluster-workers.auto.tfvars.json" <<'EOF' { "worker_nodes": { @@ -95,6 +100,47 @@ assert_eq() { fi } +assert_contains() { + local label="$1" + local needle="$2" + local haystack="$3" + + if grep -Fq "${needle}" <<<"${haystack}"; then + pass "${label}" + else + fail "${label}: expected output to contain '${needle}'" + fi +} + +assert_command_success() { + local label="$1" + shift + + if "$@" >/dev/null 2>&1; then + pass "${label}" + else + fail "${label}: command failed" + fi +} + +assert_command_failure_contains() { + local label="$1" + local needle="$2" + shift 2 + local output + local status + + set +e + output="$("$@" 2>&1)" + status=$? + set -e + if [[ "${status}" -eq 0 ]]; then + fail "${label}: command unexpectedly passed" + return 0 + fi + assert_contains "${label}" "${needle}" "${output}" +} + test_pimox_worker_count_detects_generated_topology() { unset LAB_PIMOX_WORKER_COUNT assert_eq "detects 3 generated Pimox workers" "3" "$(pimox_worker_count_default)" @@ -120,6 +166,54 @@ test_cluster_worker_targets_include_pimox_and_manual_workers() { assert_eq "cluster targets include generated Pimox workers" "jv@192.168.100.66,jv@192.168.100.67,jv@192.168.100.76,jv@192.168.100.99" "${targets}" } +test_prepare_cluster_worker_var_file_merges_manual_and_pimox_workers() { + unset LAB_INCLUDE_RASPBERRY_WORKER + LAB_PIMOX_WORKER_NODE_LABELS_JSON='{"homelab.dev/node-role":"app","homelab.dev/storage":"ssd"}' + LAB_MANUAL_WORKER_NODE_LABELS_JSON='{"homelab.dev/node-role":"manual","homelab.dev/storage":"custom"}' + prepare_cluster_worker_var_file false + python3 - "${LAB_DIR}/cluster-workers.auto.tfvars.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as handle: + document = json.load(handle) +nodes = document["worker_nodes"] +labels = document["worker_node_labels"] +expected = {"pimox01", "pimox02", "pimox03", "manual01"} +if set(nodes) != expected: + raise SystemExit(f"worker keys mismatch: {sorted(nodes)}") +if nodes["manual01"]["node_name"] != "manual-worker-01": + raise SystemExit("manual worker node name was not preserved") +if labels["pimox01"]["homelab.dev/storage"] != "ssd": + raise SystemExit("pimox labels were not applied") +if labels["manual01"]["homelab.dev/node-role"] != "manual": + raise SystemExit("manual labels were not applied") +PY + pass "worker var file merges manual and Pimox workers" + unset LAB_PIMOX_WORKER_NODE_LABELS_JSON LAB_MANUAL_WORKER_NODE_LABELS_JSON LAB_CLUSTER_VAR_FILE LAB_INCLUDE_RASPBERRY_WORKER +} + +test_prepare_cluster_worker_var_file_can_include_raspberry_worker() { + LAB_INCLUDE_RASPBERRY_WORKER=true + LAB_RASPBERRY_HOST=192.168.100.89 + LAB_RASPBERRY_NODE_NAME=raspberry + prepare_cluster_worker_var_file false + python3 - "${LAB_DIR}/cluster-workers.auto.tfvars.json" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as handle: + document = json.load(handle) +node = document["worker_nodes"].get("raspberrypi") +if not node: + raise SystemExit("raspberrypi worker was not included") +if node["host"] != "192.168.100.89" or node["node_name"] != "raspberry": + raise SystemExit(f"unexpected raspberry worker: {node}") +PY + pass "worker var file can include Raspberry Pi worker" + unset LAB_INCLUDE_RASPBERRY_WORKER LAB_RASPBERRY_HOST LAB_RASPBERRY_NODE_NAME LAB_CLUSTER_VAR_FILE +} + test_report_renderer_hides_ok_links() { local output output="$( @@ -133,11 +227,59 @@ test_report_renderer_hides_ok_links() { fi } +test_report_renderer_problems_only_hides_ok_rows() { + local output + output="$( + printf 'ok\tArea\tHealthy\tok\t\t\t\t\nwarn\tArea\tWarning\tneeds attention\tdetail\tfix\tgraph\tquery\n' | + "${REPO_ROOT}/scripts/report-render" --title "Unit Report" --only problems + )" + if grep -Fq 'Healthy' <<<"${output}"; then + fail "report renderer problems-only hides ok rows" + else + pass "report renderer problems-only hides ok rows" + fi + assert_contains "report renderer problems-only keeps warning" "Warning" "${output}" + assert_contains "report renderer problems-only prints next fix" "Next Fix" "${output}" +} + +test_report_renderer_empty_problems_message() { + local output + output="$( + printf 'ok\tArea\tHealthy\tok\t\t\t\t\n' | + "${REPO_ROOT}/scripts/report-render" --title "Unit Report" --only problems + )" + assert_contains "report renderer empty problems message" "No failing or warning items." "${output}" +} + +test_inventory_validator_accepts_current_inventory() { + assert_command_success "inventory validator accepts repo inventory" "${REPO_ROOT}/scripts/validate-homelab-inventory" "${REPO_ROOT}/homelab.yml" +} + +test_inventory_validator_rejects_ip_drift() { + local inventory="${TMP_DIR}/invalid-homelab.yml" + python3 - "${REPO_ROOT}/homelab.yml" "${inventory}" <<'PY' +import sys + +source, target = sys.argv[1:3] +text = open(source, encoding="utf-8").read() +text = text.replace("lan_ip: 192.168.100.73", "lan_ip: not-an-ip", 1) +with open(target, "w", encoding="utf-8") as handle: + handle.write(text) +PY + assert_command_failure_contains "inventory validator rejects invalid Debian LAN IP" "hosts.debian.lan_ip is not a valid IP" "${REPO_ROOT}/scripts/validate-homelab-inventory" "${inventory}" +} + test_pimox_worker_count_detects_generated_topology test_configured_worker_count_cannot_hide_existing_workers test_configured_worker_count_can_expand_target_set test_cluster_worker_targets_include_pimox_and_manual_workers +test_prepare_cluster_worker_var_file_merges_manual_and_pimox_workers +test_prepare_cluster_worker_var_file_can_include_raspberry_worker test_report_renderer_hides_ok_links +test_report_renderer_problems_only_hides_ok_rows +test_report_renderer_empty_problems_message +test_inventory_validator_accepts_current_inventory +test_inventory_validator_rejects_ip_drift if ((FAILURES > 0)); then printf '\n%s unit test(s) failed.\n' "${FAILURES}" >&2