test-webserver-port-retry.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env node
  2. import assert from 'assert';
  3. import fs from 'fs';
  4. import net from 'net';
  5. import os from 'os';
  6. import path from 'path';
  7. import { WebServer } from './src/server/server.js';
  8. import { findFreePort } from './src/server/helpers.js';
  9. function listenOnPort(port) {
  10. return new Promise((resolve, reject) => {
  11. const server = net.createServer();
  12. server.once('error', reject);
  13. server.listen({ port, exclusive: true }, () => resolve(server));
  14. });
  15. }
  16. function closeNetServer(server) {
  17. return new Promise((resolve) => server.close(() => resolve()));
  18. }
  19. function createStubProjectContext(workDir) {
  20. return {
  21. workDir,
  22. projectMeta: null,
  23. async load() {},
  24. async scan() {},
  25. isVLProject() { return false; },
  26. getSummary() {
  27. return { projectName: null, totalFiles: 0, breakdown: '', version: null };
  28. },
  29. getAllFiles() { return []; },
  30. getProjectMeta() { return null; },
  31. };
  32. }
  33. async function main() {
  34. const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vlcode-port-retry-'));
  35. const occupiedPort = await findFreePort(4070, 4098);
  36. const blocker = await listenOnPort(occupiedPort);
  37. const contextManager = {
  38. messages: [],
  39. turnCount: 0,
  40. compressionCount: 0,
  41. totalTokensSaved: 0,
  42. totalInputTokens: 0,
  43. totalOutputTokens: 0,
  44. estimateTokens() {},
  45. };
  46. const server = new WebServer({
  47. config: {
  48. workDir,
  49. noWorkspace: true,
  50. emptyWorkspaceDir: workDir,
  51. model: 'test-model',
  52. port: occupiedPort,
  53. _undoStack: [],
  54. },
  55. orchestrator: null,
  56. projectContext: createStubProjectContext(workDir),
  57. contextManager,
  58. toolRegistry: {
  59. clear() {},
  60. getToolSchemas() { return []; },
  61. async execute() { return {}; },
  62. },
  63. symbolIndex: {
  64. async build() {},
  65. getStats() { return { totalSymbols: 0, totalReferences: 0 }; },
  66. },
  67. impactAnalyzer: null,
  68. autoFix: null,
  69. localWorkspace: {},
  70. blueprintContext: { async load() {} },
  71. });
  72. try {
  73. const boundPort = await server.start(occupiedPort);
  74. assert.strictEqual(boundPort, occupiedPort + 1, 'WebServer.start should retry the next port after EADDRINUSE');
  75. assert.strictEqual(server._serverPort, occupiedPort + 1, 'server state should track the retried port');
  76. console.log('\n── WebServer Port Retry Regression ──');
  77. console.log('PASS test-webserver-port-retry.js');
  78. } finally {
  79. await server.stop().catch(() => {});
  80. await closeNetServer(blocker).catch(() => {});
  81. fs.rmSync(workDir, { recursive: true, force: true });
  82. }
  83. }
  84. main().catch((err) => {
  85. console.error(err.stack || err.message);
  86. process.exit(1);
  87. });