disinto/tests/lib-generators.bats
Claude 9ee704ea9c
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful
fix: bug: code fixes to docker/agents/ don't take effect — agent image is never rebuilt (#887)
Add `pull_policy: build` to every agent service emitted by the generator
that shares `docker/agents/Dockerfile` as its build context. Without it,
`docker compose up -d --force-recreate agents-<name>` reuses the cached
`disinto/agents:latest` image and silently keeps running stale
`docker/agents/entrypoint.sh` code even after the repo is updated. This
masked PR #864 (and likely earlier merges) — the fix landed on disk but
never reached the container.

#853 already paired `build:` with `image:` on hired-agent stanzas, which
was enough for first-time ups but not for re-ups. `pull_policy: build`
tells Compose to rebuild the image on every up; BuildKit's layer cache
makes the no-change case near-instant, and the change case picks up the
new source automatically. This covers:

- TOML-driven `agents-<name>` hired via `disinto hire-an-agent` — primary
  target of the issue.
- Legacy `agents-llama` and `agents-llama-all` stanzas — same Dockerfile,
  same staleness problem.

`bin/disinto up` already passed `--build`, so operators on the supported
UX path were already covered; this closes the gap for the direct
`docker compose` path the issue explicitly names in its acceptance.

Regression test added to `tests/lib-generators.bats` to pin the directive
alongside the existing #853 build/image invariants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:08:48 +00:00

161 lines
5.8 KiB
Bash

#!/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_USER>` / `FORGE_PASS_<FORGE_USER>` 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:<tag>` 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:'* ]]
}