@@ -36,6 +36,7 @@ import {
3636 type SupervisorConnectionState ,
3737} from "./model.ts" ;
3838import * as RpcSession from "../rpc/session.ts" ;
39+ import { presentConnectionState } from "./presentation.ts" ;
3940import * as EnvironmentSupervisor from "./supervisor.ts" ;
4041import * as ConnectionWakeups from "./wakeups.ts" ;
4142import { connectionRouteId , entryWithRoutes , isLearned , mergeLearnedRoutes } from "./routes.ts" ;
@@ -985,6 +986,73 @@ describe("EnvironmentSupervisor", () => {
985986 } ) . pipe ( Effect . provide ( TestClock . layer ( ) ) ) ,
986987 ) ;
987988
989+ it . effect ( "does not report a session lost while suspended as a failed connection" , ( ) =>
990+ Effect . gen ( function * ( ) {
991+ const allowReconnect = yield * Deferred . make < void > ( ) ;
992+ const harness = yield * makeHarness ( {
993+ prepare : ( attempt ) =>
994+ attempt === 2
995+ ? Deferred . await ( allowReconnect ) . pipe ( Effect . as ( PREPARED_CONNECTION ) )
996+ : Effect . succeed ( PREPARED_CONNECTION ) ,
997+ } ) ;
998+ const supervisor = yield * EnvironmentSupervisor . make ( TARGET_ENTRY , {
999+ initiallyDesired : true ,
1000+ } ) . pipe ( Effect . provide ( harness . dependencies ) ) ;
1001+
1002+ yield * awaitState ( supervisor . state , ( state ) => state . phase === "connected" ) ;
1003+ // The OS killed the socket while the app was suspended; its close arrives
1004+ // before the app reports it is active again.
1005+ yield * harness . closeLatestSession ( ) ;
1006+ const waiting = yield * awaitState ( supervisor . state , ( state ) => state . phase === "backoff" ) ;
1007+ expect ( presentConnectionState ( waiting ) ) . toEqual ( {
1008+ phase : "reconnecting" ,
1009+ error : null ,
1010+ traceId : null ,
1011+ } ) ;
1012+
1013+ yield * harness . wake ( "application-active" ) ;
1014+ const replacing = yield * awaitState (
1015+ supervisor . state ,
1016+ ( state ) => state . phase === "connecting" && state . stage === "preparing" ,
1017+ ) ;
1018+ expect ( presentConnectionState ( replacing ) . error ) . toBeNull ( ) ;
1019+
1020+ yield * Deferred . succeed ( allowReconnect , undefined ) ;
1021+ yield * awaitState (
1022+ supervisor . state ,
1023+ ( state ) => state . phase === "connected" && state . generation === 2 ,
1024+ ) ;
1025+ } ) . pipe ( Effect . provide ( TestClock . layer ( ) ) ) ,
1026+ ) ;
1027+
1028+ it . effect ( "does not show an earlier setup failure while replacing a short session" , ( ) =>
1029+ Effect . gen ( function * ( ) {
1030+ const harness = yield * makeHarness ( {
1031+ prepare : ( attempt ) =>
1032+ attempt === 1
1033+ ? Effect . fail ( transient ( "Relay connection timed out." ) )
1034+ : attempt === 3
1035+ ? Effect . never
1036+ : Effect . succeed ( PREPARED_CONNECTION ) ,
1037+ } ) ;
1038+ const supervisor = yield * EnvironmentSupervisor . make ( TARGET_ENTRY , {
1039+ initiallyDesired : true ,
1040+ } ) . pipe ( Effect . provide ( harness . dependencies ) ) ;
1041+
1042+ yield * awaitState ( supervisor . state , ( state ) => state . phase === "backoff" ) ;
1043+ yield * TestClock . adjust ( "3 seconds" ) ;
1044+ yield * awaitState ( supervisor . state , ( state ) => state . phase === "connected" ) ;
1045+ // A long resume replaces the session before it counts as stable.
1046+ yield * harness . wake ( "application-active-reconnect" ) ;
1047+ const replacing = yield * awaitState (
1048+ supervisor . state ,
1049+ ( state ) => state . phase === "connecting" && state . generation === 2 ,
1050+ ) ;
1051+
1052+ expect ( presentConnectionState ( replacing ) . error ) . toBeNull ( ) ;
1053+ } ) . pipe ( Effect . provide ( TestClock . layer ( ) ) ) ,
1054+ ) ;
1055+
9881056 it . effect ( "keeps escalating backoff when a newly opened session flaps" , ( ) =>
9891057 Effect . gen ( function * ( ) {
9901058 const harness = yield * makeHarness ( ) ;
@@ -1245,6 +1313,7 @@ describe("EnvironmentSupervisor", () => {
12451313 ( state ) => state . phase === "connecting" ,
12461314 ) ;
12471315 expect ( reconnecting . attempt ) . toBe ( 1 ) ;
1316+ expect ( reconnecting . lastFailure ) . toBeNull ( ) ;
12481317 expect ( Option . isNone ( yield * SubscriptionRef . get ( supervisor . session ) ) ) . toBe ( true ) ;
12491318
12501319 yield * Deferred . succeed ( allowReconnect , undefined ) ;
0 commit comments