test: isolate Docker live profile-key auth

This commit is contained in:
Peter Steinberger
2026-04-15 06:29:34 -07:00
parent ec4c2cb62c
commit 20cce166ef
3 changed files with 63 additions and 3 deletions
+1
View File
@@ -902,6 +902,7 @@ Useful env vars:
- `OPENCLAW_CONFIG_DIR=...` (default: `~/.openclaw`) mounted to `/home/node/.openclaw`
- `OPENCLAW_WORKSPACE_DIR=...` (default: `~/.openclaw/workspace`) mounted to `/home/node/.openclaw/workspace`
- `OPENCLAW_PROFILE_FILE=...` (default: `~/.profile`) mounted to `/home/node/.profile` and sourced before running tests
- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1` to verify only env vars sourced from `OPENCLAW_PROFILE_FILE`, using temporary config/workspace dirs and no external CLI auth mounts
- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...` (default: `~/.cache/openclaw/docker-cli-tools`) mounted to `/home/node/.npm-global` for cached CLI installs inside Docker
- External CLI auth dirs/files under `$HOME` are mounted read-only under `/host-auth...`, then copied into `/home/node/...` before tests start
- Default dirs: `.minimax`
+30 -2
View File
@@ -5,10 +5,37 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/live-docker-auth.sh"
IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}"
LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}"
CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}"
WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}"
PROFILE_FILE="${OPENCLAW_PROFILE_FILE:-$HOME/.profile}"
openclaw_live_truthy() {
case "${1:-}" in
1 | true | TRUE | yes | YES | on | ON)
return 0
;;
*)
return 1
;;
esac
}
TEMP_DIRS=()
cleanup_temp_dirs() {
if ((${#TEMP_DIRS[@]} > 0)); then
rm -rf "${TEMP_DIRS[@]}"
fi
}
trap cleanup_temp_dirs EXIT
if openclaw_live_truthy "${OPENCLAW_DOCKER_PROFILE_ENV_ONLY:-}"; then
CONFIG_DIR="$(mktemp -d)"
WORKSPACE_DIR="$(mktemp -d)"
TEMP_DIRS+=("$CONFIG_DIR" "$WORKSPACE_DIR")
OPENCLAW_DOCKER_AUTH_DIRS=none
else
CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}"
WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}"
fi
PROFILE_MOUNT=()
if [[ -f "$PROFILE_FILE" ]]; then
PROFILE_MOUNT=(-v "$PROFILE_FILE":/home/node/.profile:ro)
@@ -124,6 +151,7 @@ EOF
echo "==> Run live model tests (profile keys)"
echo "==> Target: src/agents/models.profiles.live.test.ts"
echo "==> Profile env only: ${OPENCLAW_DOCKER_PROFILE_ENV_ONLY:-0}"
echo "==> External auth dirs: ${AUTH_DIRS_CSV:-none}"
echo "==> External auth files: ${AUTH_FILES_CSV:-none}"
docker run --rm -t \
+32 -1
View File
@@ -20,7 +20,10 @@ import { isLiveProfileKeyModeEnabled, isLiveTestEnabled } from "./live-test-help
import { getApiKeyForModel, requireApiKey } from "./model-auth.js";
import { shouldSuppressBuiltInModel } from "./model-suppression.js";
import { ensureOpenClawModelsJson } from "./models-config.js";
import { isRateLimitErrorMessage } from "./pi-embedded-helpers/errors.js";
import {
isCloudflareOrHtmlErrorPage,
isRateLimitErrorMessage,
} from "./pi-embedded-helpers/errors.js";
import { discoverAuthStorage, discoverModels } from "./pi-model-discovery.js";
const LIVE = isLiveTestEnabled();
@@ -162,6 +165,24 @@ describe("isModelNotFoundErrorMessage", () => {
});
});
describe("isProviderUnavailableErrorMessage", () => {
it("matches raw HTML provider error pages from transient upstreams", () => {
expect(
isProviderUnavailableErrorMessage(
"Error: <html><head><title>Service Unavailable</title></head><body>try again</body></html>",
),
).toBe(true);
});
it("matches status-prefixed Cloudflare HTML pages", () => {
expect(
isProviderUnavailableErrorMessage(
"521 <!DOCTYPE html><html><head><title>Web server is down</title></head><body>Cloudflare</body></html>",
),
).toBe(true);
});
});
function isChatGPTUsageLimitErrorMessage(raw: string): boolean {
const msg = raw.toLowerCase();
return msg.includes("hit your chatgpt usage limit") && msg.includes("try again in");
@@ -190,6 +211,8 @@ function isModelTimeoutError(raw: string): boolean {
function isProviderUnavailableErrorMessage(raw: string): boolean {
const msg = raw.toLowerCase();
return (
isRawHtmlProviderErrorPage(raw) ||
isCloudflareOrHtmlErrorPage(raw) ||
msg.includes("no allowed providers are available") ||
msg.includes("provider unavailable") ||
msg.includes("upstream provider unavailable") ||
@@ -201,6 +224,14 @@ function isProviderUnavailableErrorMessage(raw: string): boolean {
);
}
function isRawHtmlProviderErrorPage(raw: string): boolean {
const normalized = raw
.trim()
.replace(/^error:\s*/i, "")
.trim();
return /^(?:<!doctype\s+html\b|<html\b)/i.test(normalized) && /<\/html>/i.test(normalized);
}
function isOllamaUnavailableErrorMessage(raw: string): boolean {
const msg = raw.toLowerCase();
return (