test(package): cover stale source plugin shadows

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Josh Lehman
2026-05-04 21:24:04 +01:00
committed by Peter Steinberger
parent 112924b113
commit 0fc8afeac9
7 changed files with 62 additions and 3 deletions

View File

@@ -123,8 +123,8 @@ pnpm test:docker:published-upgrade-survivor
```
Available scenarios are `base`, `feishu-channel`, `bootstrap-persona`,
`plugin-deps-cleanup`, `configured-plugin-installs`, `tilde-log-path`, and
`versioned-runtime-deps`. In aggregate runs,
`plugin-deps-cleanup`, `configured-plugin-installs`,
`stale-source-plugin-shadow`, `tilde-log-path`, and `versioned-runtime-deps`. In aggregate runs,
`OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues` expands to all reported
issue-shaped scenarios, including the configured-plugin install migration.

View File

@@ -44,7 +44,7 @@ title: "Tests"
- `pnpm test:docker:openwebui`: Starts Dockerized OpenClaw + Open WebUI, signs in through Open WebUI, checks `/api/models`, then runs a real proxied chat through `/api/chat/completions`. Requires a usable live model key (for example OpenAI in `~/.profile`), pulls an external Open WebUI image, and is not expected to be CI-stable like the normal unit/e2e suites.
- `pnpm test:docker:mcp-channels`: Starts a seeded Gateway container and a second client container that spawns `openclaw mcp serve`, then verifies routed conversation discovery, transcript reads, attachment metadata, live event queue behavior, outbound send routing, and Claude-style channel + permission notifications over the real stdio bridge. The Claude notification assertion reads the raw stdio MCP frames directly so the smoke reflects what the bridge actually emits.
- `pnpm test:docker:upgrade-survivor`: Installs the packed OpenClaw tarball over a dirty old-user fixture, runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks that agents, channel config, plugin allowlists, workspace/session files, stale legacy plugin dependency state, startup, and RPC status survive.
- `pnpm test:docker:published-upgrade-survivor`: Installs `openclaw@latest` by default, seeds realistic existing-user files without live provider or channel keys, configures that baseline with a baked `openclaw config set` command recipe, updates that published install to the packed OpenClaw tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks that configured intents, workspace/session files, stale plugin config and legacy dependency state, startup, `/healthz`, `/readyz`, and RPC status survive or repair cleanly. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, expand an exact matrix with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS` such as `all-since-2026.4.23`, or add scenario fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues`; the reported-issues set includes `configured-plugin-installs` to verify configured external OpenClaw plugins install automatically during upgrade. Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
- `pnpm test:docker:published-upgrade-survivor`: Installs `openclaw@latest` by default, seeds realistic existing-user files without live provider or channel keys, configures that baseline with a baked `openclaw config set` command recipe, updates that published install to the packed OpenClaw tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks that configured intents, workspace/session files, stale plugin config and legacy dependency state, startup, `/healthz`, `/readyz`, and RPC status survive or repair cleanly. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, expand an exact matrix with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS` such as `all-since-2026.4.23`, or add scenario fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues`; the reported-issues set includes `configured-plugin-installs` to verify configured external OpenClaw plugins install automatically during upgrade and `stale-source-plugin-shadow` to keep source-only plugin shadows from breaking startup. Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
- `pnpm test:docker:update-migration`: Runs the published-upgrade survivor harness in the cleanup-heavy `plugin-deps-cleanup` scenario, starting at `openclaw@2026.4.23` by default. The separate `Update Migration` workflow expands this lane with `baselines=all-since-2026.4.23` so every stable published package from `.23` onward updates to the candidate and proves configured-plugin dependency cleanup outside Full Release CI.
- `pnpm test:docker:plugins`: Runs install/update smoke for local path, `file:`, npm registry packages with hoisted dependencies, git moving refs, ClawHub fixtures, marketplace updates, and Claude-bundle enable/inspect.

View File

@@ -8,6 +8,7 @@ const SCENARIOS = new Set([
"bootstrap-persona",
"plugin-deps-cleanup",
"configured-plugin-installs",
"stale-source-plugin-shadow",
"tilde-log-path",
"versioned-runtime-deps",
]);
@@ -355,6 +356,13 @@ function assertStateSurvived() {
assert(actual === contents, `${fileName} was changed during update/doctor`);
}
}
if (scenario === "stale-source-plugin-shadow") {
const staleRoot = path.join(stateDir, "extensions", "opik-openclaw");
assert(
fs.existsSync(path.join(staleRoot, "src", "index.ts")),
"source-only plugin shadow fixture missing",
);
}
if (scenario === "versioned-runtime-deps") {
if (stage === "baseline") {
return;

View File

@@ -286,6 +286,47 @@ configured_plugin_installs_enabled() {
[ "$SCENARIO" = "configured-plugin-installs" ]
}
source_only_plugin_shadow_enabled() {
[ "$SCENARIO" = "stale-source-plugin-shadow" ]
}
seed_source_only_plugin_shadow() {
source_only_plugin_shadow_enabled || return 0
local shadow_root="$OPENCLAW_STATE_DIR/extensions/opik-openclaw"
mkdir -p "$shadow_root/src"
cat >"$shadow_root/package.json" <<'JSON'
{
"name": "@opik/opik-openclaw",
"version": "0.0.0-upgrade-survivor",
"openclaw": {
"extensions": ["./src/index.ts"]
}
}
JSON
cat >"$shadow_root/openclaw.plugin.json" <<'JSON'
{
"id": "opik-openclaw",
"activation": {
"onStartup": false
},
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
JSON
cat >"$shadow_root/src/index.ts" <<'TS'
export default {
id: "opik-openclaw",
name: "Source-only Opik shadow",
register() {},
};
TS
echo "Seeded source-only plugin shadow: $shadow_root"
}
configure_configured_plugin_install_fixture_registry() {
configured_plugin_installs_enabled || return 0
@@ -785,6 +826,7 @@ phase validate-baseline-config validate_baseline_config
phase install-baseline-plugin-dependencies install_baseline_plugin_dependencies
phase seed-legacy-plugin-dependency-debris seed_legacy_plugin_dependency_debris
phase assert-legacy-plugin-dependency-debris assert_legacy_plugin_dependency_debris_present
phase seed-source-only-plugin-shadow seed_source_only_plugin_shadow
phase assert-baseline assert_baseline_state
phase seed-legacy-runtime-deps-symlink seed_legacy_runtime_deps_symlink
phase resolve-candidate resolve_candidate_version

View File

@@ -75,6 +75,7 @@ const UPGRADE_SURVIVOR_SCENARIOS = [
"bootstrap-persona",
"plugin-deps-cleanup",
"configured-plugin-installs",
"stale-source-plugin-shadow",
"tilde-log-path",
"versioned-runtime-deps",
];

View File

@@ -382,6 +382,7 @@ describe("scripts/lib/docker-e2e-plan", () => {
"published-upgrade-survivor-2026.4.29-bootstrap-persona",
"published-upgrade-survivor-2026.4.29-plugin-deps-cleanup",
"published-upgrade-survivor-2026.4.29-configured-plugin-installs",
"published-upgrade-survivor-2026.4.29-stale-source-plugin-shadow",
"published-upgrade-survivor-2026.4.29-tilde-log-path",
"published-upgrade-survivor-2026.4.29-versioned-runtime-deps",
]);
@@ -400,12 +401,14 @@ describe("scripts/lib/docker-e2e-plan", () => {
"published-upgrade-survivor-2026.4.29-bootstrap-persona",
"published-upgrade-survivor-2026.4.29-plugin-deps-cleanup",
"published-upgrade-survivor-2026.4.29-configured-plugin-installs",
"published-upgrade-survivor-2026.4.29-stale-source-plugin-shadow",
"published-upgrade-survivor-2026.4.29-tilde-log-path",
"published-upgrade-survivor-2026.4.29-versioned-runtime-deps",
"published-upgrade-survivor-2026.3.13",
"published-upgrade-survivor-2026.3.13-feishu-channel",
"published-upgrade-survivor-2026.3.13-bootstrap-persona",
"published-upgrade-survivor-2026.3.13-configured-plugin-installs",
"published-upgrade-survivor-2026.3.13-stale-source-plugin-shadow",
"published-upgrade-survivor-2026.3.13-tilde-log-path",
"published-upgrade-survivor-2026.3.13-versioned-runtime-deps",
]);

View File

@@ -253,6 +253,11 @@ describe("package artifact reuse", () => {
expect(publishedUpgradeSurvivor).toContain(
"assert_legacy_plugin_dependency_debris_before_doctor",
);
expect(publishedUpgradeSurvivor.indexOf("phase seed-source-only-plugin-shadow")).toBeLessThan(
publishedUpgradeSurvivor.indexOf("phase assert-baseline"),
);
expect(publishedUpgradeSurvivor).toContain('"id": "opik-openclaw"');
expect(publishedUpgradeSurvivor).toContain('"configSchema": {');
expect(publishedUpgradeSurvivor).toContain(
"Legacy plugin dependency debris was already removed before doctor",
);