From cb9824d6b4a4fa3bee098e4cb8b15b7465dcacaa Mon Sep 17 00:00:00 2001 From: Kevin Lin Date: Mon, 4 May 2026 16:51:34 -0700 Subject: [PATCH] test: add slack onboarding channel smoke (#77575) --- docs/help/testing.md | 2 +- package.json | 1 + .../npm-onboard-channel-agent/assertions.mjs | 12 +++++++++--- .../e2e/npm-onboard-channel-agent-docker.sh | 19 +++++++++++++++---- scripts/lib/docker-e2e-scenarios.mjs | 10 ++++++++++ scripts/lib/plugin-prerelease-test-plan.mjs | 4 ++++ test/scripts/docker-e2e-plan.test.ts | 5 +++++ .../plugin-prerelease-test-plan.test.ts | 1 + 8 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/help/testing.md b/docs/help/testing.md index daaa48c0163..d956ccc28fe 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -640,7 +640,7 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or - Observability smoke: `pnpm qa:otel:smoke` is a private QA source-checkout lane. It is intentionally not part of package Docker release lanes because the npm tarball omits QA Lab. - 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, runs doctor, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_CURRENT_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, runs doctor, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_CURRENT_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` or `OPENCLAW_NPM_ONBOARD_CHANNEL=slack`. - Update channel switch smoke: `pnpm test:docker:update-channel-switch` installs the packed OpenClaw tarball globally in Docker, switches from package `stable` to git `dev`, verifies the persisted channel and plugin post-update work, then switches back to package `stable` and checks update status. - Upgrade survivor smoke: `pnpm test:docker:upgrade-survivor` installs the packed OpenClaw tarball over a dirty old-user fixture with agents, channel config, plugin allowlists, stale plugin dependency state, and existing workspace/session files. It runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks config/state preservation plus startup/status budgets. - Published upgrade survivor smoke: `pnpm test:docker:published-upgrade-survivor` installs `openclaw@latest` by default, seeds realistic existing-user files, configures that baseline with a baked command recipe, validates the resulting config, updates that published install to the candidate tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks configured intents, state preservation, startup, `/healthz`, `/readyz`, and RPC status budgets. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, ask the aggregate scheduler to expand exact baselines with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS` such as `all-since-2026.4.23`, and expand issue-shaped fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` such as `reported-issues`; the reported-issues set includes `configured-plugin-installs` for automatic external OpenClaw plugin install repair. Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`; Full Release Validation uses the default latest baseline in the blocking path and expands to all-since/reported-issues only for `run_release_soak=true` or `release_profile=full`. diff --git a/package.json b/package.json index c839b15e853..8956bffa7c5 100644 --- a/package.json +++ b/package.json @@ -1553,6 +1553,7 @@ "test:docker:mcp-channels": "bash scripts/e2e/mcp-channels-docker.sh", "test:docker:npm-onboard-channel-agent": "bash scripts/e2e/npm-onboard-channel-agent-docker.sh", "test:docker:npm-onboard-discord-channel-agent": "OPENCLAW_NPM_ONBOARD_CHANNEL=discord bash scripts/e2e/npm-onboard-channel-agent-docker.sh", + "test:docker:npm-onboard-slack-channel-agent": "OPENCLAW_NPM_ONBOARD_CHANNEL=slack bash scripts/e2e/npm-onboard-channel-agent-docker.sh", "test:docker:npm-telegram-live": "bash scripts/e2e/npm-telegram-live-docker.sh", "test:docker:onboard": "bash scripts/e2e/onboard-docker.sh", "test:docker:openai-image-auth": "bash scripts/e2e/openai-image-auth-docker.sh", diff --git a/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs b/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs index 26c35392a48..b321f0c13c5 100644 --- a/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs +++ b/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs @@ -83,15 +83,21 @@ function configureMockModel() { function assertChannelConfig() { const channel = process.argv[3]; - const token = process.argv[4]; + const expectedTokens = process.argv.slice(4); + if (expectedTokens.length === 0) { + throw new Error("assert-channel-config requires at least one expected token"); + } const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json"); const cfg = readJson(configPath); const entry = cfg.channels?.[channel]; if (!entry || entry.enabled === false) { throw new Error(`${channel} was not enabled`); } - if (!JSON.stringify(entry).includes(token)) { - throw new Error(`${channel} token was not persisted`); + const serializedEntry = JSON.stringify(entry); + for (const token of expectedTokens) { + if (!serializedEntry.includes(token)) { + throw new Error(`${channel} token was not persisted`); + } } } diff --git a/scripts/e2e/npm-onboard-channel-agent-docker.sh b/scripts/e2e/npm-onboard-channel-agent-docker.sh index 16e827d76fc..dd59373b9ab 100644 --- a/scripts/e2e/npm-onboard-channel-agent-docker.sh +++ b/scripts/e2e/npm-onboard-channel-agent-docker.sh @@ -14,9 +14,9 @@ PACKAGE_TGZ="${OPENCLAW_CURRENT_PACKAGE_TGZ:-}" CHANNEL="${OPENCLAW_NPM_ONBOARD_CHANNEL:-telegram}" case "$CHANNEL" in -telegram | discord) ;; +telegram | discord | slack) ;; *) - echo "OPENCLAW_NPM_ONBOARD_CHANNEL must be telegram or discord, got: $CHANNEL" >&2 + echo "OPENCLAW_NPM_ONBOARD_CHANNEL must be telegram, discord, or slack, got: $CHANNEL" >&2 exit 1 ;; esac @@ -69,10 +69,21 @@ case "$CHANNEL" in telegram) CHANNEL_TOKEN="123456:openclaw-npm-onboard-token" DEP_SENTINEL="grammy" + CHANNEL_ADD_ARGS=(--token "$CHANNEL_TOKEN") + CHANNEL_CONFIG_TOKENS=("$CHANNEL_TOKEN") ;; discord) CHANNEL_TOKEN="openclaw-npm-onboard-discord-token" DEP_SENTINEL="discord-api-types" + CHANNEL_ADD_ARGS=(--token "$CHANNEL_TOKEN") + CHANNEL_CONFIG_TOKENS=("$CHANNEL_TOKEN") + ;; + slack) + SLACK_BOT_TOKEN="xoxb-openclaw-npm-onboard-slack-token" + SLACK_APP_TOKEN="xapp-openclaw-npm-onboard-slack-token" + DEP_SENTINEL="@slack/bolt" + CHANNEL_ADD_ARGS=(--bot-token "$SLACK_BOT_TOKEN" --app-token "$SLACK_APP_TOKEN") + CHANNEL_CONFIG_TOKENS=("$SLACK_BOT_TOKEN" "$SLACK_APP_TOKEN") ;; *) echo "unsupported channel: $CHANNEL" >&2 @@ -138,8 +149,8 @@ node scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs configure-mock-mod openclaw_e2e_assert_dep_absent "$DEP_SENTINEL" "$HOME/.openclaw" echo "Configuring $CHANNEL..." -openclaw channels add --channel "$CHANNEL" --token "$CHANNEL_TOKEN" >/tmp/openclaw-channel-add.log 2>&1 -node scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs assert-channel-config "$CHANNEL" "$CHANNEL_TOKEN" +openclaw channels add --channel "$CHANNEL" "${CHANNEL_ADD_ARGS[@]}" >/tmp/openclaw-channel-add.log 2>&1 +node scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs assert-channel-config "$CHANNEL" "${CHANNEL_CONFIG_TOKENS[@]}" echo "Checking status surfaces for $CHANNEL..." openclaw channels status --json >/tmp/openclaw-channels-status.json 2>/tmp/openclaw-channels-status.err diff --git a/scripts/lib/docker-e2e-scenarios.mjs b/scripts/lib/docker-e2e-scenarios.mjs index 911e2d4922c..571fbcaa7e2 100644 --- a/scripts/lib/docker-e2e-scenarios.mjs +++ b/scripts/lib/docker-e2e-scenarios.mjs @@ -181,6 +181,11 @@ export const mainLanes = [ "OPENCLAW_NPM_ONBOARD_CHANNEL=discord OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent", { resources: ["service"], stateScenario: "empty", weight: 3 }, ), + npmLane( + "npm-onboard-slack-channel-agent", + "OPENCLAW_NPM_ONBOARD_CHANNEL=slack OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent", + { resources: ["service"], stateScenario: "empty", weight: 3 }, + ), serviceLane("gateway-network", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:gateway-network"), serviceLane( "agents-delete-shared-workspace", @@ -499,6 +504,11 @@ const releasePathPackageUpdateCoreLanes = [ "OPENCLAW_NPM_ONBOARD_CHANNEL=discord OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent", { resources: ["service"], stateScenario: "empty", weight: 3 }, ), + npmLane( + "npm-onboard-slack-channel-agent", + "OPENCLAW_NPM_ONBOARD_CHANNEL=slack OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:npm-onboard-channel-agent", + { resources: ["service"], stateScenario: "empty", weight: 3 }, + ), npmLane("doctor-switch", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:doctor-switch", { stateScenario: "empty", weight: 3, diff --git a/scripts/lib/plugin-prerelease-test-plan.mjs b/scripts/lib/plugin-prerelease-test-plan.mjs index 7ada7f13b18..19917eaf467 100644 --- a/scripts/lib/plugin-prerelease-test-plan.mjs +++ b/scripts/lib/plugin-prerelease-test-plan.mjs @@ -33,6 +33,10 @@ const pluginPrereleaseDockerLanes = Object.freeze([ "status-diagnostics", ], }, + { + lane: "npm-onboard-slack-channel-agent", + surfaces: ["package-artifact", "gateway-bootstrap", "status-diagnostics"], + }, { lane: "doctor-switch", surfaces: ["package-artifact", "doctor-fix"], diff --git a/test/scripts/docker-e2e-plan.test.ts b/test/scripts/docker-e2e-plan.test.ts index c5b5cabf24a..344ba23d73a 100644 --- a/test/scripts/docker-e2e-plan.test.ts +++ b/test/scripts/docker-e2e-plan.test.ts @@ -151,6 +151,7 @@ describe("scripts/lib/docker-e2e-plan", () => { expect(packageUpdateCore.lanes.map((lane) => lane.name)).toEqual([ "npm-onboard-channel-agent", "npm-onboard-discord-channel-agent", + "npm-onboard-slack-channel-agent", "doctor-switch", "update-channel-switch", "upgrade-survivor", @@ -166,6 +167,10 @@ describe("scripts/lib/docker-e2e-plan", () => { name: "npm-onboard-discord-channel-agent", stateScenario: "empty", }), + expect.objectContaining({ + name: "npm-onboard-slack-channel-agent", + stateScenario: "empty", + }), expect.objectContaining({ name: "doctor-switch", stateScenario: "empty", diff --git a/test/scripts/plugin-prerelease-test-plan.test.ts b/test/scripts/plugin-prerelease-test-plan.test.ts index c7172358bac..a09728ad343 100644 --- a/test/scripts/plugin-prerelease-test-plan.test.ts +++ b/test/scripts/plugin-prerelease-test-plan.test.ts @@ -37,6 +37,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => { expect(plan.dockerLanes).toEqual([ "npm-onboard-channel-agent", "npm-onboard-discord-channel-agent", + "npm-onboard-slack-channel-agent", "doctor-switch", "update-channel-switch", "plugins-offline",