From deb9f118970ed2f44941dc025f14f88691e3752e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 4 Jun 2026 14:47:26 -0700 Subject: [PATCH] test(docker): harden live acp bind probes --- scripts/test-live-acp-bind-docker.sh | 23 ++++++++++++++++++ src/gateway/gateway-acp-bind.live.test.ts | 24 +++++++++++++++++-- .../package-acceptance-workflow.test.ts | 5 ++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/scripts/test-live-acp-bind-docker.sh b/scripts/test-live-acp-bind-docker.sh index 9130593febc6..dcbe9c3a9672 100644 --- a/scripts/test-live-acp-bind-docker.sh +++ b/scripts/test-live-acp-bind-docker.sh @@ -98,6 +98,26 @@ if [[ -f "$PROFILE_FILE" && -r "$PROFILE_FILE" ]]; then PROFILE_STATUS="$PROFILE_FILE" fi +openclaw_live_acp_bind_load_factory_api_key_from_profile() { + [[ -z "${FACTORY_API_KEY:-}" ]] || return 0 + [[ -f "$PROFILE_FILE" && -r "$PROFILE_FILE" ]] || return 0 + [[ "$PROFILE_FILE" != "$HOME/.profile" ]] || return 0 + + local line value + line="$(sed -nE 's/^(export[[:space:]]+)?FACTORY_API_KEY=//p' "$PROFILE_FILE" | tail -n 1 || true)" + [[ -n "$line" ]] || return 0 + value="$line" + if [[ "$value" == \"*\" && "$value" == *\" ]]; then + value="${value#\"}" + value="${value%\"}" + elif [[ "$value" == \'*\' && "$value" == *\' ]]; then + value="${value#\'}" + value="${value%\'}" + fi + [[ -n "$value" ]] || return 0 + export FACTORY_API_KEY="$value" +} + read -r -d '' LIVE_TEST_CMD <<'EOF' || true set -euo pipefail [ -f "$HOME/.profile" ] && [ -r "$HOME/.profile" ] && source "$HOME/.profile" || true @@ -317,6 +337,9 @@ for ACP_AGENT in "${ACP_AGENTS[@]}"; do DOCKER_AUTH_PRESTAGED=1 fi + if [[ "$ACP_AGENT" == "droid" ]]; then + openclaw_live_acp_bind_load_factory_api_key_from_profile + fi if [[ "$ACP_AGENT" == "droid" && -z "${FACTORY_API_KEY:-}" ]]; then echo "==> Run ACP bind live test in Docker" echo "==> Agent: $ACP_AGENT" diff --git a/src/gateway/gateway-acp-bind.live.test.ts b/src/gateway/gateway-acp-bind.live.test.ts index 4d79b6a07d4a..55046e98b770 100644 --- a/src/gateway/gateway-acp-bind.live.test.ts +++ b/src/gateway/gateway-acp-bind.live.test.ts @@ -580,9 +580,21 @@ async function pollCronJobVisibleViaCli(params: { env: NodeJS.ProcessEnv; expectedName: string; expectedMessage: string; -}): Promise<{ job?: Awaited>; pollsUsed: number }> { +}): Promise<{ + error?: string; + job?: Awaited>; + pollsUsed: number; +}> { for (let verifyAttempt = 0; verifyAttempt < ACP_CRON_MCP_PROBE_VERIFY_POLLS; verifyAttempt += 1) { - const job = await assertCronJobVisibleViaCli(params); + let job: Awaited>; + try { + job = await assertCronJobVisibleViaCli(params); + } catch (error) { + return { + error: error instanceof Error ? error.message : String(error), + pollsUsed: verifyAttempt + 1, + }; + } if (job) { return { job, pollsUsed: verifyAttempt + 1 }; } @@ -1063,6 +1075,14 @@ describeLive("gateway live (ACP bind)", () => { expectedMessage: cronProbe.message, }); const createdJob = verifyResult.job; + if (verifyResult.error) { + lastCronMismatch = verifyResult.error; + logLiveStep( + `cron cli verification failed after attempt ${String( + attempt + 1, + )}; polls=${String(verifyResult.pollsUsed)}; error=${lastCronMismatch}`, + ); + } if (createdJob) { try { assertCronJobMatches({ diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index c07446dfcfc5..ab67793a57f6 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -918,6 +918,11 @@ describe("package artifact reuse", () => { it("fails Droid ACP Docker live proof when Factory auth is missing", () => { const script = readFileSync("scripts/test-live-acp-bind-docker.sh", "utf8"); + expect(script).toContain("openclaw_live_acp_bind_load_factory_api_key_from_profile"); + expect(script).not.toContain('source "$PROFILE_FILE"'); + expect(script.indexOf("openclaw_live_acp_bind_load_factory_api_key_from_profile")).toBeLessThan( + script.indexOf('if [[ "$ACP_AGENT" == "droid" && -z "${FACTORY_API_KEY:-}" ]]; then'), + ); expect(script).toContain( "ERROR: Droid Docker ACP bind requires FACTORY_API_KEY; Factory OAuth/keyring auth in ~/.factory is not portable into the container.", );