mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
33ea7ffa54
* fix(qa): isolate package Telegram harness Keep private QA source, dependencies, taxonomy, and SDK dist in the trusted harness while the installed candidate owns its CLI, Gateway runtime, and persisted mock auth. Preserve the documented package RTT canary after taxonomy selection. Co-authored-by: Dallin Romney <dallinromney@gmail.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * fix(qa): export private QA harness SDK entries Canonicalize the QA-only plugin SDK entries shared by the private build and package Telegram harness manifest so qa-runtime and qa-lab resolve from trusted dist. * fix(qa): expose private runtime to package harness * fix(qa): surface Telegram observer conflicts * fix(qa): accept separate preview and final messages * test(qa): exercise Telegram poll delay contract --------- Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
521 lines
22 KiB
TypeScript
521 lines
22 KiB
TypeScript
// Npm Telegram Live tests cover npm telegram live script behavior.
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { testing } from "../../scripts/e2e/npm-telegram-live-runner.ts";
|
|
import { privateLocalOnlyPluginSdkEntrypoints } from "../../scripts/lib/plugin-sdk-entries.mjs";
|
|
|
|
const TEST_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
const DOCKER_SCRIPT_PATH = path.resolve(TEST_DIR, "../../scripts/e2e/npm-telegram-live-docker.sh");
|
|
const PREPARE_PACKAGE_PATH = path.resolve(
|
|
TEST_DIR,
|
|
"../../scripts/e2e/lib/npm-telegram-live/prepare-package.mjs",
|
|
);
|
|
const tempRoots: string[] = [];
|
|
|
|
function mkTempRoot() {
|
|
const root = mkdtempSync(path.join(tmpdir(), "openclaw-npm-telegram-live-"));
|
|
tempRoots.push(root);
|
|
return root;
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const root of tempRoots.splice(0)) {
|
|
rmSync(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
describe("package Telegram live Docker E2E", () => {
|
|
it("supports npm-specific Convex credential aliases", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE");
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE");
|
|
expect(script).toContain('docker_env+=(-e OPENCLAW_QA_CREDENTIAL_SOURCE="$credential_source")');
|
|
expect(script).toContain('docker_env+=(-e OPENCLAW_QA_CREDENTIAL_ROLE="$credential_role")');
|
|
});
|
|
|
|
it("defaults CI runs to Convex when broker credentials are present", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain(
|
|
'if [ -n "${CI:-}" ] && [ -n "${OPENCLAW_QA_CONVEX_SITE_URL:-}" ]; then',
|
|
);
|
|
expect(script).toContain("OPENCLAW_QA_CONVEX_SECRET_CI");
|
|
expect(script).toContain("OPENCLAW_QA_CONVEX_SECRET_MAINTAINER");
|
|
expect(script).toContain('printf "convex"');
|
|
});
|
|
|
|
it("installs the package candidate before forwarding runtime secrets", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
const installRunStart = script.indexOf('echo "Running package Telegram live Docker E2E');
|
|
const installRunEnd = script.indexOf("# Mount the trusted current-source QA harness");
|
|
const installRun = script.slice(installRunStart, installRunEnd);
|
|
|
|
expect(installRunStart).toBeGreaterThanOrEqual(0);
|
|
expect(installRunEnd).toBeGreaterThan(installRunStart);
|
|
expect(installRun).toContain(
|
|
'-e OPENCLAW_E2E_NPM_INSTALL_TIMEOUT="${OPENCLAW_E2E_NPM_INSTALL_TIMEOUT:-600s}"',
|
|
);
|
|
expect(installRun).toContain(
|
|
'"$timeout_bin" --kill-after=30s "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
|
|
);
|
|
expect(installRun).toContain("elif command -v gtimeout >/dev/null 2>&1; then");
|
|
expect(installRun).toContain('timeout_bin="gtimeout"');
|
|
expect(installRun).toContain(
|
|
'echo "timeout or gtimeout is required for OPENCLAW_E2E_NPM_INSTALL_TIMEOUT=$npm_install_timeout" >&2',
|
|
);
|
|
expect(installRun).toContain('"$timeout_bin" --kill-after=1s 1s true >/dev/null 2>&1');
|
|
expect(installRun).toContain(
|
|
'"$timeout_bin" "$npm_install_timeout" npm install -g "$install_source" --no-fund --no-audit',
|
|
);
|
|
expect(installRun).toContain('npm install -g "$install_source" --no-fund --no-audit');
|
|
expect(installRun).not.toContain(
|
|
"running package install without OPENCLAW_E2E_NPM_INSTALL_TIMEOUT",
|
|
);
|
|
expect(installRun).toContain('"${package_mount_args[@]}"');
|
|
expect(installRun).not.toContain('"${docker_env[@]}"');
|
|
expect(installRun).toContain(
|
|
'run_logged_print_heartbeat "npm-telegram-package-install" 60 docker_e2e_docker_run_cmd run --rm',
|
|
);
|
|
expect(installRun).not.toContain("run_logged_print_heartbeat docker run --rm");
|
|
expect(script).toContain(
|
|
'run_logged_print_heartbeat "npm-telegram-live-suite" 60 docker_e2e_run_with_harness',
|
|
);
|
|
expect(script).not.toContain('cat "$run_log"');
|
|
expect(script).toContain('"${docker_env[@]}"');
|
|
expect(script).toContain(
|
|
'if [ -z "$credential_role" ] && [ "$credential_source" = "convex" ]; then',
|
|
);
|
|
expect(script).toContain('credential_role="ci"');
|
|
expect(script).toContain('credential_role="maintainer"');
|
|
});
|
|
|
|
it("bounds installed-package hot path OpenClaw commands", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
const runtimeRunStart = script.indexOf("# Mount the trusted current-source QA harness");
|
|
const runtimeRun = script.slice(runtimeRunStart);
|
|
|
|
expect(runtimeRunStart).toBeGreaterThanOrEqual(0);
|
|
expect(script).toContain(
|
|
'-e OPENCLAW_E2E_COMMAND_TIMEOUT="${OPENCLAW_E2E_COMMAND_TIMEOUT:-300s}"',
|
|
);
|
|
expect(runtimeRun).toContain("source scripts/lib/openclaw-e2e-instance.sh");
|
|
expect(runtimeRun).toContain('sut_command="/npm-global/bin/openclaw"');
|
|
expect(runtimeRun).toContain('openclaw_e2e_run_command "$sut_command" --version');
|
|
expect(runtimeRun).toContain('openclaw_e2e_run_command "$sut_command" onboard');
|
|
expect(runtimeRun).toContain(
|
|
'OPENAI_API_KEY="$hotpath_model_value" openclaw_e2e_run_command "$sut_command" onboard',
|
|
);
|
|
expect(runtimeRun).not.toContain("export OPENAI_API_KEY=");
|
|
expect(runtimeRun).toContain('openclaw_e2e_run_command "$sut_command" channels add');
|
|
expect(runtimeRun).toContain('openclaw_e2e_run_command "$sut_command" doctor --fix');
|
|
expect(runtimeRun).toContain(
|
|
'openclaw_e2e_run_command "$sut_command" doctor --non-interactive',
|
|
);
|
|
expect(runtimeRun).toContain('export OPENCLAW_NPM_TELEGRAM_SUT_COMMAND="$sut_command"');
|
|
expect(runtimeRun).toContain('openclaw_e2e_print_log "$file"');
|
|
expect(runtimeRun).not.toContain("sed -n '1,220p'");
|
|
expect(runtimeRun).not.toMatch(/^\s*openclaw (onboard|channels add|doctor )/mu);
|
|
});
|
|
|
|
it("isolates onboarding hot-path config from the live suite", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain(
|
|
'runtime_home="$(mktemp -d "/tmp/openclaw-npm-telegram-runtime.XXXXXX")"',
|
|
);
|
|
expect(script).toContain(
|
|
'hotpath_home="$(mktemp -d "/tmp/openclaw-npm-telegram-hotpath.XXXXXX")"',
|
|
);
|
|
expect(script).toContain('export HOME="$hotpath_home"');
|
|
expect(script).toContain('export HOME="$runtime_home"');
|
|
});
|
|
|
|
it("fails fast after the first package Telegram scenario failure", () => {
|
|
const runner = readFileSync(
|
|
path.resolve(TEST_DIR, "../../scripts/e2e/npm-telegram-live-runner.ts"),
|
|
"utf8",
|
|
);
|
|
|
|
expect(runner).toContain("failFast: true");
|
|
});
|
|
|
|
it("can install a resolved package tarball instead of a registry spec", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_PACKAGE_TGZ");
|
|
expect(script).toContain("OPENCLAW_CURRENT_PACKAGE_TGZ");
|
|
expect(script).toContain('-e OPENCLAW_QA_PACKAGE_SOURCE="$package_install_source"');
|
|
expect(script).toContain('-e OPENCLAW_QA_PACKAGE_SOURCE_KIND="$package_source_kind"');
|
|
expect(script).toContain("OPENCLAW_QA_PACKAGE_SOURCE_SHA");
|
|
expect(script).toContain(
|
|
'package_mount_args=(-v "$resolved_package_tgz:$package_install_source:ro")',
|
|
);
|
|
expect(script).toContain('validate_openclaw_package_spec "$PACKAGE_SPEC"');
|
|
expect(script.indexOf('if [ -n "$resolved_package_tgz" ]; then')).toBeLessThan(
|
|
script.indexOf('validate_openclaw_package_spec "$PACKAGE_SPEC"'),
|
|
);
|
|
});
|
|
|
|
it("installs prepared root and companion tarballs through an exact local registry", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_PACKAGE_DIR");
|
|
expect(script).toContain('package_source_kind="prepared-package-set"');
|
|
expect(script).toContain('package_install_source="openclaw@$(read_package_version');
|
|
expect(script).toContain('-v "$resolved_package_dir:/package-under-test:ro"');
|
|
expect(script).toContain(
|
|
'-v "$ROOT_DIR/scripts/lib/bounded-response.mjs:/tmp/lib/bounded-response.mjs:ro"',
|
|
);
|
|
expect(script).toContain(
|
|
'-v "$ROOT_DIR/scripts/e2e/lib/plugins/npm-registry-server.mjs:/tmp/openclaw-e2e/lib/plugins/npm-registry-server.mjs:ro"',
|
|
);
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_PACKAGE_SET");
|
|
expect(script).toContain("node /tmp/openclaw-e2e/lib/plugins/npm-registry-server.mjs");
|
|
expect(script).toContain("OPENCLAW_NPM_REGISTRY_UPSTREAM=https://registry.npmjs.org");
|
|
expect(script).toContain('export NPM_CONFIG_REGISTRY="$registry_url"');
|
|
});
|
|
|
|
it("keeps live Docker artifacts isolated by default", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain(
|
|
'RUN_ID="${OPENCLAW_NPM_TELEGRAM_RUN_ID:-$(date -u +%Y%m%dT%H%M%SZ)-$$}"',
|
|
);
|
|
expect(script).toContain(
|
|
'OUTPUT_DIR="${OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR:-.artifacts/qa-e2e/npm-telegram-live/$RUN_ID}"',
|
|
);
|
|
expect(script).toContain(
|
|
'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).not.toContain(
|
|
'OUTPUT_DIR="${OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR:-.artifacts/qa-e2e/npm-telegram-live}"',
|
|
);
|
|
});
|
|
|
|
it("uses unique direct-run output dirs by default", () => {
|
|
const repoRoot = mkTempRoot();
|
|
const firstDir = testing.resolvePackageTelegramOutputDir({}, repoRoot);
|
|
const secondDir = testing.resolvePackageTelegramOutputDir({}, repoRoot);
|
|
|
|
expect(path.dirname(firstDir)).toBe(path.join(repoRoot, ".artifacts", "qa-e2e"));
|
|
expect(path.basename(firstDir)).toMatch(/^npm-telegram-live-[a-z0-9]+-[a-f0-9]{8}$/u);
|
|
expect(secondDir).not.toBe(firstDir);
|
|
expect(
|
|
testing.resolvePackageTelegramOutputDir(
|
|
{ OPENCLAW_NPM_TELEGRAM_OUTPUT_DIR: ".artifacts/custom" },
|
|
repoRoot,
|
|
),
|
|
).toBe(".artifacts/custom");
|
|
});
|
|
|
|
it("keeps the installed OpenClaw command as the package SUT", async () => {
|
|
const prefix = mkTempRoot();
|
|
const command = path.join(prefix, "bin", "openclaw");
|
|
const harnessCommand = path.join(mkTempRoot(), "bin", "openclaw");
|
|
mkdirSync(path.dirname(command), { recursive: true });
|
|
mkdirSync(path.dirname(harnessCommand), { recursive: true });
|
|
writeFileSync(command, "#!/bin/sh\n", { mode: 0o755 });
|
|
writeFileSync(harnessCommand, "#!/bin/sh\n", { mode: 0o755 });
|
|
|
|
await expect(
|
|
testing.resolveTrustedOpenClawCommand(command, {
|
|
NPM_CONFIG_PREFIX: prefix,
|
|
}),
|
|
).resolves.toEqual({
|
|
executablePath: command,
|
|
usePackagedPlugins: true,
|
|
});
|
|
await expect(
|
|
testing.resolveTrustedOpenClawCommand(harnessCommand, {
|
|
NPM_CONFIG_PREFIX: prefix,
|
|
}),
|
|
).rejects.toThrow("OPENCLAW_NPM_TELEGRAM_SUT_COMMAND must resolve inside NPM_CONFIG_PREFIX.");
|
|
});
|
|
|
|
it("mounts the QA taxonomy without exposing the repository root", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain('-v "$ROOT_DIR/taxonomy.yaml:/app/taxonomy.yaml:ro"');
|
|
expect(script).not.toContain('-v "$ROOT_DIR:/app');
|
|
});
|
|
|
|
it("mounts configured output paths before entering the container", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
const dockerEnvStart = script.indexOf("docker_env=(");
|
|
const dockerEnvEnd = script.indexOf(")\n\nforward_env_if_set", dockerEnvStart);
|
|
const dockerEnv = script.slice(dockerEnvStart, dockerEnvEnd);
|
|
|
|
expect(script).toContain('*) OUTPUT_DIR_HOST="$ROOT_DIR/$OUTPUT_DIR" ;;');
|
|
expect(script).toContain('mkdir -p "$OUTPUT_DIR_HOST"');
|
|
expect(script).toContain(
|
|
'printf \'schema=1\\nexit_code=%s\\nlive_output=job_log\\n\' "$rc" > "$OUTPUT_DIR_HOST/run-metadata.txt"',
|
|
);
|
|
expect(script).toContain("trap cleanup EXIT");
|
|
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"');
|
|
});
|
|
|
|
it("uses the container temp root for OpenClaw runtime scratch files", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
const dockerEnvStart = script.indexOf("docker_env=(");
|
|
const dockerEnvEnd = script.indexOf(")\n\nforward_env_if_set", dockerEnvStart);
|
|
const dockerEnv = script.slice(dockerEnvStart, dockerEnvEnd);
|
|
|
|
expect(dockerEnvStart).toBeGreaterThanOrEqual(0);
|
|
expect(dockerEnvEnd).toBeGreaterThan(dockerEnvStart);
|
|
expect(dockerEnv).toContain("-e TMPDIR=/tmp");
|
|
});
|
|
|
|
it("forwards repeated RTT controls to the package Telegram live lane", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_RTT_SAMPLES");
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_RTT_TIMEOUT_MS");
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_RTT_MAX_FAILURES");
|
|
expect(script).toContain("OPENCLAW_NPM_TELEGRAM_RTT_CHECKS");
|
|
});
|
|
|
|
it("isolates the trusted private QA harness from the installed package candidate", () => {
|
|
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
|
expect(script).toContain('cp "$ROOT_DIR/package.json" "$harness_package_json"');
|
|
expect(script).toContain(
|
|
'node "$ROOT_DIR/scripts/e2e/lib/npm-telegram-live/prepare-package.mjs" "$harness_package_json"',
|
|
);
|
|
expect(script).toContain('-v "$harness_package_json:/app/package.json:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/dist:/app/dist:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/node_modules:/trusted-harness/node_modules:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/packages:/app/packages:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/extensions:/app/extensions:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/taxonomy.yaml:/app/taxonomy.yaml:ro"');
|
|
expect(script).toContain('-v "$ROOT_DIR/qa/scenarios:/app/qa/scenarios:ro"');
|
|
expect(script).toContain("for dependency_dir in /trusted-harness/node_modules/*");
|
|
expect(script).toContain("for workspace_dir in /app/packages/* /app/extensions/*");
|
|
expect(script).toContain('link_harness_dependency "$workspace_dir" "$workspace_name"');
|
|
expect(script).toContain("link_harness_dependency /app openclaw");
|
|
expect(script).not.toContain('openclaw_package_dir="/npm-global/lib/node_modules/openclaw"');
|
|
expect(script).not.toContain('cp "$openclaw_package_dir/package.json" /app/package.json');
|
|
expect(script).not.toContain("/app/node_modules/openclaw/package.json");
|
|
expect(script).not.toContain("link_installed_package_dependency");
|
|
});
|
|
|
|
it("adds private SDK exports only to the trusted harness manifest", () => {
|
|
const root = mkTempRoot();
|
|
const harnessManifestPath = path.join(root, "harness-package.json");
|
|
const candidateManifestPath = path.join(root, "candidate-package.json");
|
|
const existingGatewayExport = {
|
|
types: "./existing/gateway-runtime.d.ts",
|
|
default: "./existing/gateway-runtime.js",
|
|
};
|
|
writeFileSync(
|
|
harnessManifestPath,
|
|
`${JSON.stringify({
|
|
name: "openclaw",
|
|
exports: {
|
|
"./kept": "./dist/kept.js",
|
|
"./plugin-sdk/gateway-runtime": existingGatewayExport,
|
|
},
|
|
})}\n`,
|
|
);
|
|
writeFileSync(candidateManifestPath, '{"name":"candidate","exports":{}}\n');
|
|
const candidateBefore = readFileSync(candidateManifestPath, "utf8");
|
|
|
|
execFileSync(process.execPath, [PREPARE_PACKAGE_PATH, harnessManifestPath]);
|
|
|
|
const prepared = JSON.parse(readFileSync(harnessManifestPath, "utf8")) as {
|
|
exports: Record<string, unknown>;
|
|
};
|
|
expect(prepared.exports["./kept"]).toBe("./dist/kept.js");
|
|
expect(prepared.exports["./plugin-sdk/gateway-runtime"]).toEqual(existingGatewayExport);
|
|
expect(prepared.exports["./plugin-sdk/qa-runtime"]).toEqual({
|
|
default: "./dist/plugin-sdk/qa-runtime.js",
|
|
});
|
|
expect(prepared.exports["./plugin-sdk/qa-lab"]).toEqual({
|
|
default: "./dist/plugin-sdk/qa-lab.js",
|
|
});
|
|
for (const subpath of privateLocalOnlyPluginSdkEntrypoints) {
|
|
expect(prepared.exports[`./plugin-sdk/${subpath}`]).toEqual({
|
|
default: `./dist/plugin-sdk/${subpath}.js`,
|
|
});
|
|
}
|
|
expect(readFileSync(candidateManifestPath, "utf8")).toBe(candidateBefore);
|
|
});
|
|
|
|
it("lets npm-specific credential aliases override shared QA env", () => {
|
|
expect(
|
|
testing.resolveCredentialSource({
|
|
OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE: "convex",
|
|
OPENCLAW_QA_CREDENTIAL_SOURCE: "env",
|
|
}),
|
|
).toBe("convex");
|
|
expect(
|
|
testing.resolveCredentialRole({
|
|
OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE: "ci",
|
|
OPENCLAW_QA_CREDENTIAL_ROLE: "maintainer",
|
|
}),
|
|
).toBe("ci");
|
|
});
|
|
|
|
it("defaults package Telegram RTT for the normal package live lane", () => {
|
|
expect(testing.resolveRttOptions({})).toEqual({
|
|
scenarioId: "channel-canary",
|
|
count: 20,
|
|
timeoutMs: 30_000,
|
|
maxFailures: 20,
|
|
});
|
|
});
|
|
|
|
it("does not force default RTT onto focused non-RTT scenario runs", () => {
|
|
expect(testing.resolveRttOptions({}, ["telegram-status-command"])).toBeUndefined();
|
|
});
|
|
|
|
it("maps repeated RTT env onto package Telegram live options", () => {
|
|
expect(
|
|
testing.resolveRttOptions({
|
|
OPENCLAW_NPM_TELEGRAM_RTT_SAMPLES: "7",
|
|
OPENCLAW_NPM_TELEGRAM_RTT_TIMEOUT_MS: "45000",
|
|
OPENCLAW_NPM_TELEGRAM_RTT_MAX_FAILURES: "2",
|
|
OPENCLAW_NPM_TELEGRAM_RTT_CHECKS: "channel-canary",
|
|
}),
|
|
).toEqual({
|
|
scenarioId: "channel-canary",
|
|
count: 7,
|
|
timeoutMs: 45_000,
|
|
maxFailures: 2,
|
|
});
|
|
});
|
|
|
|
it("builds a generic suite probe for the Telegram RTT lane", () => {
|
|
const probe = testing.createRoundTripProbe(testing.resolveRttOptions({}));
|
|
|
|
expect(probe).toMatchObject({
|
|
scenarioId: "channel-canary",
|
|
count: 20,
|
|
timeoutMs: 30_000,
|
|
markerPrefix: "QA-TELEGRAM-RTT",
|
|
textPrefix: "@openclaw Telegram RTT check. Reply exactly: ",
|
|
chainReplies: true,
|
|
input: {
|
|
conversation: { id: "telegram-rtt-room", kind: "group" },
|
|
},
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "promotes the default canary before taxonomy-backed release selection",
|
|
env: {},
|
|
requested: [],
|
|
resolved: ["telegram-status-command"],
|
|
expected: ["channel-canary", "telegram-status-command"],
|
|
},
|
|
{
|
|
name: "keeps focused non-RTT selections unchanged",
|
|
env: {},
|
|
requested: ["telegram-status-command"],
|
|
resolved: ["telegram-status-command"],
|
|
expected: ["telegram-status-command"],
|
|
},
|
|
{
|
|
name: "promotes an explicitly requested RTT canary",
|
|
env: { OPENCLAW_NPM_TELEGRAM_RTT_CHECKS: "channel-canary" },
|
|
requested: ["telegram-status-command"],
|
|
resolved: ["telegram-status-command"],
|
|
expected: ["channel-canary", "telegram-status-command"],
|
|
},
|
|
{
|
|
name: "does not duplicate an already selected RTT canary",
|
|
env: {},
|
|
requested: ["telegram-status-command", "channel-canary"],
|
|
resolved: ["telegram-status-command", "channel-canary"],
|
|
expected: ["channel-canary", "telegram-status-command"],
|
|
},
|
|
])("$name", ({ env, requested, resolved, expected }) => {
|
|
const options = testing.resolveRttOptions(env, requested);
|
|
|
|
expect(testing.prioritizeRoundTripProbeScenario(resolved, options)).toEqual(expected);
|
|
});
|
|
|
|
it("rejects retired RTT scenario ids", () => {
|
|
expect(() =>
|
|
testing.resolveRttOptions({
|
|
OPENCLAW_NPM_TELEGRAM_RTT_CHECKS: "telegram-mentioned-message-reply",
|
|
}),
|
|
).toThrow("unknown Telegram QA RTT check: telegram-mentioned-message-reply");
|
|
});
|
|
|
|
it("rejects invalid repeated RTT env", () => {
|
|
expect(() =>
|
|
testing.resolveRttOptions({
|
|
OPENCLAW_NPM_TELEGRAM_RTT_SAMPLES: "7samples",
|
|
}),
|
|
).toThrow("invalid OPENCLAW_NPM_TELEGRAM_RTT_SAMPLES: 7samples");
|
|
});
|
|
|
|
it.each(["fail", "skip", "skipped", "timeout"])(
|
|
"fails package Telegram QA when a scenario has %s status",
|
|
async (status) => {
|
|
const summaryPath = path.join(mkTempRoot(), "qa-evidence.json");
|
|
writeFileSync(
|
|
summaryPath,
|
|
JSON.stringify({
|
|
kind: "openclaw.qa.evidence-summary",
|
|
schemaVersion: 2,
|
|
generatedAt: "2026-05-01T00:00:00.000Z",
|
|
entries: [{ result: { status } }],
|
|
}),
|
|
"utf8",
|
|
);
|
|
|
|
await expect(
|
|
testing.shouldFailPackageTelegramRun(
|
|
{ summaryPath },
|
|
{ OPENCLAW_NPM_TELEGRAM_ALLOW_FAILURES: "" },
|
|
),
|
|
).resolves.toBe(true);
|
|
},
|
|
);
|
|
|
|
it("passes package Telegram QA when every scenario passes", async () => {
|
|
const summaryPath = path.join(mkTempRoot(), "qa-evidence.json");
|
|
writeFileSync(
|
|
summaryPath,
|
|
JSON.stringify({
|
|
kind: "openclaw.qa.evidence-summary",
|
|
schemaVersion: 2,
|
|
generatedAt: "2026-05-01T00:00:00.000Z",
|
|
entries: [{ result: { status: "pass" } }],
|
|
}),
|
|
"utf8",
|
|
);
|
|
|
|
await expect(
|
|
testing.shouldFailPackageTelegramRun(
|
|
{ summaryPath },
|
|
{ OPENCLAW_NPM_TELEGRAM_ALLOW_FAILURES: "" },
|
|
),
|
|
).resolves.toBe(false);
|
|
});
|
|
|
|
it("does not read package Telegram summaries when failures are allowed", async () => {
|
|
await expect(
|
|
testing.shouldFailPackageTelegramRun(
|
|
{ summaryPath: path.join(mkTempRoot(), "missing-summary.json") },
|
|
{ OPENCLAW_NPM_TELEGRAM_ALLOW_FAILURES: "1" },
|
|
),
|
|
).resolves.toBe(false);
|
|
});
|
|
});
|