mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
test: harden Docker E2E readiness
This commit is contained in:
@@ -516,7 +516,7 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
- Gateway + dev agent: `pnpm test:docker:live-gateway` (script: `scripts/test-live-gateway-models-docker.sh`)
|
||||
- Open WebUI live smoke: `pnpm test:docker:openwebui` (script: `scripts/e2e/openwebui-docker.sh`)
|
||||
- Onboarding wizard (TTY, full scaffolding): `pnpm test:docker:onboard` (script: `scripts/e2e/onboard-docker.sh`)
|
||||
- Npm tarball onboarding/channel/agent smoke: `pnpm test:docker:npm-onboard-channel-agent` installs the packed OpenClaw tarball globally in Docker, configures OpenAI via env-ref onboarding plus Telegram by default, verifies enabling the plugin installs its runtime deps on demand, runs doctor, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host rebuild with `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, or switch channel with `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`.
|
||||
- Npm tarball onboarding/channel/agent smoke: `pnpm test:docker:npm-onboard-channel-agent` installs the packed OpenClaw tarball globally in Docker, configures OpenAI via env-ref onboarding plus Telegram by default, verifies doctor repairs activated plugin runtime deps, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host rebuild with `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, or switch channel with `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`.
|
||||
- Bun global install smoke: `bash scripts/e2e/bun-global-install-smoke.sh` packs the current tree, installs it with `bun install -g` in an isolated home, and verifies `openclaw infer image providers --json` returns bundled image providers instead of hanging. Reuse a prebuilt tarball with `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host build with `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0`, or copy `dist/` from a built Docker image with `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local`.
|
||||
- Installer Docker smoke: `bash scripts/test-install-sh-docker.sh` shares one npm cache across its root, update, and direct-npm containers. Update smoke defaults to npm `latest` as the stable baseline before upgrading to the candidate tarball. Non-root installer checks keep an isolated npm cache so root-owned cache entries do not mask user-local install behavior. Set `OPENCLAW_INSTALL_SMOKE_NPM_CACHE_DIR=/path/to/cache` to reuse the root/update/direct-npm cache across local reruns.
|
||||
- Install Smoke CI skips the duplicate direct-npm global update with `OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL=1`; run the script locally without that env when direct `npm install -g` coverage is needed.
|
||||
|
||||
@@ -131,7 +131,7 @@ async function runCronCleanupScenario(params: {
|
||||
payload: {
|
||||
kind: "agentTurn",
|
||||
message: "Use available context and then stop.",
|
||||
timeoutSeconds: 12,
|
||||
timeoutSeconds: 90,
|
||||
lightContext: true,
|
||||
},
|
||||
delivery: { mode: "none" },
|
||||
@@ -182,7 +182,7 @@ async function runCronCleanupScenario(params: {
|
||||
entry.payload.jobId === job.id &&
|
||||
entry.payload.action === "finished",
|
||||
)?.payload,
|
||||
90_000,
|
||||
150_000,
|
||||
);
|
||||
assert(finished, "missing cron finished event");
|
||||
|
||||
@@ -212,7 +212,7 @@ async function runSubagentCleanupScenario(params: {
|
||||
cleanupBundleMcpOnRunEnd: true,
|
||||
idempotencyKey: randomUUID(),
|
||||
deliver: false,
|
||||
timeout: 20,
|
||||
timeout: 90,
|
||||
bestEffortDeliver: true,
|
||||
});
|
||||
assert(
|
||||
|
||||
@@ -34,16 +34,21 @@ async function main() {
|
||||
mcp = mcpHandle.client;
|
||||
}
|
||||
|
||||
const listed = (await mcp.callTool({
|
||||
name: "conversations_list",
|
||||
arguments: {},
|
||||
})) as {
|
||||
structuredContent?: { conversations?: Array<Record<string, unknown>> };
|
||||
};
|
||||
const conversation = listed.structuredContent?.conversations?.find(
|
||||
(entry) => entry.sessionKey === "agent:main:main",
|
||||
const conversation = await waitFor(
|
||||
"seeded conversation in conversations_list",
|
||||
async () => {
|
||||
const listed = (await mcp.callTool({
|
||||
name: "conversations_list",
|
||||
arguments: {},
|
||||
})) as {
|
||||
structuredContent?: { conversations?: Array<Record<string, unknown>> };
|
||||
};
|
||||
return listed.structuredContent?.conversations?.find(
|
||||
(entry) => entry.sessionKey === "agent:main:main",
|
||||
);
|
||||
},
|
||||
240_000,
|
||||
);
|
||||
assert(conversation, "expected seeded conversation in conversations_list");
|
||||
assert(conversation.channel === "imessage", "expected seeded channel");
|
||||
assert(conversation.to === "+15551234567", "expected seeded target");
|
||||
|
||||
@@ -60,19 +65,31 @@ async function main() {
|
||||
"conversation_get returned wrong session",
|
||||
);
|
||||
|
||||
const history = (await mcp.callTool({
|
||||
name: "messages_read",
|
||||
arguments: { session_key: "agent:main:main", limit: 10 },
|
||||
})) as {
|
||||
structuredContent?: { messages?: Array<Record<string, unknown>> };
|
||||
};
|
||||
const messages = history.structuredContent?.messages ?? [];
|
||||
assert(messages.length >= 2, "expected seeded transcript messages");
|
||||
const attachmentMessage = messages.find((entry) => {
|
||||
const raw = entry.__openclaw;
|
||||
return raw && typeof raw === "object" && (raw as { id?: unknown }).id === "msg-attachment";
|
||||
});
|
||||
assert(attachmentMessage, "expected seeded attachment message");
|
||||
const messages = await waitFor(
|
||||
"seeded transcript messages",
|
||||
async () => {
|
||||
const history = (await mcp.callTool({
|
||||
name: "messages_read",
|
||||
arguments: { session_key: "agent:main:main", limit: 10 },
|
||||
})) as {
|
||||
structuredContent?: { messages?: Array<Record<string, unknown>> };
|
||||
};
|
||||
const currentMessages = history.structuredContent?.messages ?? [];
|
||||
return currentMessages.length >= 2 ? currentMessages : undefined;
|
||||
},
|
||||
240_000,
|
||||
);
|
||||
await waitFor(
|
||||
"seeded attachment message",
|
||||
() =>
|
||||
messages.find((entry) => {
|
||||
const raw = entry.__openclaw;
|
||||
return (
|
||||
raw && typeof raw === "object" && (raw as { id?: unknown }).id === "msg-attachment"
|
||||
);
|
||||
}),
|
||||
240_000,
|
||||
);
|
||||
|
||||
const attachments = (await mcp.callTool({
|
||||
name: "attachments_fetch",
|
||||
|
||||
@@ -74,12 +74,12 @@ export function extractTextFromGatewayPayload(
|
||||
|
||||
export async function waitFor<T>(
|
||||
label: string,
|
||||
predicate: () => T | undefined,
|
||||
predicate: () => Promise<T | undefined> | T | undefined,
|
||||
timeoutMs = 10_000,
|
||||
): Promise<T> {
|
||||
const started = Date.now();
|
||||
while (Date.now() - started < timeoutMs) {
|
||||
const value = predicate();
|
||||
const value = await predicate();
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -432,10 +432,8 @@ if (!serialized.includes(token)) {
|
||||
}
|
||||
NODE
|
||||
|
||||
assert_dep_present "$DEP_SENTINEL"
|
||||
|
||||
echo "Running doctor after activated plugin dep install..."
|
||||
openclaw doctor --non-interactive >/tmp/openclaw-doctor.log 2>&1
|
||||
echo "Running doctor after channel activation..."
|
||||
openclaw doctor --repair --non-interactive >/tmp/openclaw-doctor.log 2>&1
|
||||
assert_dep_present "$DEP_SENTINEL"
|
||||
|
||||
echo "Running local agent turn against mocked OpenAI..."
|
||||
|
||||
Reference in New Issue
Block a user