test: isolate Docker live profile-key auth
This commit is contained in:
@@ -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`
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user