@@ -574,9 +574,11 @@ describe("resolveGatewayReloadSettings", () => {
574574type WatcherHandler = ( ) => void ;
575575type WatcherEvent = "add" | "change" | "unlink" | "error" ;
576576
577- function createWatcherMock ( ) {
577+ function createWatcherMock ( effectiveUsePolling ?: boolean ) {
578578 const handlers = new Map < WatcherEvent , WatcherHandler [ ] > ( ) ;
579579 return {
580+ effectiveUsePolling,
581+ options : { usePolling : false } ,
580582 on ( event : WatcherEvent , handler : WatcherHandler ) {
581583 const existing = handlers . get ( event ) ?? [ ] ;
582584 existing . push ( handler ) ;
@@ -1602,9 +1604,15 @@ describe("startGatewayConfigReloader watcher error recovery", () => {
16021604
16031605 function startReloaderWithWatchers ( watchers : ReturnType < typeof createWatcherMock > [ ] ) {
16041606 const watchSpy = vi . spyOn ( chokidar , "watch" ) ;
1605- for ( const watcher of watchers ) {
1606- watchSpy . mockReturnValueOnce ( watcher as unknown as never ) ;
1607- }
1607+ let watcherIndex = 0 ;
1608+ watchSpy . mockImplementation ( ( _path , options ) => {
1609+ const watcher = watchers [ watcherIndex ++ ] ;
1610+ if ( ! watcher ) {
1611+ throw new Error ( "missing watcher mock" ) ;
1612+ }
1613+ watcher . options . usePolling = watcher . effectiveUsePolling ?? Boolean ( options ?. usePolling ) ;
1614+ return watcher as unknown as never ;
1615+ } ) ;
16081616 const log = { info : vi . fn ( ) , warn : vi . fn ( ) , error : vi . fn ( ) } ;
16091617 const reloader = startGatewayConfigReloader ( {
16101618 initialConfig : { gateway : { reload : { debounceMs : 0 } } } ,
@@ -1676,9 +1684,7 @@ describe("startGatewayConfigReloader watcher error recovery", () => {
16761684 // Fourth native error triggers degradation to polling mode (not disabled).
16771685 watchers [ 3 ] ?. emit ( "error" ) ;
16781686 expect ( reloader . hotReloadStatus ( ) ) . toBe ( "active" ) ;
1679- expect ( log . warn ) . toHaveBeenCalledWith (
1680- expect . stringContaining ( "degrading to polling mode" ) ,
1681- ) ;
1687+ expect ( log . warn ) . toHaveBeenCalledWith ( expect . stringContaining ( "degrading to polling mode" ) ) ;
16821688 await vi . advanceTimersByTimeAsync ( 500 ) ;
16831689 expect ( watchSpy ) . toHaveBeenCalledTimes ( 5 ) ;
16841690 expect ( watchOptions ( 4 ) ?. usePolling ) . toBe ( true ) ;
@@ -1772,6 +1778,45 @@ describe("startGatewayConfigReloader watcher error recovery", () => {
17721778 }
17731779 } ) ;
17741780
1781+ it ( "uses chokidar's effective polling mode when the platform forces it on" , async ( ) => {
1782+ const originalVitest = process . env . VITEST ;
1783+ const originalChokidarPolling = process . env . CHOKIDAR_USEPOLLING ;
1784+ delete process . env . VITEST ;
1785+ delete process . env . CHOKIDAR_USEPOLLING ;
1786+ let reloader : { stop : ( ) => Promise < void > ; hotReloadStatus : ( ) => string } | undefined ;
1787+ try {
1788+ const watchers = Array . from ( { length : 4 } , ( ) => createWatcherMock ( true ) ) ;
1789+ const started = startReloaderWithWatchers ( watchers ) ;
1790+ const { log } = started ;
1791+ reloader = started . reloader ;
1792+ const backoffs = [ 500 , 2000 , 5000 ] as const ;
1793+
1794+ for ( let index = 0 ; index < watchers . length - 1 ; index += 1 ) {
1795+ watchers [ index ] ?. emit ( "error" ) ;
1796+ await vi . advanceTimersByTimeAsync ( backoffs [ index ] ?? 0 ) ;
1797+ }
1798+ watchers . at ( - 1 ) ?. emit ( "error" ) ;
1799+
1800+ expect ( reloader . hotReloadStatus ( ) ) . toBe ( "disabled" ) ;
1801+ expect ( log . warn ) . not . toHaveBeenCalledWith (
1802+ expect . stringContaining ( "degrading to polling mode" ) ,
1803+ ) ;
1804+ expect ( log . error ) . toHaveBeenCalledWith ( expect . stringContaining ( "in polling mode" ) ) ;
1805+ } finally {
1806+ if ( originalVitest === undefined ) {
1807+ delete process . env . VITEST ;
1808+ } else {
1809+ process . env . VITEST = originalVitest ;
1810+ }
1811+ if ( originalChokidarPolling === undefined ) {
1812+ delete process . env . CHOKIDAR_USEPOLLING ;
1813+ } else {
1814+ process . env . CHOKIDAR_USEPOLLING = originalChokidarPolling ;
1815+ }
1816+ await reloader ?. stop ( ) ;
1817+ }
1818+ } ) ;
1819+
17751820 it ( "does not report polling fallback when chokidar polling is forced off" , async ( ) => {
17761821 const originalVitest = process . env . VITEST ;
17771822 const originalChokidarPolling = process . env . CHOKIDAR_USEPOLLING ;
0 commit comments