惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
IT之家
IT之家
B
Blog
博客园_首页
博客园 - 司徒正美
有赞技术团队
有赞技术团队
博客园 - 聂微东
I
InfoQ
美团技术团队
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
H
Help Net Security
大猫的无限游戏
大猫的无限游戏
MyScale Blog
MyScale Blog
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
人人都是产品经理
人人都是产品经理
Microsoft Azure Blog
Microsoft Azure Blog
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
test(plugins): assert local install uninstall cleanup · o...
vincentkoc · 2026-05-03 · via Recent Commits to openclaw:main

@@ -46,6 +46,50 @@ function assertPluginRemoved(params) {

4646

}

4747

}

484849+

function rememberPluginInstallPath(params) {

50+

const record = getInstallRecords()[params.pluginId];

51+

if (!record) {

52+

throw new Error(`missing install record for ${params.pluginId}`);

53+

}

54+

if (params.source && record.source !== params.source) {

55+

throw new Error(`unexpected source for ${params.pluginId}: ${record.source}`);

56+

}

57+

if (params.sourcePath && record.sourcePath !== params.sourcePath) {

58+

throw new Error(

59+

`unexpected source path for ${params.pluginId}: ${record.sourcePath}, expected ${params.sourcePath}`,

60+

);

61+

}

62+

const installPath = record.installPath?.replace(/^~(?=$|\/)/u, process.env.HOME);

63+

if (!installPath || !fs.existsSync(installPath)) {

64+

throw new Error(`${params.pluginId} install path missing on disk: ${installPath}`);

65+

}

66+

fs.writeFileSync(params.installPathFile, installPath, "utf8");

67+

if (params.sourcePathFile && params.sourcePath) {

68+

fs.writeFileSync(params.sourcePathFile, params.sourcePath, "utf8");

69+

}

70+

return { installPath, record };

71+

}

72+73+

function assertManagedInstallRemoved(params) {

74+

const installPath = fs.readFileSync(params.installPathFile, "utf8").trim();

75+

const sourcePath =

76+

params.sourcePathFile && fs.existsSync(params.sourcePathFile)

77+

? fs.readFileSync(params.sourcePathFile, "utf8").trim()

78+

: "";

79+

assertPluginRemoved({

80+

pluginId: params.pluginId,

81+

listFile: params.listFile,

82+

});

83+

if (sourcePath && !fs.existsSync(sourcePath)) {

84+

throw new Error(`${params.pluginId} source path was deleted during uninstall: ${sourcePath}`);

85+

}

86+

if (installPath !== sourcePath && fs.existsSync(installPath)) {

87+

throw new Error(

88+

`${params.pluginId} managed install path still exists after uninstall: ${installPath}`,

89+

);

90+

}

91+

}

92+4993

function recordFixturePluginTrust() {

5094

const pluginId = process.argv[3];

5195

const pluginRoot = process.argv[4];

@@ -246,6 +290,54 @@ function assertMarketplaceRecords() {

246290

}

247291

}

248292293+

function assertPluginTgz() {

294+

assertSimplePlugin(

295+

"/tmp/plugins2.json",

296+

"/tmp/plugins2-inspect.json",

297+

"demo-plugin-tgz",

298+

"demo.tgz",

299+

);

300+

rememberPluginInstallPath({

301+

pluginId: "demo-plugin-tgz",

302+

installPathFile: "/tmp/plugins2-install-path.txt",

303+

source: "archive",

304+

});

305+

}

306+307+

function assertPluginTgzRemoved() {

308+

assertManagedInstallRemoved({

309+

pluginId: "demo-plugin-tgz",

310+

listFile: "/tmp/plugins2-uninstalled.json",

311+

installPathFile: "/tmp/plugins2-install-path.txt",

312+

});

313+

}

314+315+

function assertPluginDir() {

316+

const sourceDir = process.argv[3];

317+

assertSimplePlugin(

318+

"/tmp/plugins3.json",

319+

"/tmp/plugins3-inspect.json",

320+

"demo-plugin-dir",

321+

"demo.dir",

322+

);

323+

rememberPluginInstallPath({

324+

pluginId: "demo-plugin-dir",

325+

installPathFile: "/tmp/plugins3-install-path.txt",

326+

sourcePathFile: "/tmp/plugins3-source-path.txt",

327+

source: "path",

328+

sourcePath: sourceDir,

329+

});

330+

}

331+332+

