@@ -591,9 +591,11 @@ describe("CoderApi", () => {
591591
592592 const setupAutoOpeningWebSocket = ( ) => {
593593 const sockets : Array < Partial < Ws > > = [ ] ;
594+ const handlers : Record < string , ( ...args : unknown [ ] ) => void > = { } ;
594595 vi . mocked ( Ws ) . mockImplementation ( function ( url : string | URL ) {
595596 const mockWs = createMockWebSocket ( String ( url ) , {
596- on : vi . fn ( ( event , handler ) => {
597+ on : vi . fn ( ( event : string , handler : ( ...args : unknown [ ] ) => void ) => {
598+ handlers [ event ] = handler ;
597599 if ( event === "open" ) {
598600 setImmediate ( ( ) => handler ( ) ) ;
599601 }
@@ -603,12 +605,12 @@ describe("CoderApi", () => {
603605 sockets . push ( mockWs ) ;
604606 return mockWs as Ws ;
605607 } ) ;
606- return sockets ;
608+ return { sockets, handlers } ;
607609 } ;
608610
609611 describe ( "Reconnection on Host/Token Changes" , ( ) => {
610612 it ( "triggers reconnection when session token changes" , async ( ) => {
611- const sockets = setupAutoOpeningWebSocket ( ) ;
613+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
612614 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
613615 await api . watchAgentMetadata ( AGENT_ID ) ;
614616
@@ -623,7 +625,7 @@ describe("CoderApi", () => {
623625 } ) ;
624626
625627 it ( "triggers reconnection when host changes" , async ( ) => {
626- const sockets = setupAutoOpeningWebSocket ( ) ;
628+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
627629 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
628630 const wsWrap = await api . watchAgentMetadata ( AGENT_ID ) ;
629631 expect ( wsWrap . url ) . toContain ( CODER_URL . replace ( "http" , "ws" ) ) ;
@@ -642,7 +644,7 @@ describe("CoderApi", () => {
642644 } ) ;
643645
644646 it ( "does not reconnect when token or host are unchanged" , async ( ) => {
645- const sockets = setupAutoOpeningWebSocket ( ) ;
647+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
646648 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
647649 await api . watchAgentMetadata ( AGENT_ID ) ;
648650
@@ -655,7 +657,7 @@ describe("CoderApi", () => {
655657 } ) ;
656658
657659 it ( "suspends sockets when host is set to empty string (logout)" , async ( ) => {
658- const sockets = setupAutoOpeningWebSocket ( ) ;
660+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
659661 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
660662 await api . watchAgentMetadata ( AGENT_ID ) ;
661663
@@ -668,7 +670,7 @@ describe("CoderApi", () => {
668670 } ) ;
669671
670672 it ( "does not reconnect when setting token after clearing host" , async ( ) => {
671- const sockets = setupAutoOpeningWebSocket ( ) ;
673+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
672674 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
673675 await api . watchAgentMetadata ( AGENT_ID ) ;
674676
@@ -682,7 +684,7 @@ describe("CoderApi", () => {
682684 } ) ;
683685
684686 it ( "setCredentials sets both host and token together" , async ( ) => {
685- const sockets = setupAutoOpeningWebSocket ( ) ;
687+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
686688 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
687689 await api . watchAgentMetadata ( AGENT_ID ) ;
688690
@@ -695,7 +697,7 @@ describe("CoderApi", () => {
695697 } ) ;
696698
697699 it ( "setCredentials suspends when host is cleared" , async ( ) => {
698- const sockets = setupAutoOpeningWebSocket ( ) ;
700+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
699701 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
700702 await api . watchAgentMetadata ( AGENT_ID ) ;
701703
@@ -796,29 +798,26 @@ describe("CoderApi", () => {
796798 describe ( "Configuration Change Reconnection" , ( ) => {
797799 const tick = ( ) => new Promise ( ( resolve ) => setImmediate ( resolve ) ) ;
798800
799- it ( "reconnects sockets when watched config value changes" , async ( ) => {
801+ it ( "does not reconnect connected sockets when config value changes" , async ( ) => {
800802 mockConfig . set ( "coder.insecure" , false ) ;
801- const sockets = setupAutoOpeningWebSocket ( ) ;
803+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
802804 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
803805 await api . watchAgentMetadata ( AGENT_ID ) ;
804806 await tick ( ) ; // Wait for open event to fire (socket becomes CONNECTED)
805807
806808 mockConfig . set ( "coder.insecure" , true ) ;
807809 await tick ( ) ;
808810
809- expect ( sockets [ 0 ] . close ) . toHaveBeenCalledWith (
810- 1000 ,
811- "Replacing connection" ,
812- ) ;
813- expect ( sockets ) . toHaveLength ( 2 ) ;
811+ expect ( sockets [ 0 ] . close ) . not . toHaveBeenCalled ( ) ;
812+ expect ( sockets ) . toHaveLength ( 1 ) ;
814813 } ) ;
815814
816815 it . each ( [
817816 [ "unchanged value" , "coder.insecure" , false ] ,
818817 [ "unrelated setting" , "unrelated.setting" , "new-value" ] ,
819818 ] ) ( "does not reconnect for %s" , async ( _desc , key , value ) => {
820819 mockConfig . set ( "coder.insecure" , false ) ;
821- const sockets = setupAutoOpeningWebSocket ( ) ;
820+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
822821 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
823822 await api . watchAgentMetadata ( AGENT_ID ) ;
824823 await tick ( ) ; // Wait for open event to fire (socket becomes CONNECTED)
@@ -832,7 +831,7 @@ describe("CoderApi", () => {
832831
833832 it ( "stops watching after dispose" , async ( ) => {
834833 mockConfig . set ( "coder.insecure" , false ) ;
835- const sockets = setupAutoOpeningWebSocket ( ) ;
834+ const { sockets } = setupAutoOpeningWebSocket ( ) ;
836835 api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
837836 await api . watchAgentMetadata ( AGENT_ID ) ;
838837 await tick ( ) ; // Wait for open event to fire (socket becomes CONNECTED)
@@ -843,6 +842,23 @@ describe("CoderApi", () => {
843842
844843 expect ( sockets ) . toHaveLength ( 1 ) ;
845844 } ) ;
845+
846+ it ( "reconnects sockets in AWAITING_RETRY state when config changes" , async ( ) => {
847+ mockConfig . set ( "coder.insecure" , false ) ;
848+ const { sockets, handlers } = setupAutoOpeningWebSocket ( ) ;
849+ api = createApi ( CODER_URL , AXIOS_TOKEN ) ;
850+ await api . watchAgentMetadata ( AGENT_ID ) ;
851+
852+ // Trigger close with abnormal code to put socket in AWAITING_RETRY
853+ handlers [ "close" ] ?.( { code : 1006 , reason : "Abnormal closure" } ) ;
854+ await tick ( ) ;
855+
856+ mockConfig . set ( "coder.insecure" , true ) ;
857+ await tick ( ) ;
858+
859+ expect ( sockets [ 0 ] . close ) . toHaveBeenCalled ( ) ;
860+ expect ( sockets ) . toHaveLength ( 2 ) ;
861+ } ) ;
846862 } ) ;
847863} ) ;
848864
0 commit comments