mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(qa): repair package Telegram harness boundaries (#108499)
* fix(qa): keep packaged entry off private transport * fix(qa): lazy-load private transport runtime * fix(qa): pass relative package artifact path * fix(qa): isolate packaged bus protocol * fix(qa): mount package scenario catalog
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const qaChannelLoads = vi.hoisted(() => vi.fn());
|
||||
const qaChannelProtocolLoads = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/qa-channel", () => {
|
||||
qaChannelLoads();
|
||||
throw new Error("QA Lab entrypoint loaded the private QA channel");
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/qa-channel-protocol", () => {
|
||||
qaChannelProtocolLoads();
|
||||
throw new Error("QA Lab entrypoint loaded the private QA channel protocol");
|
||||
});
|
||||
|
||||
describe("QA Lab plugin entrypoint", () => {
|
||||
it("loads without the private QA transport runtime", async () => {
|
||||
const { default: plugin } = await import("./index.js");
|
||||
|
||||
expect(plugin.id).toBe("qa-lab");
|
||||
expect(qaChannelLoads).not.toHaveBeenCalled();
|
||||
expect(qaChannelProtocolLoads).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("loads the package Telegram harness without the private QA transport runtime", async () => {
|
||||
const { runQaTelegramSuite } = await import("./src/live-transports/telegram/cli.runtime.js");
|
||||
|
||||
expect(runQaTelegramSuite).toBeTypeOf("function");
|
||||
expect(qaChannelLoads).not.toHaveBeenCalled();
|
||||
expect(qaChannelProtocolLoads).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
// Qa Lab plugin entrypoint registers its OpenClaw integration.
|
||||
import { setTimeout as sleep } from "node:timers/promises";
|
||||
// Keep plugin registration independent of private QA transports, which packaged runtimes omit.
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
||||
import { definePluginEntry } from "./runtime-api.js";
|
||||
import { registerQaLabCli } from "./src/cli.js";
|
||||
import { createQaLabWebSearchProvider } from "./src/qa-web-search-provider.js";
|
||||
import { createStaticSshWorkerProvider } from "./src/static-ssh-worker-provider.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Qa Lab plugin module implements bus queries behavior.
|
||||
import { parseQaTarget } from "openclaw/plugin-sdk/qa-channel-protocol";
|
||||
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { parseQaTarget } from "./qa-bus-protocol.js";
|
||||
import type {
|
||||
QaBusAttachment,
|
||||
QaBusConversation,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Qa Lab plugin module implements bus state behavior.
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { sanitizeQaBusToolCalls } from "openclaw/plugin-sdk/qa-channel-protocol";
|
||||
import {
|
||||
buildQaBusSnapshot,
|
||||
cloneMessage,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
searchQaBusMessages,
|
||||
} from "./bus-queries.js";
|
||||
import { createQaBusWaiterStore } from "./bus-waiters.js";
|
||||
import { sanitizeQaBusToolCalls } from "./qa-bus-protocol.js";
|
||||
import type {
|
||||
QaBusAttachment,
|
||||
QaBusConversation,
|
||||
|
||||
@@ -14,7 +14,7 @@ const qaChannelMock = vi.hoisted(() => ({
|
||||
startAccount: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./runtime-api.js", () => ({
|
||||
vi.mock("openclaw/plugin-sdk/qa-channel", () => ({
|
||||
qaChannelPlugin: {
|
||||
config: {
|
||||
resolveAccount: qaChannelMock.resolveAccount,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import fs from "node:fs";
|
||||
import { createServer } from "node:http";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import {
|
||||
acquireDebugProxyCaptureStore,
|
||||
@@ -54,7 +55,6 @@ import {
|
||||
createQaRunOutputDir,
|
||||
normalizeQaRunSelection,
|
||||
} from "./run-config.js";
|
||||
import { qaChannelPlugin, setQaChannelRuntime, type OpenClawConfig } from "./runtime-api.js";
|
||||
import { readQaBootstrapScenarioCatalog } from "./scenario-catalog.js";
|
||||
import { runQaSelfCheckAgainstState, type QaSelfCheckResult } from "./self-check.js";
|
||||
|
||||
@@ -247,6 +247,7 @@ function detectQaEvidenceArtifactContentType(filePath: string): string {
|
||||
}
|
||||
|
||||
async function startQaGatewayLoop(params: { state: QaBusState; baseUrl: string }) {
|
||||
const { qaChannelPlugin, setQaChannelRuntime } = await import("openclaw/plugin-sdk/qa-channel");
|
||||
const runtime = createQaRunnerRuntime();
|
||||
setQaChannelRuntime(runtime);
|
||||
const cfg = createQaLabConfig(params.baseUrl);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
parseQaTarget as parseCanonicalQaTarget,
|
||||
sanitizeQaBusToolCalls as sanitizeCanonicalQaBusToolCalls,
|
||||
} from "openclaw/plugin-sdk/qa-channel-protocol";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseQaTarget, sanitizeQaBusToolCalls } from "./qa-bus-protocol.js";
|
||||
|
||||
describe("QA Lab package bus protocol", () => {
|
||||
it.each([
|
||||
"bare-id",
|
||||
"channel:CaseSensitive",
|
||||
"group:team-room",
|
||||
"dm:user-1",
|
||||
"thread:Room/Topic",
|
||||
])("matches the canonical target parser for %s", (target) => {
|
||||
expect(parseQaTarget(target)).toEqual(parseCanonicalQaTarget(target));
|
||||
});
|
||||
|
||||
it.each(["", "CHANNEL:CaseSensitive", "thread:Room/", "dm:"])(
|
||||
"matches canonical target errors for %j",
|
||||
(target) => {
|
||||
expect(() => parseQaTarget(target)).toThrow();
|
||||
expect(() => parseCanonicalQaTarget(target)).toThrow();
|
||||
},
|
||||
);
|
||||
|
||||
it("matches canonical bounded redaction", () => {
|
||||
const toolCalls = [
|
||||
null,
|
||||
{ name: 123 },
|
||||
{
|
||||
name: " exec ",
|
||||
arguments: {
|
||||
command: "cat README.md",
|
||||
apiToken: "secret-token",
|
||||
headers: { Authorization: "Bearer secret" },
|
||||
values: ["ok", { password: "hunter2" }],
|
||||
nested: { one: { two: { three: { four: "truncated" } } } },
|
||||
finite: 42,
|
||||
infinite: Number.POSITIVE_INFINITY,
|
||||
bigint: 123n,
|
||||
omitted: undefined,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(sanitizeQaBusToolCalls(toolCalls)).toEqual(sanitizeCanonicalQaBusToolCalls(toolCalls));
|
||||
});
|
||||
|
||||
it("matches the canonical count limit without processing the tail", () => {
|
||||
const toolCalls = Array.from({ length: 50 }, (_, index) => ({ name: `tool-${index}` }));
|
||||
toolCalls.push({
|
||||
get name(): string {
|
||||
throw new Error("tail should not be sanitized");
|
||||
},
|
||||
});
|
||||
|
||||
expect(sanitizeQaBusToolCalls(toolCalls)).toEqual(sanitizeCanonicalQaBusToolCalls(toolCalls));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
// QA Lab source is mounted into package acceptance without the local-only QA Channel SDK.
|
||||
// Keep its in-memory bus protocol self-contained; parity tests guard the shared semantics.
|
||||
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import type { QaBusConversation, QaBusToolCall } from "./runtime-api.js";
|
||||
|
||||
type QaTargetParts = {
|
||||
chatType: QaBusConversation["kind"];
|
||||
conversationId: string;
|
||||
threadId?: string;
|
||||
};
|
||||
|
||||
export function parseQaTarget(raw: string): QaTargetParts {
|
||||
const normalized = raw.trim();
|
||||
if (!normalized) {
|
||||
throw new Error("qa-channel target is required");
|
||||
}
|
||||
const prefixed = /^(thread|channel|group|dm):(.*)$/u.exec(normalized);
|
||||
if (!prefixed && /^(thread|channel|group|dm):/iu.test(normalized)) {
|
||||
throw new Error(`qa-channel target prefixes must be lowercase: ${normalized}`);
|
||||
}
|
||||
const prefix = prefixed?.[1];
|
||||
const rest = prefixed?.[2]?.trim();
|
||||
if (prefix === "thread") {
|
||||
if (!rest) {
|
||||
throw new Error(`invalid qa-channel thread target: ${normalized}`);
|
||||
}
|
||||
const slashIndex = rest.indexOf("/");
|
||||
if (slashIndex <= 0 || slashIndex === rest.length - 1) {
|
||||
throw new Error(`invalid qa-channel thread target: ${normalized}`);
|
||||
}
|
||||
const conversationId = rest.slice(0, slashIndex).trim();
|
||||
const threadId = rest.slice(slashIndex + 1).trim();
|
||||
if (!conversationId || !threadId) {
|
||||
throw new Error(`invalid qa-channel thread target: ${normalized}`);
|
||||
}
|
||||
return {
|
||||
chatType: "channel",
|
||||
conversationId,
|
||||
threadId,
|
||||
};
|
||||
}
|
||||
if (prefix) {
|
||||
if (!rest) {
|
||||
throw new Error(`invalid qa-channel ${prefix} target: ${normalized}`);
|
||||
}
|
||||
return {
|
||||
chatType: prefix === "dm" ? "direct" : prefix === "group" ? "group" : "channel",
|
||||
conversationId: rest,
|
||||
};
|
||||
}
|
||||
return {
|
||||
chatType: "direct",
|
||||
conversationId: normalized,
|
||||
};
|
||||
}
|
||||
|
||||
const TOOL_CALL_MAX_COUNT = 50;
|
||||
const TOOL_CALL_MAX_DEPTH = 4;
|
||||
const TOOL_CALL_MAX_ARRAY_LENGTH = 20;
|
||||
const TOOL_CALL_MAX_OBJECT_KEYS = 40;
|
||||
const TOOL_CALL_REDACTED = "[redacted]";
|
||||
const TOOL_CALL_SENSITIVE_KEY_RE =
|
||||
/authorization|cookie|credential|password|secret|token|api[-_]?key|access[-_]?key|private[-_]?key/iu;
|
||||
|
||||
function sanitizeToolCallValue(value: unknown, depth: number, key?: string): unknown {
|
||||
if (key && TOOL_CALL_SENSITIVE_KEY_RE.test(key)) {
|
||||
return TOOL_CALL_REDACTED;
|
||||
}
|
||||
if (value === null || typeof value === "boolean" || typeof value === "number") {
|
||||
return Number.isFinite(value as number) || typeof value !== "number" ? value : String(value);
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return TOOL_CALL_REDACTED;
|
||||
}
|
||||
if (typeof value === "bigint") {
|
||||
return value.toString();
|
||||
}
|
||||
if (value === undefined || typeof value === "function" || typeof value === "symbol") {
|
||||
return undefined;
|
||||
}
|
||||
if (depth >= TOOL_CALL_MAX_DEPTH) {
|
||||
return "[truncated]";
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.slice(0, TOOL_CALL_MAX_ARRAY_LENGTH).map((entry) => {
|
||||
return sanitizeToolCallValue(entry, depth + 1);
|
||||
});
|
||||
}
|
||||
if (isRecord(value)) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value)
|
||||
.slice(0, TOOL_CALL_MAX_OBJECT_KEYS)
|
||||
.flatMap(([entryKey, entryValue]) => {
|
||||
const sanitized = sanitizeToolCallValue(entryValue, depth + 1, entryKey);
|
||||
return sanitized === undefined ? [] : [[entryKey, sanitized]];
|
||||
}),
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function sanitizeToolCallArguments(value: unknown): Record<string, unknown> | undefined {
|
||||
if (!isRecord(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const sanitized = sanitizeToolCallValue(value, 0);
|
||||
return isRecord(sanitized) ? sanitized : undefined;
|
||||
}
|
||||
|
||||
export function sanitizeQaBusToolCalls(value: unknown): QaBusToolCall[] | undefined {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const sanitized = value.slice(0, TOOL_CALL_MAX_COUNT).flatMap((toolCall) => {
|
||||
if (!isRecord(toolCall)) {
|
||||
return [];
|
||||
}
|
||||
const name = typeof toolCall.name === "string" ? toolCall.name.trim() : "";
|
||||
if (!name) {
|
||||
return [];
|
||||
}
|
||||
const args = sanitizeToolCallArguments(toolCall.arguments);
|
||||
return [
|
||||
{
|
||||
name,
|
||||
...(args && Object.keys(args).length > 0 ? { arguments: args } : {}),
|
||||
},
|
||||
];
|
||||
});
|
||||
return sanitized.length > 0 ? sanitized : undefined;
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import type {
|
||||
QaTransportPolicy,
|
||||
QaTransportReportParams,
|
||||
} from "./qa-transport.js";
|
||||
import { qaChannelPlugin } from "./runtime-api.js";
|
||||
|
||||
const QA_CHANNEL_ID = "qa-channel";
|
||||
const QA_CHANNEL_ACCOUNT_ID = "default";
|
||||
@@ -142,6 +141,7 @@ async function handleQaChannelAction(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
}) {
|
||||
const { qaChannelPlugin } = await import("openclaw/plugin-sdk/qa-channel");
|
||||
return await qaChannelPlugin.actions?.handleAction?.({
|
||||
channel: QA_CHANNEL_ID,
|
||||
action: params.action,
|
||||
|
||||
@@ -86,7 +86,6 @@ function createDeps(overrides?: Partial<QaScenarioRuntimeDeps>): QaScenarioRunti
|
||||
liveTurnTimeoutMs: fn,
|
||||
resolveQaLiveTurnTimeoutMs: fn,
|
||||
splitModelRef: fn,
|
||||
qaChannelPlugin: { id: "qa-channel" },
|
||||
hasDiscoveryLabels: fn,
|
||||
reportsDiscoveryScopeLeak: fn,
|
||||
reportsMissingDiscoveryFiles: fn,
|
||||
|
||||
@@ -98,7 +98,6 @@ type QaScenarioRuntimeDeps = {
|
||||
liveTurnTimeoutMs: QaScenarioRuntimeFunction;
|
||||
resolveQaLiveTurnTimeoutMs: QaScenarioRuntimeFunction;
|
||||
splitModelRef: QaScenarioRuntimeFunction;
|
||||
qaChannelPlugin: unknown;
|
||||
hasDiscoveryLabels: QaScenarioRuntimeFunction;
|
||||
reportsDiscoveryScopeLeak: QaScenarioRuntimeFunction;
|
||||
reportsMissingDiscoveryFiles: QaScenarioRuntimeFunction;
|
||||
@@ -195,7 +194,6 @@ type QaScenarioRuntimeApi<
|
||||
liveTurnTimeoutMs: TDeps["liveTurnTimeoutMs"];
|
||||
resolveQaLiveTurnTimeoutMs: TDeps["resolveQaLiveTurnTimeoutMs"];
|
||||
splitModelRef: TDeps["splitModelRef"];
|
||||
qaChannelPlugin: unknown;
|
||||
hasDiscoveryLabels: TDeps["hasDiscoveryLabels"];
|
||||
reportsDiscoveryScopeLeak: TDeps["reportsDiscoveryScopeLeak"];
|
||||
reportsMissingDiscoveryFiles: TDeps["reportsMissingDiscoveryFiles"];
|
||||
@@ -309,7 +307,6 @@ export function createQaScenarioRuntimeApi<
|
||||
liveTurnTimeoutMs: params.deps.liveTurnTimeoutMs,
|
||||
resolveQaLiveTurnTimeoutMs: params.deps.resolveQaLiveTurnTimeoutMs,
|
||||
splitModelRef: params.deps.splitModelRef,
|
||||
qaChannelPlugin: params.deps.qaChannelPlugin,
|
||||
hasDiscoveryLabels: params.deps.hasDiscoveryLabels,
|
||||
reportsDiscoveryScopeLeak: params.deps.reportsDiscoveryScopeLeak,
|
||||
reportsMissingDiscoveryFiles: params.deps.reportsMissingDiscoveryFiles,
|
||||
|
||||
@@ -27,7 +27,6 @@ import { extractQaToolPayload } from "./extract-tool-payload.js";
|
||||
import { assertNoGatewayLogSentinels, scanGatewayLogSentinels } from "./gateway-log-sentinel.js";
|
||||
import { resolveQaLiveTurnTimeoutMs } from "./live-timeout.js";
|
||||
import { hasModelSwitchContinuitySignal } from "./model-switch-eval.js";
|
||||
import { qaChannelPlugin } from "./runtime-api.js";
|
||||
import { runRuntimeToolFixture } from "./runtime-tool-fixture.js";
|
||||
import type { QaSeedScenarioWithSource } from "./scenario-catalog.js";
|
||||
import { runScenarioFlow } from "./scenario-flow-runner.js";
|
||||
@@ -291,7 +290,6 @@ function createQaSuiteScenarioDeps(params: QaSuiteScenarioDepsParams) {
|
||||
liveTurnTimeoutMs: params.liveTurnTimeoutMs,
|
||||
resolveQaLiveTurnTimeoutMs: params.resolveQaLiveTurnTimeoutMs,
|
||||
splitModelRef: params.splitModelRef,
|
||||
qaChannelPlugin,
|
||||
hasDiscoveryLabels,
|
||||
reportsDiscoveryScopeLeak,
|
||||
reportsMissingDiscoveryFiles,
|
||||
|
||||
@@ -18,7 +18,8 @@ case "$OUTPUT_DIR" in
|
||||
/*) OUTPUT_DIR_HOST="$OUTPUT_DIR" ;;
|
||||
*) OUTPUT_DIR_HOST="$ROOT_DIR/$OUTPUT_DIR" ;;
|
||||
esac
|
||||
OUTPUT_DIR_CONTAINER="/app/.artifacts/qa-e2e/npm-telegram-live-output"
|
||||
OUTPUT_DIR_CONTAINER_RELATIVE=".artifacts/qa-e2e/npm-telegram-live-output"
|
||||
OUTPUT_DIR_CONTAINER="/app/$OUTPUT_DIR_CONTAINER_RELATIVE"
|
||||
|
||||
resolve_credential_source() {
|
||||
if [ -n "${OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE:-}" ]; then
|
||||
@@ -220,7 +221,7 @@ docker_env=(
|
||||
-e TMPDIR=/tmp
|
||||
-e OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC="$PACKAGE_SPEC"
|
||||
-e OPENCLAW_NPM_TELEGRAM_PACKAGE_LABEL="$PACKAGE_LABEL"
|
||||
-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER"
|
||||
-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER_RELATIVE"
|
||||
-e OPENCLAW_QA_PACKAGE_SOURCE="$package_install_source"
|
||||
-e OPENCLAW_QA_PACKAGE_SOURCE_KIND="$package_source_kind"
|
||||
-e OPENCLAW_QA_RUNNER="${OPENCLAW_QA_RUNNER:-docker}"
|
||||
@@ -416,6 +417,7 @@ run_logged docker_e2e_run_with_harness \
|
||||
-v "$ROOT_DIR/.artifacts:/app/.artifacts" \
|
||||
-v "$OUTPUT_DIR_HOST:$OUTPUT_DIR_CONTAINER" \
|
||||
-v "$ROOT_DIR/extensions/qa-lab:/app/extensions/qa-lab:ro" \
|
||||
-v "$ROOT_DIR/qa/scenarios:/app/qa/scenarios:ro" \
|
||||
-v "$npm_prefix_host:/npm-global" \
|
||||
-i "$IMAGE_NAME" bash -s <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
@@ -156,9 +156,12 @@ describe("package Telegram live Docker E2E", () => {
|
||||
'OUTPUT_DIR="${OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR:-.artifacts/qa-e2e/npm-telegram-live/$RUN_ID}"',
|
||||
);
|
||||
expect(script).toContain(
|
||||
'OUTPUT_DIR_CONTAINER="/app/.artifacts/qa-e2e/npm-telegram-live-output"',
|
||||
'OUTPUT_DIR_CONTAINER_RELATIVE=".artifacts/qa-e2e/npm-telegram-live-output"',
|
||||
);
|
||||
expect(script).toContain('OUTPUT_DIR_CONTAINER="/app/$OUTPUT_DIR_CONTAINER_RELATIVE"');
|
||||
expect(script).toContain(
|
||||
'-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER_RELATIVE"',
|
||||
);
|
||||
expect(script).toContain('-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER"');
|
||||
expect(script).not.toContain(
|
||||
'OUTPUT_DIR="${OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR:-.artifacts/qa-e2e/npm-telegram-live}"',
|
||||
);
|
||||
@@ -204,7 +207,10 @@ describe("package Telegram live Docker E2E", () => {
|
||||
|
||||
expect(script).toContain('*) OUTPUT_DIR_HOST="$ROOT_DIR/$OUTPUT_DIR" ;;');
|
||||
expect(script).toContain('mkdir -p "$OUTPUT_DIR_HOST"');
|
||||
expect(dockerEnv).toContain('-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER"');
|
||||
expect(dockerEnv).toContain(
|
||||
'-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER_RELATIVE"',
|
||||
);
|
||||
expect(dockerEnv).not.toContain('-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR_CONTAINER"');
|
||||
expect(dockerEnv).not.toContain('-e OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR="$OUTPUT_DIR"');
|
||||
expect(script).toContain('-v "$OUTPUT_DIR_HOST:$OUTPUT_DIR_CONTAINER"');
|
||||
});
|
||||
@@ -251,6 +257,7 @@ describe("package Telegram live Docker E2E", () => {
|
||||
expect(script).toContain('ln -sfnT "$openclaw_package_dir/dist" /app/dist');
|
||||
expect(script).toContain('cp "$openclaw_package_dir/package.json" /app/package.json');
|
||||
expect(script).toContain('-v "$ROOT_DIR/extensions/qa-lab:/app/extensions/qa-lab:ro"');
|
||||
expect(script).toContain('-v "$ROOT_DIR/qa/scenarios:/app/qa/scenarios:ro"');
|
||||
expect(script).not.toContain('ln -sfnT /app/extensions "$openclaw_package_dir/extensions"');
|
||||
expect(script).toContain("node scripts/e2e/lib/npm-telegram-live/prepare-package.mjs");
|
||||
expect(script).toContain("/app/node_modules/openclaw/package.json");
|
||||
|
||||
Reference in New Issue
Block a user