#!/usr/bin/env bats # ============================================================================= # tests/lib-generators.bats — Regression guard for the #849 fix. # # Before #849, `_generate_local_model_services` emitted the forge-user env # variable keyed by service name (`FORGE_BOT_USER_${service_name^^}`), so for # an `[agents.llama]` block with `forge_user = "dev-qwen"` the compose file # contained `FORGE_BOT_USER_LLAMA: "dev-qwen"`. That suffix diverges from the # `FORGE_TOKEN_` / `FORGE_PASS_` convention that the # same block uses two lines above, and it doesn't even round-trip through a # dash-containing service name (`dev-qwen` → `DEV-QWEN`, which is not a valid # shell identifier — see #852). # # The fix keys on `$user_upper` (already computed from `forge_user` via # `tr 'a-z-' 'A-Z_'`), yielding `FORGE_BOT_USER_DEV_QWEN: "dev-qwen"`. # ============================================================================= setup() { ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)" export FACTORY_ROOT="${BATS_TEST_TMPDIR}/factory" mkdir -p "${FACTORY_ROOT}/projects" # Minimal compose skeleton that `_generate_local_model_services` can splice into. # It only needs a `volumes:` marker line and nothing below it that would be # re-read after the splice. cat > "${FACTORY_ROOT}/docker-compose.yml" <<'EOF' services: agents: image: placeholder volumes: agent-data: EOF } @test "local-model agent service emits FORGE_BOT_USER keyed by forge_user (#849)" { cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF' name = "test" repo = "test-owner/test-repo" forge_url = "http://localhost:3000" [agents.llama] base_url = "http://10.10.10.1:8081" model = "qwen" api_key = "sk-no-key-required" roles = ["dev"] forge_user = "dev-qwen" compact_pct = 60 EOF run bash -c " set -euo pipefail source '${ROOT}/lib/generators.sh' _generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml' cat '${FACTORY_ROOT}/docker-compose.yml' " [ "$status" -eq 0 ] # New, forge_user-keyed suffix is present with the right value. [[ "$output" == *'FORGE_BOT_USER_DEV_QWEN: "dev-qwen"'* ]] # Legacy service-name-keyed suffix must not be emitted. [[ "$output" != *'FORGE_BOT_USER_LLAMA'* ]] } @test "local-model agent service emits local image ref + build: fallback (#853)" { # Before #853 the generator emitted `image: ghcr.io/disinto/agents:` for # every hired agent. The ghcr image isn't publicly pullable and the running # deployment has no credentials, so `docker compose up` failed with `denied`. # The fix: emit the registry-less local name (matches `disinto init --build` # and the legacy agents-llama stanza) plus a build: directive so hosts # without a pre-built image can rebuild locally. cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF' name = "test" repo = "test-owner/test-repo" forge_url = "http://localhost:3000" [agents.dev-qwen2] base_url = "http://10.10.10.1:8081" model = "qwen" api_key = "sk-no-key-required" roles = ["dev"] forge_user = "dev-qwen2" EOF run bash -c " set -euo pipefail source '${ROOT}/lib/generators.sh' _generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml' cat '${FACTORY_ROOT}/docker-compose.yml' " [ "$status" -eq 0 ] # Local image ref — no ghcr prefix. [[ "$output" == *'image: disinto/agents:${DISINTO_IMAGE_TAG:-latest}'* ]] [[ "$output" != *'image: ghcr.io/disinto/agents'* ]] # build: fallback so hosts without a pre-built image can rebuild. [[ "$output" == *'dockerfile: docker/agents/Dockerfile'* ]] } @test "local-model agent service emits pull_policy: build so docker compose up rebuilds on source change (#887)" { # Without pull_policy: build, `docker compose up -d --force-recreate` reuses # the cached `disinto/agents:latest` image and silently runs stale # docker/agents/entrypoint.sh even after the repo is updated. `pull_policy: # build` forces a rebuild on every up; BuildKit layer cache makes unchanged # rebuilds near-instant. The alternative was requiring every operator to # remember `--build` on every invocation, which was the bug that prompted # #887 (2h of debugging a fix that was merged but never reached the container). cat > "${FACTORY_ROOT}/projects/test.toml" <<'EOF' name = "test" repo = "test-owner/test-repo" forge_url = "http://localhost:3000" [agents.dev-qwen2] base_url = "http://10.10.10.1:8081" model = "qwen" api_key = "sk-no-key-required" roles = ["dev"] forge_user = "dev-qwen2" EOF run bash -c " set -euo pipefail source '${ROOT}/lib/generators.sh' _generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml' cat '${FACTORY_ROOT}/docker-compose.yml' " [ "$status" -eq 0 ] [[ "$output" == *'pull_policy: build'* ]] } @test "local-model agent service keys FORGE_BOT_USER to forge_user even when it differs from service name (#849)" { # Exercise the case the issue calls out: two agents in the same factory # whose service names are identical (`[agents.llama]`) but whose # forge_users diverge would previously both have emitted # `FORGE_BOT_USER_LLAMA`. With the fix each emission carries its own # forge_user-derived suffix. cat > "${FACTORY_ROOT}/projects/a.toml" <<'EOF' name = "a" repo = "a/a" forge_url = "http://localhost:3000" [agents.dev] base_url = "http://10.10.10.1:8081" model = "qwen" api_key = "sk-no-key-required" roles = ["dev"] forge_user = "review-qwen" EOF run bash -c " set -euo pipefail source '${ROOT}/lib/generators.sh' _generate_local_model_services '${FACTORY_ROOT}/docker-compose.yml' cat '${FACTORY_ROOT}/docker-compose.yml' " [ "$status" -eq 0 ] [[ "$output" == *'FORGE_BOT_USER_REVIEW_QWEN: "review-qwen"'* ]] [[ "$output" != *'FORGE_BOT_USER_DEV:'* ]] }