@@ -44,6 +44,25 @@ function request(requestId: string, platform: NodeJS.Platform): DesktopAppActiva
4444 } ;
4545}
4646
47+ function startOkServer ( target : ReturnType < typeof makeTarget > , userId : number | undefined ) {
48+ return startDesktopAppControlServer ( {
49+ ...target ,
50+ userId,
51+ handle : async ( input ) => ( {
52+ version : 1 ,
53+ requestId : input . requestId ,
54+ ok : true ,
55+ projectId : ProjectId . make ( "project-1" ) ,
56+ threadId : ThreadId . make ( "thread-1" ) ,
57+ } ) ,
58+ cancel : ( ) => undefined ,
59+ onReclaimError : ( ) => undefined ,
60+ } ) . then ( ( server ) => {
61+ openServers . push ( server ) ;
62+ return server ;
63+ } ) ;
64+ }
65+
4766function exchange ( address : string , payload : DesktopAppActivationRequest ) {
4867 return new Promise < DesktopAppActivationResponse > ( ( resolve , reject ) => {
4968 const socket = NodeNet . createConnection ( address ) ;
@@ -84,6 +103,7 @@ describe("desktop app control server", () => {
84103 } ;
85104 } ,
86105 cancel : ( ) => undefined ,
106+ onReclaimError : ( ) => undefined ,
87107 } ) ;
88108 openServers . push ( server ) ;
89109
@@ -117,6 +137,7 @@ describe("desktop app control server", () => {
117137 userId,
118138 handle : ( ) => new Promise ( ( ) => undefined ) ,
119139 cancel : resolveCanceled ,
140+ onReclaimError : ( ) => undefined ,
120141 } ) ;
121142 openServers . push ( server ) ;
122143 const socket = NodeNet . createConnection ( target . address ) ;
@@ -137,4 +158,50 @@ describe("desktop app control server", () => {
137158 } ) ;
138159 } ) ,
139160 ) ;
161+
162+ // Two desktop apps can share one state dir, such as nightly and a preview build.
163+ it . effect ( "keeps a newer app's socket when an older app on the same state dir quits" , ( ) =>
164+ Effect . gen ( function * ( ) {
165+ const platform = yield * HostProcessPlatform ;
166+ const userId = yield * HostProcessUserId ;
167+ if ( platform === "win32" ) return ;
168+ yield * Effect . promise ( async ( ) => {
169+ const root = await NodeFSP . mkdtemp ( NodePath . join ( NodeOS . tmpdir ( ) , "t3-app-takeover-test-" ) ) ;
170+ const target = makeTarget ( NodePath . join ( root , "userdata" ) , platform , userId ) ;
171+ const older = await startOkServer ( target , userId ) ;
172+ await startOkServer ( target , userId ) ;
173+
174+ await older . close ( ) ;
175+
176+ await expect (
177+ exchange ( target . address , request ( "after-quit" , platform ) ) ,
178+ ) . resolves . toMatchObject ( { ok : true , requestId : "after-quit" } ) ;
179+ await NodeFSP . rm ( root , { recursive : true , force : true } ) ;
180+ } ) ;
181+ } ) ,
182+ ) ;
183+
184+ it . effect ( "binds its address again after the socket file is removed" , ( ) =>
185+ Effect . gen ( function * ( ) {
186+ const platform = yield * HostProcessPlatform ;
187+ const userId = yield * HostProcessUserId ;
188+ if ( platform === "win32" ) return ;
189+ yield * Effect . promise ( async ( ) => {
190+ const root = await NodeFSP . mkdtemp ( NodePath . join ( NodeOS . tmpdir ( ) , "t3-app-reclaim-test-" ) ) ;
191+ const target = makeTarget ( NodePath . join ( root , "userdata" ) , platform , userId ) ;
192+ const server = await startOkServer ( target , userId ) ;
193+
194+ await NodeFSP . unlink ( target . address ) ;
195+ await server . reclaim ( ) ;
196+
197+ await expect (
198+ exchange ( target . address , request ( "reclaimed" , platform ) ) ,
199+ ) . resolves . toMatchObject ( {
200+ ok : true ,
201+ requestId : "reclaimed" ,
202+ } ) ;
203+ await NodeFSP . rm ( root , { recursive : true , force : true } ) ;
204+ } ) ;
205+ } ) ,
206+ ) ;
140207} ) ;
0 commit comments