#!/usr/bin/env bats # Seam: the shared artifact gates every LLM endpoint runs before it starts. # # The rule these encode: a serve script and the deployer must refuse the # SAME missing/truncated artifact, and they must refuse it the same way. # Two hand-written copies of the same gates drift apart until a deploy # passes what the unit would refuse; the library is the one place the # refusal lives, and both callers source it. # # Everything here is observable: arguments in, exit status and stderr out. # Nothing inspects the library's internals. load helpers LIB="$REPO_ROOT/scripts/lib/llm-gates.sh" setup() { TMP="$(mktemp -d)" # A fixture with a known, independent size: 12 bytes. printf '0123456789ab' > "$TMP/artifact.bin" mkdir -p "$TMP/flat" touch "$TMP/flat/tokenizer.json" "$TMP/flat/config.json" } teardown() { rm -rf "$TMP" } # call — run one gate in a fresh shell under `set -e`, exactly # as a serve script sources the library, and capture status + both streams. call() { run env GATE_PREFIX="${GATE_PREFIX:-testgate}" PATH="$PATH" \ bash -c "set -euo pipefail; source '$LIB'; $1" } @test "a present artifact passes the file gate with no output" { GATE_PREFIX=testgate call "require_file '$TMP/artifact.bin' checkpoint 'fetch it'" [ "$status" -eq 0 ] [ -z "$output" ] } @test "an absent artifact fails the file gate naming the path and the remedy" { call "require_file '$TMP/absent.bin' checkpoint 'run scripts/download-frank-archon-swift15.sh'" [ "$status" -ne 0 ] [[ "$output" == *"checkpoint: missing"* ]] [[ "$output" == *"$TMP/absent.bin"* ]] [[ "$output" == *"run scripts/download-frank-archon-swift15.sh"* ]] } @test "the file gate refuses a directory where an artifact is required" { # The serve gate is `-f`, not `-e`: a directory named like the payload # (an unpacked staging dir) must not boot the engine. call "require_file '$TMP/flat' checkpoint 'fetch it'" [ "$status" -ne 0 ] [[ "$output" == *"checkpoint"* ]] } @test "a symlinked artifact is refused: a HF snapshot link dangles in the container mount" { ln -s "$TMP/artifact.bin" "$TMP/linked.bin" call "require_file '$TMP/linked.bin' checkpoint 'fetch it'" [ "$status" -ne 0 ] [[ "$output" == *"checkpoint"*"missing"* ]] } @test "the byte gate accepts the exact contracted size" { call "require_bytes '$TMP/artifact.bin' checkpoint 12 'fetch it'" [ "$status" -eq 0 ] [ -z "$output" ] } @test "the byte gate refuses a size mismatch quoting both the real and the expected size" { # The Hub's declared byte count is the contract; curl writes the error # body and exits 0, so a truncation is invisible without this gate. call "require_bytes '$TMP/artifact.bin' checkpoint 42 'run the fetch script'" [ "$status" -ne 0 ] [[ "$output" == *"12 bytes"* ]] [[ "$output" == *"expected 42"* ]] [[ "$output" == *"run the fetch script"* ]] } @test "the byte gate is an equality contract, not a floor" { # A LARGER file is not a better artifact: it is a different artifact # (the compacted r2 of a checkpoint is byte-exact, not "at least"). call "require_bytes '$TMP/artifact.bin' checkpoint 6 'fetch it'" [ "$status" -ne 0 ] [[ "$output" == *"expected 6"* ]] } @test "the minimum-size gate accepts the threshold itself" { dd if=/dev/zero of="$TMP/two-mib.rad" bs=1048576 count=2 status=none call "require_min_mib '$TMP/two-mib.rad' container 2 'rebuild it'" [ "$status" -eq 0 ] [ -z "$output" ] } @test "the minimum-size gate refuses below the threshold naming both sizes" { dd if=/dev/zero of="$TMP/one-mib.rad" bs=1048576 count=1 status=none call "require_min_mib '$TMP/one-mib.rad' container 2 'rebuild it'" [ "$status" -ne 0 ] [[ "$output" == *"1 MiB"* ]] [[ "$output" == *"2 MiB"* ]] [[ "$output" == *"rebuild it"* ]] } @test "the flat-directory gate accepts a directory of regular files" { call "require_dir_files '$TMP/flat' tokenizer 'fetch the tokenizer' tokenizer.json config.json" [ "$status" -eq 0 ] [ -z "$output" ] } @test "the flat-directory gate names the member that is absent" { call "require_dir_files '$TMP/flat' tokenizer 'fetch the tokenizer' tokenizer.json vocab.json" [ "$status" -ne 0 ] [[ "$output" == *"tokenizer"*"missing"* ]] [[ "$output" == *"vocab.json"* ]] } @test "the flat-directory gate refuses a symlinked member" { # A HuggingFace cache snapshot's entries are symlinks into blobs/ and # dangle inside the container mount even when the directory is real. ln -s /nowhere/x "$TMP/flat/vocab.json" call "require_dir_files '$TMP/flat' tokenizer 'fetch the tokenizer' tokenizer.json vocab.json" [ "$status" -ne 0 ] [[ "$output" == *"vocab.json"*"missing or a symlink"* ]] } @test "the flat-directory gate refuses a missing directory" { call "require_dir_files '$TMP/nope' tokenizer 'fetch the tokenizer' tokenizer.json" [ "$status" -ne 0 ] [[ "$output" == *"tokenizer: missing"* ]] } @test "the flat-directory gate refuses a symlinked directory" { # The snapshot directory itself can be the link (a HF cache blobs/ # sibling), and a link into the container mount dangles there too. ln -s "$TMP/flat" "$TMP/linked-dir" call "require_dir_files '$TMP/linked-dir' tokenizer 'fetch the tokenizer' tokenizer.json" [ "$status" -ne 0 ] [[ "$output" == *"tokenizer: missing"* ]] } @test "the image gate refuses a ref the storage does not hold" { # A name@sha256 ref exists only if THAT manifest was pulled, so the # gate is the ref itself — one command, no grep race. skip_if_no_podman call "require_image 'example.invalid/nothing@sha256:0000000000000000000000000000000000000000000000000000000000000000' 'podman pull it'" [ "$status" -ne 0 ] [[ "$output" == *"not in podman storage"* ]] [[ "$output" == *"podman pull it"* ]] } @test "every failure names the endpoint the operator is standing in front of" { GATE_PREFIX=frank-dragoon call "require_file '$TMP/absent.bin' checkpoint 'fetch it'" [ "$status" -ne 0 ] [[ "$output" == frank-dragoon:* ]] } @test "a failing gate stops the caller from reaching the runtime" { # The composition property that matters: the gate is not advisory. A # serve script that calls it and then execs the engine must die first. cat > "$TMP/serve-like.sh" <