Files
nisse/scripts/e2e.sh
T
dongarandClaude Opus 5.5 d7e4fd0938
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m10s
CI / e2e (pull_request) Successful in 2m54s
M1.5 accepted: the tests run over a published image
- The Dockerfile's test stage stacks on TEST_BASE: this build's agent by
  default, or a published image. It adds only the test tools (uv export
  of the dev group into the image's environment), never reinstalling the
  agent, so the suite tests the image as published.
- scripts/e2e.sh --image passes NISSE_E2E_MODEL into the container.
- The plan marks M1.5 done: v0.1.0's published image passed the whole e2e
  suite (9 of 9, 11m37s), with the release's own tests over it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 23:09:08 +00:00

65 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# The end-to-end test (Development Plan M1.6): the whole agent against real
# services, driven by a scripted operator. CI runs it on every PR (with
# --local); locally, run only the tests you're writing (-k) or diagnosing
# (--lf).
#
# scripts/e2e.sh on the bring-up build VM (configured below)
# scripts/e2e.sh --local on a throwaway local sshd instead
# scripts/e2e.sh --image REF inside an image's `test` stage (Dockerfile),
# on its own sshd: checks a release as built
#
# Uses tests/integration/nisse-test.yaml and its secrets (.env at the repo
# root), like the integration tests. Every role runs on a local model
# (tests/integration/e2e_support.py; NISSE_E2E_MODEL overrides it), so tell
# the operator before a full run: it keeps the RTX 4090 busy for a while.
set -euo pipefail
cd "$(dirname "$0")/.."
if [ "${1:-}" = "--image" ]; then
image=${2:?--image needs an image reference}
shift 2
runs=${NISSE_E2E_RUNS:-/root/nisse-e2e/test-runs}
mkdir -p "$runs"
secrets=()
[ -f .env ] && secrets=(--env-file .env)
# The model, if overridden here, reaches the tests inside too.
[ -n "${NISSE_E2E_MODEL:-}" ] && secrets+=(-e "NISSE_E2E_MODEL=$NISSE_E2E_MODEL")
# Host networking: the suite reaches Matrix, Gitea and LiteLLM as here.
exec docker run --rm --network host "${secrets[@]}" \
-e NISSE_E2E_RUNS=/runs -v "$runs:/runs" \
"$image" scripts/e2e.sh --local "$@"
fi
if [ "${1:-}" = "--local" ]; then
shift
else
export NISSE_E2E_BUILD_HOST=${NISSE_E2E_BUILD_HOST:-nisse-dev.garrett-hall.com}
export NISSE_E2E_BUILD_KEY=${NISSE_E2E_BUILD_KEY:-/root/nisse-e2e/ssh/id_ed25519}
fi
# Each run's state store is kept here, for nisse_dev.dump after a failure.
export NISSE_E2E_RUNS=${NISSE_E2E_RUNS:-/root/nisse-e2e/test-runs}
# What the run spent on the test key, read from LiteLLM (spend only). The
# end-to-end tests run on a local model, so anything above $0 means a call
# went to a paid one: an override, or a proxy fallback.
spend() {
(set -a; [ -f .env ] && . ./.env; set +a
uv run python -c '
import os, httpx
r = httpx.get("https://litellm.dongar.net/key/info", timeout=30,
headers={"Authorization": "Bearer " + os.environ["NISSE_LLM_TOKEN"]})
i = r.json()["info"]
print(i["spend"], i.get("max_budget"))' 2>/dev/null) || echo "? ?"
}
read -r before budget < <(spend)
status=0
uv run pytest -m e2e -rs tests/integration/test_end_to_end.py tests/integration/test_end_to_end_loop.py "$@" || status=$?
read -r after _ < <(spend)
python3 -c "
b, a, cap = '$before', '$after', '$budget'
try:
print(f'e2e spend: \${float(a) - float(b):.4f} this run; \${float(a):.2f} of \${float(cap):.2f} today')
except ValueError:
print('e2e spend: unknown (could not read the key)')"
exit "$status"