mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:00:44 +00:00
test(channels): cover staged setup entry dependency loading
This commit is contained in:
14
extensions/whatsapp/setup-entry.test.ts
Normal file
14
extensions/whatsapp/setup-entry.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("@whiskeysockets/baileys", () => {
|
||||
throw new Error("setup plugin load must not load Baileys");
|
||||
});
|
||||
|
||||
describe("whatsapp setup entry", () => {
|
||||
it("loads the setup plugin without installing or importing runtime dependencies", async () => {
|
||||
const { default: setupEntry } = await import("./setup-entry.js");
|
||||
|
||||
expect(setupEntry.kind).toBe("bundled-channel-setup-entry");
|
||||
expect(setupEntry.loadSetupPlugin({ installRuntimeDeps: false }).id).toBe("whatsapp");
|
||||
});
|
||||
});
|
||||
@@ -585,8 +585,10 @@ export OPENCLAW_NO_ONBOARD=1
|
||||
export OPENCLAW_PLUGIN_STAGE_DIR="$HOME/.openclaw/plugin-runtime-deps"
|
||||
mkdir -p "$OPENCLAW_PLUGIN_STAGE_DIR"
|
||||
|
||||
CHANNEL="feishu"
|
||||
DEP_SENTINEL="@larksuiteoapi/node-sdk"
|
||||
declare -A SETUP_ENTRY_DEP_SENTINELS=(
|
||||
[feishu]="@larksuiteoapi/node-sdk"
|
||||
[whatsapp]="@whiskeysockets/baileys"
|
||||
)
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
@@ -597,18 +599,21 @@ package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TG
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-setup-entry-install.log 2>&1
|
||||
|
||||
root="$(package_root)"
|
||||
test -d "$root/dist/extensions/$CHANNEL"
|
||||
if [ -d "$root/dist/extensions/$CHANNEL/node_modules" ]; then
|
||||
echo "$CHANNEL runtime deps should not be preinstalled in package" >&2
|
||||
find "$root/dist/extensions/$CHANNEL/node_modules" -maxdepth 3 -type f | head -40 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
if [ -f "$root/node_modules/$DEP_SENTINEL/package.json" ]; then
|
||||
echo "$DEP_SENTINEL should not be installed at package root before setup-entry load" >&2
|
||||
exit 1
|
||||
fi
|
||||
for channel in "${!SETUP_ENTRY_DEP_SENTINELS[@]}"; do
|
||||
dep_sentinel="${SETUP_ENTRY_DEP_SENTINELS[$channel]}"
|
||||
test -d "$root/dist/extensions/$channel"
|
||||
if [ -d "$root/dist/extensions/$channel/node_modules" ]; then
|
||||
echo "$channel runtime deps should not be preinstalled in package" >&2
|
||||
find "$root/dist/extensions/$channel/node_modules" -maxdepth 3 -type f | head -40 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
if [ -f "$root/node_modules/$dep_sentinel/package.json" ]; then
|
||||
echo "$dep_sentinel should not be installed at package root before setup-entry load" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Probing real Feishu bundled setup entry before channel configuration..."
|
||||
echo "Probing real bundled setup entries before channel configuration..."
|
||||
(
|
||||
cd "$root"
|
||||
node --input-type=module - <<'NODE'
|
||||
@@ -633,22 +638,33 @@ const setupPluginLoader = Object.values(bundled).find(
|
||||
if (!setupPluginLoader) {
|
||||
throw new Error("missing packaged getBundledChannelSetupPlugin export");
|
||||
}
|
||||
const plugin = setupPluginLoader("feishu");
|
||||
console.log(plugin ? "Feishu setup plugin loaded pre-config" : "Feishu setup plugin deferred pre-config");
|
||||
for (const channel of ["feishu", "whatsapp"]) {
|
||||
const plugin = setupPluginLoader(channel);
|
||||
if (!plugin) {
|
||||
throw new Error(`${channel} setup plugin did not load pre-config`);
|
||||
}
|
||||
if (plugin.id !== channel) {
|
||||
throw new Error(`${channel} setup plugin id mismatch: ${plugin.id}`);
|
||||
}
|
||||
console.log(`${channel} setup plugin loaded pre-config`);
|
||||
}
|
||||
NODE
|
||||
)
|
||||
|
||||
if [ -e "$root/dist/extensions/$CHANNEL/node_modules/$DEP_SENTINEL/package.json" ]; then
|
||||
echo "setup-entry discovery installed deps into bundled plugin tree before channel configuration" >&2
|
||||
exit 1
|
||||
fi
|
||||
if find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$DEP_SENTINEL/package.json" -type f | grep -q .; then
|
||||
echo "setup-entry discovery installed external staged deps before channel configuration" >&2
|
||||
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -160 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
for channel in "${!SETUP_ENTRY_DEP_SENTINELS[@]}"; do
|
||||
dep_sentinel="${SETUP_ENTRY_DEP_SENTINELS[$channel]}"
|
||||
if [ -e "$root/dist/extensions/$channel/node_modules/$dep_sentinel/package.json" ]; then
|
||||
echo "setup-entry discovery installed $channel deps into bundled plugin tree before channel configuration" >&2
|
||||
exit 1
|
||||
fi
|
||||
if find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$dep_sentinel/package.json" -type f | grep -q .; then
|
||||
echo "setup-entry discovery installed $channel external staged deps before channel configuration" >&2
|
||||
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -160 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Configuring Feishu; doctor should now install bundled runtime deps externally..."
|
||||
echo "Configuring setup-entry channels; doctor should now install bundled runtime deps externally..."
|
||||
node - <<'NODE'
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
@@ -669,6 +685,10 @@ config.channels = {
|
||||
...(config.channels?.feishu || {}),
|
||||
enabled: true,
|
||||
},
|
||||
whatsapp: {
|
||||
...(config.channels?.whatsapp || {}),
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
@@ -676,16 +696,19 @@ NODE
|
||||
|
||||
openclaw doctor --non-interactive >/tmp/openclaw-setup-entry-doctor.log 2>&1
|
||||
|
||||
if [ -e "$root/dist/extensions/$CHANNEL/node_modules/$DEP_SENTINEL/package.json" ]; then
|
||||
echo "expected configured Feishu deps to be installed externally, not into bundled plugin tree" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$DEP_SENTINEL/package.json" -type f | grep -q .; then
|
||||
echo "missing external staged dependency sentinel for configured $CHANNEL: $DEP_SENTINEL" >&2
|
||||
cat /tmp/openclaw-setup-entry-doctor.log >&2
|
||||
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -160 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
for channel in "${!SETUP_ENTRY_DEP_SENTINELS[@]}"; do
|
||||
dep_sentinel="${SETUP_ENTRY_DEP_SENTINELS[$channel]}"
|
||||
if [ -e "$root/dist/extensions/$channel/node_modules/$dep_sentinel/package.json" ]; then
|
||||
echo "expected configured $channel deps to be installed externally, not into bundled plugin tree" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -path "*/node_modules/$dep_sentinel/package.json" -type f | grep -q .; then
|
||||
echo "missing external staged dependency sentinel for configured $channel: $dep_sentinel" >&2
|
||||
cat /tmp/openclaw-setup-entry-doctor.log >&2
|
||||
find "$OPENCLAW_PLUGIN_STAGE_DIR" -maxdepth 12 -type f | sort | head -160 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "bundled channel setup-entry runtime deps Docker E2E passed"
|
||||
EOF
|
||||
|
||||
@@ -763,6 +763,34 @@ describe("bundled channel entry shape guards", () => {
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps staged runtime-dependency setup entries on setup-only plugin barrels", () => {
|
||||
const offenders: string[] = [];
|
||||
|
||||
for (const extensionDir of bundledPluginRoots) {
|
||||
const setupEntryPath = path.join(extensionDir, "setup-entry.ts");
|
||||
const packageJsonPath = path.join(extensionDir, "package.json");
|
||||
if (!fs.existsSync(setupEntryPath) || !fs.existsSync(packageJsonPath)) {
|
||||
continue;
|
||||
}
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as {
|
||||
openclaw?: {
|
||||
bundle?: {
|
||||
stageRuntimeDependencies?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (packageJson.openclaw?.bundle?.stageRuntimeDependencies !== true) {
|
||||
continue;
|
||||
}
|
||||
const setupEntrySource = fs.readFileSync(setupEntryPath, "utf8");
|
||||
if (/specifier:\s*["']\.\/(?:api|channel-plugin-api)\.js["']/u.test(setupEntrySource)) {
|
||||
offenders.push(path.relative(process.cwd(), setupEntryPath));
|
||||
}
|
||||
}
|
||||
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps bundled channel entrypoints free of static src imports", () => {
|
||||
const offenders = collectBundledChannelEntrypointOffenders(bundledPluginRoots, (source) =>
|
||||
/^(?:import|export)\s.+["']\.\/src\//mu.test(source),
|
||||
|
||||
Reference in New Issue
Block a user