function assertPluginDirRemoved() {

333+

assertManagedInstallRemoved({

334+

pluginId: "demo-plugin-dir",

335+

listFile: "/tmp/plugins3-uninstalled.json",

336+

installPathFile: "/tmp/plugins3-install-path.txt",

337+

sourcePathFile: "/tmp/plugins3-source-path.txt",

338+

});

339+

}

340+249341

function assertGitPlugin() {

250342

const repoUrl = process.argv[3];

251343

const gitRef = process.argv[4];

@@ -403,6 +495,22 @@ function assertPluginDirDeps() {

403495

throw new Error(`missing copied local plugin dependency: ${dependencyPackagePath}`);

404496

}

405497

assertRealPathInside(installPath, dependencyPackagePath, "local plugin copied dependency");

498+

rememberPluginInstallPath({

499+

pluginId: "demo-plugin-dir-deps",

500+

installPathFile: "/tmp/plugins-dir-deps-install-path.txt",

501+

sourcePathFile: "/tmp/plugins-dir-deps-source-path.txt",

502+

source: "path",

503+

sourcePath: sourceDir,

504+

});

505+

}

506+507+

function assertPluginDirDepsRemoved() {

508+

assertManagedInstallRemoved({

509+

pluginId: "demo-plugin-dir-deps",

510+

listFile: "/tmp/plugins-dir-deps-uninstalled.json",

511+

installPathFile: "/tmp/plugins-dir-deps-install-path.txt",

512+

sourcePathFile: "/tmp/plugins-dir-deps-source-path.txt",

513+

});

406514

}

407515408516

function assertLocalPathUpdateSkipped() {

@@ -463,6 +571,32 @@ function assertNpmPluginUpdateUnchanged() {

463571

assertNpmPlugin();

464572

}

465573574+

function assertPluginFile() {

575+

const sourceDir = process.argv[3];

576+

assertSimplePlugin(

577+

"/tmp/plugins4.json",

578+

"/tmp/plugins4-inspect.json",

579+

"demo-plugin-file",

580+

"demo.file",

581+

);

582+

rememberPluginInstallPath({

583+

pluginId: "demo-plugin-file",

584+

installPathFile: "/tmp/plugins4-install-path.txt",

585+

sourcePathFile: "/tmp/plugins4-source-path.txt",

586+

source: "path",

587+

sourcePath: sourceDir,

588+

});

589+

}

590+591+

function assertPluginFileRemoved() {

592+

assertManagedInstallRemoved({

593+

pluginId: "demo-plugin-file",

594+

listFile: "/tmp/plugins4-uninstalled.json",

595+

installPathFile: "/tmp/plugins4-install-path.txt",

596+

sourcePathFile: "/tmp/plugins4-source-path.txt",

597+

});

598+

}

599+466600

function assertNpmPluginRemoved() {

467601

const installPath = fs.readFileSync("/tmp/plugins-npm-install-path.txt", "utf8").trim();

468602

const dependencyPackagePath = fs

@@ -689,29 +823,15 @@ function assertClawHubUpdated() {

689823

const commands = {

690824

"record-fixture-plugin-trust": recordFixturePluginTrust,

691825

"demo-plugin": assertDemoPlugin,

692-

"plugin-tgz": () =>

693-

assertSimplePlugin(

694-

"/tmp/plugins2.json",

695-

"/tmp/plugins2-inspect.json",

696-

"demo-plugin-tgz",

697-

"demo.tgz",

698-

),

699-

"plugin-dir": () =>

700-

assertSimplePlugin(

701-

"/tmp/plugins3.json",

702-

"/tmp/plugins3-inspect.json",

703-

"demo-plugin-dir",

704-

"demo.dir",

705-

),

826+

"plugin-tgz": assertPluginTgz,

827+

"plugin-tgz-removed": assertPluginTgzRemoved,

828+

"plugin-dir": assertPluginDir,

829+

"plugin-dir-removed": assertPluginDirRemoved,

706830

"plugin-dir-update-skipped": assertLocalPathUpdateSkipped,

707831

"plugin-dir-deps": assertPluginDirDeps,

708-

"plugin-file": () =>

709-

assertSimplePlugin(

710-

"/tmp/plugins4.json",

711-

"/tmp/plugins4-inspect.json",

712-

"demo-plugin-file",

713-

"demo.file",

714-

),

832+

"plugin-dir-deps-removed": assertPluginDirDepsRemoved,

833+

"plugin-file": assertPluginFile,

834+

"plugin-file-removed": assertPluginFileRemoved,

715835

"plugin-npm": assertNpmPlugin,

716836

"plugin-npm-update": assertNpmPluginUpdateUnchanged,

717837

"plugin-npm-removed": assertNpmPluginRemoved,