startup-preview-smoke-test.js 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. 'use strict';
  2. const assert = require('assert/strict');
  3. const fs = require('fs');
  4. const os = require('os');
  5. const path = require('path');
  6. const { spawnSync } = require('child_process');
  7. const { resolveWorkspaceRoot, workspaceIdentity } = require('../mcp/src/core/runtime-context');
  8. const { buildStartupSummary, startupAssistantMessage } = require('../mcp/src/core/startup-summary');
  9. const ROOT = path.resolve(__dirname, '..');
  10. function testRuntimeContext(tempRoot) {
  11. const installedPackage = path.join(tempRoot, '.claude', 'plugins', 'qiwei-assistant');
  12. fs.mkdirSync(installedPackage, { recursive: true });
  13. assert.equal(resolveWorkspaceRoot({ packageRoot: installedPackage, cwd: installedPackage }), tempRoot);
  14. assert.equal(resolveWorkspaceRoot({ packageRoot: ROOT, workspaceRoot: tempRoot }), tempRoot);
  15. assert.match(workspaceIdentity(tempRoot), /^[0-9a-f]{12}$/);
  16. }
  17. function testStartupSummary() {
  18. const offline = buildStartupSummary(
  19. { summary: { authConfigured: true, subscribed: true } },
  20. { data: { account: { configured: true, online: false }, listener: { running: false }, config: { allowedSenderCount: 1 } } },
  21. );
  22. assert.equal(offline.ready, false);
  23. assert.equal(offline.nextAction, '在工作台点击“恢复登录”');
  24. assert.match(startupAssistantMessage(offline), /企微账号待处理/);
  25. const ready = buildStartupSummary(
  26. { summary: { authConfigured: true, subscribed: true } },
  27. { data: { account: { configured: true, online: true }, listener: { running: true }, config: { allowedSenderCount: 1 } } },
  28. );
  29. assert.equal(ready.ready, true);
  30. assert.match(ready.nextAction, /可以进入智能会话/);
  31. }
  32. function testWorkspaceInstall(tempRoot) {
  33. const target = path.join(tempRoot, 'customer-project');
  34. fs.mkdirSync(target, { recursive: true });
  35. const result = spawnSync(process.execPath, [path.join(ROOT, 'install.js'), 'workspace', target, '--skip-install'], {
  36. cwd: ROOT,
  37. encoding: 'utf8',
  38. windowsHide: true,
  39. });
  40. assert.equal(result.status, 0, result.stderr || result.stdout);
  41. const mcp = JSON.parse(fs.readFileSync(path.join(target, '.mcp.json'), 'utf8'));
  42. const config = mcp.mcpServers?.['qiwei-assistant'];
  43. assert.equal(config.cwd, target);
  44. assert.equal(config.env?.QIWEI_WORKSPACE_ROOT, target);
  45. assert.equal(config.env?.QIWEI_OUTPUTS_DIR, path.join(target, 'outputs'));
  46. assert.equal(config.env?.CLAUDE_CODE_WORKDIR, target);
  47. assert(fs.existsSync(path.join(target, '.claude', 'plugins', 'qiwei-assistant', 'scripts', 'preview-dashboard.js')));
  48. assert(!fs.existsSync(path.join(target, '.claude', 'plugins', 'qiwei-assistant', '.env.local')));
  49. }
  50. function testRuntimeProcessSeparation() {
  51. const previewSource = fs.readFileSync(path.join(ROOT, 'scripts', 'preview-dashboard.js'), 'utf8');
  52. const dashboardSource = fs.readFileSync(path.join(ROOT, 'scripts', 'start-dashboard.js'), 'utf8');
  53. const serverSource = fs.readFileSync(path.join(ROOT, 'mcp', 'src', 'server.js'), 'utf8');
  54. assert.doesNotMatch(previewSource, /\.startRuntime\s*\(/);
  55. assert.match(previewSource, /ensureRelayDaemon\s*\(/);
  56. assert.match(dashboardSource, /ensureRelayDaemon\s*\(/);
  57. assert.match(serverSource, /ensureRelayDaemon\s*\(/);
  58. }
  59. function testAgentWorkbenchOfflineStartup() {
  60. const appSource = fs.readFileSync(path.join(ROOT, 'mcp', 'src', 'dashboard', 'app.js'), 'utf8');
  61. const renderAgentSource = appSource.slice(
  62. appSource.indexOf('function renderAgentPage()'),
  63. appSource.indexOf('// ==================== Login ===================='),
  64. );
  65. assert.match(renderAgentSource, /startAgentPageRuntime\(page\);/);
  66. assert.doesNotMatch(renderAgentSource, /if \(!state\.bootstrapping\)\s+startAgentPageRuntime\(page\)/);
  67. assert.match(appSource, /api\('GET', '\/api\/status\?fast=true', undefined, 2500\)/);
  68. assert.match(appSource, /api\('GET', '\/api\/agent\/status', undefined, 2500\)/);
  69. }
  70. function main() {
  71. const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-preview-smoke-'));
  72. try {
  73. testRuntimeContext(tempRoot);
  74. testStartupSummary();
  75. testWorkspaceInstall(tempRoot);
  76. testRuntimeProcessSeparation();
  77. testAgentWorkbenchOfflineStartup();
  78. } finally {
  79. fs.rmSync(tempRoot, { recursive: true, force: true });
  80. }
  81. process.stdout.write(`${JSON.stringify({ status: 'ok', checks: 5 }, null, 2)}\n`);
  82. }
  83. try { main(); }
  84. catch (error) {
  85. process.stderr.write(`${error.message}\n`);
  86. process.exit(1);
  87. }