|
6 | 6 | mockExpressApp, |
7 | 7 | mockRemoteTunnelControllerMiddleware, |
8 | 8 | mockRemoteTunnelControllerInstance, |
| 9 | + mockGetPort, |
9 | 10 | } = vi.hoisted(() => { |
10 | 11 | const httpServer = { |
11 | 12 | close: vi.fn((cb: any) => cb?.()), |
@@ -36,11 +37,29 @@ const { |
36 | 37 | isActive: vi.fn().mockReturnValue(false), |
37 | 38 | }; |
38 | 39 |
|
| 40 | + const mockGetPort = vi.fn( |
| 41 | + async (opts?: { port?: number | Iterable<number>; host?: string }) => { |
| 42 | + if (opts?.port == null) return 8000; |
| 43 | + if (typeof opts.port === "number") return opts.port; |
| 44 | + for (const p of opts.port) return p; |
| 45 | + return 8000; |
| 46 | + }, |
| 47 | + ); |
| 48 | + |
39 | 49 | return { |
40 | 50 | mockHttpServer: httpServer, |
41 | 51 | mockExpressApp: expressApp, |
42 | 52 | mockRemoteTunnelControllerMiddleware: remoteTunnelControllerMiddleware, |
43 | 53 | mockRemoteTunnelControllerInstance: remoteTunnelControllerInstance, |
| 54 | + mockGetPort, |
| 55 | + }; |
| 56 | +}); |
| 57 | + |
| 58 | +vi.mock("get-port", async (importOriginal) => { |
| 59 | + const actual = await importOriginal<typeof import("get-port")>(); |
| 60 | + return { |
| 61 | + ...actual, |
| 62 | + default: mockGetPort, |
44 | 63 | }; |
45 | 64 | }); |
46 | 65 |
|
@@ -247,6 +266,100 @@ describe("ServerPlugin", () => { |
247 | 266 | ); |
248 | 267 | }); |
249 | 268 |
|
| 269 | + test("uses get-port portNumbers in development when default preferred", async () => { |
| 270 | + process.env.NODE_ENV = "development"; |
| 271 | + mockGetPort.mockResolvedValueOnce(8123); |
| 272 | + const plugin = new ServerPlugin({}); |
| 273 | + |
| 274 | + await plugin.start(); |
| 275 | + |
| 276 | + expect(mockGetPort).toHaveBeenCalledWith( |
| 277 | + expect.objectContaining({ |
| 278 | + host: ServerPlugin.DEFAULT_CONFIG.host, |
| 279 | + }), |
| 280 | + ); |
| 281 | + const opts = mockGetPort.mock.calls[0][0] as { |
| 282 | + port: Iterable<number>; |
| 283 | + }; |
| 284 | + expect([...opts.port].slice(0, 2)).toEqual([ |
| 285 | + ServerPlugin.DEFAULT_CONFIG.port, |
| 286 | + ServerPlugin.DEFAULT_CONFIG.port + 1, |
| 287 | + ]); |
| 288 | + expect(mockExpressApp.listen).toHaveBeenCalledWith( |
| 289 | + 8123, |
| 290 | + expect.any(String), |
| 291 | + expect.any(Function), |
| 292 | + ); |
| 293 | + }); |
| 294 | + |
| 295 | + test("uses get-port portNumbers in development when explicit port preferred", async () => { |
| 296 | + process.env.NODE_ENV = "development"; |
| 297 | + mockGetPort.mockResolvedValueOnce(9123); |
| 298 | + const plugin = new ServerPlugin({ port: 4000 }); |
| 299 | + |
| 300 | + await plugin.start(); |
| 301 | + |
| 302 | + expect(mockGetPort).toHaveBeenCalledWith( |
| 303 | + expect.objectContaining({ |
| 304 | + host: ServerPlugin.DEFAULT_CONFIG.host, |
| 305 | + }), |
| 306 | + ); |
| 307 | + const opts = mockGetPort.mock.calls[0][0] as { |
| 308 | + port: Iterable<number>; |
| 309 | + }; |
| 310 | + expect([...opts.port].slice(0, 2)).toEqual([4000, 4001]); |
| 311 | + expect(mockExpressApp.listen).toHaveBeenCalledWith( |
| 312 | + 9123, |
| 313 | + expect.any(String), |
| 314 | + expect.any(Function), |
| 315 | + ); |
| 316 | + }); |
| 317 | + |
| 318 | + test("does not use get-port outside development", async () => { |
| 319 | + process.env.NODE_ENV = "production"; |
| 320 | + mockGetPort.mockClear(); |
| 321 | + const plugin = new ServerPlugin({ port: 3000 }); |
| 322 | + |
| 323 | + await plugin.start(); |
| 324 | + |
| 325 | + expect(mockGetPort).not.toHaveBeenCalled(); |
| 326 | + expect(mockExpressApp.listen).toHaveBeenCalledWith( |
| 327 | + 3000, |
| 328 | + expect.any(String), |
| 329 | + expect.any(Function), |
| 330 | + ); |
| 331 | + }); |
| 332 | + |
| 333 | + test("logs info when dev preferred port was busy and another was picked", async () => { |
| 334 | + process.env.NODE_ENV = "development"; |
| 335 | + mockLoggerInfo.mockClear(); |
| 336 | + mockGetPort.mockResolvedValueOnce(8123); |
| 337 | + const plugin = new ServerPlugin({}); |
| 338 | + |
| 339 | + await plugin.start(); |
| 340 | + |
| 341 | + expect(mockLoggerInfo).toHaveBeenCalledWith( |
| 342 | + "Port %d was busy, picking %d", |
| 343 | + ServerPlugin.DEFAULT_CONFIG.port, |
| 344 | + 8123, |
| 345 | + ); |
| 346 | + }); |
| 347 | + |
| 348 | + test("does not log busy info when dev preferred port was free", async () => { |
| 349 | + process.env.NODE_ENV = "development"; |
| 350 | + mockLoggerInfo.mockClear(); |
| 351 | + mockGetPort.mockResolvedValueOnce(ServerPlugin.DEFAULT_CONFIG.port); |
| 352 | + const plugin = new ServerPlugin({}); |
| 353 | + |
| 354 | + await plugin.start(); |
| 355 | + |
| 356 | + expect(mockLoggerInfo).not.toHaveBeenCalledWith( |
| 357 | + "Port %d was busy, picking %d", |
| 358 | + expect.any(Number), |
| 359 | + expect.any(Number), |
| 360 | + ); |
| 361 | + }); |
| 362 | + |
250 | 363 | test("should setup ViteDevServer in development mode", async () => { |
251 | 364 | process.env.NODE_ENV = "development"; |
252 | 365 | const plugin = new ServerPlugin({}); |
|
0 commit comments