#!/usr/bin/env bats # Seam: target reachability and deployment flow integrity. # These tests assert the deployment contract of the unified repo: # - a deploy.sh exists and accepts and --dry-run # - deploy.sh covers both the podman stack and the LLM fleet services # - every host referenced in the repo is reachable over SSH (dank@host) # - the local git remote is configured before deploy is attempted # Reachability tests are SKIPPED when the target is unreachable so the suite # still runs during offline development. load helpers @test "deploy.sh exists and is executable" { [ -x "$REPO_ROOT/scripts/deploy.sh" ] } @test "deploy.sh accepts the two known targets" { run bash "$REPO_ROOT/scripts/deploy.sh" --help 2>&1 [ "$status" -eq 0 ] || true grep -qE 'dankclaw|dankserver' "$REPO_ROOT/scripts/deploy.sh" } @test "git remote origin is configured" { git -C "$REPO_ROOT" remote get-url origin >/dev/null 2>&1 \ || skip "no origin remote configured yet" } @test "dankclaw is reachable over ssh" { timeout 8 ssh -o BatchMode=yes -o ConnectTimeout=5 dank@dankclaw 'true' 2>/dev/null \ || skip "dankclaw not reachable from this host" } @test "dankserver is reachable over ssh" { timeout 8 ssh -o BatchMode=yes -o ConnectTimeout=5 dank@dankserver 'true' 2>/dev/null \ || skip "dankserver not reachable from this host" } # Regression: the Caddyfile render step in deploy.sh must survive a bcrypt # hash (which contains '/' characters). Reproduce the sed expression with a # slash-bearing hash and confirm the placeholder is replaced. @test "deploy.sh reloads Caddy after rendering (hot reload, not just container start)" { # The caddy container often survives 'up -d' (a Caddyfile change is a # bind-mount content change, not a container config change). Without an # explicit reload, a rendered fix can sit on disk while Caddy serves the # stale in-memory config. grep -q 'caddy reload' "$REPO_ROOT/scripts/deploy.sh" } @test "deploy.sh restarts caddy when the reload cannot be applied" { # If the running config has the admin API disabled (or the API is down), # 'caddy reload' can never apply the new file -- the fix cannot be # delivered over the API it is meant to enable. The fallback is a # container restart, which always reads the file. grep -q 'podman restart caddy' "$REPO_ROOT/scripts/deploy.sh" } # The unified deploy.sh must manage the LLM fleet services on both hosts: # frank-default-small (dankserver) and frank-default-large (dankclaw). A # deploy that only restarts the podman stack leaves the LLM units pointing # at the retired vllmScripts checkout. @test "deploy.sh restarts the LLM services on both targets" { grep -q 'frank-default-small' "$REPO_ROOT/scripts/deploy.sh" grep -q 'frank-default-large' "$REPO_ROOT/scripts/deploy.sh" } @test "deploy.sh health-checks both LLM endpoints (both on :7770, by host)" { # 2026-08-22: frank-default-large moved from :52625 to :7770 on # dankclaw. Both LLM services now listen on :7770, distinguished by # host (dankserver vs dankclaw). grep -qE '^LLM_HEALTH\[dankserver\]="http://127\.0\.0\.1:7770/health"' "$REPO_ROOT/scripts/deploy.sh" grep -qE '^LLM_HEALTH\[dankclaw\]="http://127\.0\.0\.1:7770/health"' "$REPO_ROOT/scripts/deploy.sh" } @test "deploy.sh pulls on the merged checkout on both targets" { # Both targets run the whole stack (podman + LLM) from one checkout. local count count=$(grep -c '/home/dank/serverAppConfigs' "$REPO_ROOT/scripts/deploy.sh") [ "$count" -ge 2 ] } @test "deploy.sh Caddyfile render survives a slash-bearing bcrypt hash" { local hash='$2a$14$dndiQ9EaycJv0w3Gp2oAYO/xH6O9x2ZRhMLJdCxrgi9rMn1te9D/spy' local rendered rendered=$(sed "s|REPLACE_WITH_HASH|${hash}|g" "$REPO_ROOT/dankclaw/Caddyfile") [ -n "$rendered" ] [[ "$rendered" == *"${hash}"* ]] ! grep -q 'REPLACE_WITH_HASH' <<< "$rendered" # The rendered line must be exactly "dankuser ". A template that # keeps its own $2a$14$ prefix renders an invalid double-prefixed hash # and every basicauth vhost returns 401 (2026-08-22 incident). grep -qF "dankuser ${hash}" <<< "$rendered" ! grep -qF '$2a$14$$2a$14$' <<< "$rendered" }