@@ -109,6 +109,62 @@ test.skipIf(process.platform === "win32")("partial preparation stays excluded an
109109} ) ;
110110
111111import { resumeProduction } from "../src/cli/macos-update" ;
112+ test . skipIf ( process . platform === "win32" ) ( "adopted recovery child must complete canonical route and catalog sync" , async ( ) => {
113+ const f = fixture ( ) ; await performMacOSUpdateCommand ( prepare , f . io ) ; f . replace ( ) ;
114+ const calls :string [ ] = [ ] ; let succeeds = false ;
115+ f . io . resume = ( transaction , authority , restoreOwned ) => resumeProduction ( transaction , authority , restoreOwned , {
116+ service :( ) => ( { kind :"absent" , fingerprint :null , active :false } ) ,
117+ live :async ( ) => ( { pid :123 , port :1234 , source :"runtime" } ) ,
118+ inspect :async ( ) => ( { pid :123 , bundlePath :"/Applications/Test.app" , fingerprint :"child" } ) ,
119+ ensure :async options => { calls . push ( options . action ! ) ; return { schemaVersion :1 , action :options . action ! , ok :succeeds , state :"running" , changed :false , pid :123 , port :1234 , message :"sync" } ; } ,
120+ } ) ;
121+ expect ( ( await performMacOSUpdateCommand ( { action :"reconcile" } , f . io ) ) . status ) . toBe ( "blocked" ) ;
122+ expect ( f . store . read ( ) ?. phase ) . toBe ( "recovering" ) ;
123+ succeeds = true ;
124+ expect ( ( await performMacOSUpdateCommand ( { action :"reconcile" } , f . io ) ) . status ) . toBe ( "recovered" ) ;
125+ expect ( f . store . read ( ) ) . toBeNull ( ) ;
126+ expect ( calls ) . toEqual ( [ "start" , "start" ] ) ;
127+ } ) ;
128+ test . skipIf ( process . platform === "win32" ) ( "ambiguous native route after a recovery config write retains the transaction" , async ( ) => {
129+ const f = fixture ( ) ; await performMacOSUpdateCommand ( prepare , f . io ) ; f . replace ( ) ;
130+ const authority = await f . io . authority ! ( { includeStart :true } ) ;
131+ const transaction = f . store . read ( ) ! ;
132+ expect ( macOSUpdateResumeIntent ( transaction , "newer-fingerprint" ) . restoreOwned ) . toBe ( false ) ;
133+ const calls :string [ ] = [ ] ;
134+ expect ( await resumeProduction ( transaction , authority , false , {
135+ service :( ) => ( { kind :"absent" , fingerprint :null , active :false } ) ,
136+ live :async ( ) => ( { pid :123 , port :1234 , source :"runtime" } ) ,
137+ inspect :async ( ) => ( { pid :123 , bundlePath :"/Applications/Test.app" , fingerprint :"child" } ) ,
138+ intentFingerprint :( ) => "newer-fingerprint" , routing :( ) => "native" ,
139+ ensure :async options => { calls . push ( options . action ! ) ; return { schemaVersion :1 , action :options . action ! , ok :true , state :"running" , changed :false , pid :123 , port :1234 , message :"sync" } ; } ,
140+ } ) ) . toBe ( false ) ;
141+ expect ( calls ) . toEqual ( [ ] ) ;
142+ f . store . recordOff ( authority ) ;
143+ let stopped = false ;
144+ expect ( await resumeProduction ( f . store . read ( ) ! , authority , false , {
145+ restoreNative :( ) => ( { success :true , changed :false , desiredChanged :false , configChanged :false , message :"native" } ) ,
146+ service :( ) => ( { kind :"absent" , fingerprint :null , active :false } ) ,
147+ live :async ( ) => ( { pid :123 , port :1234 , source :"runtime" } ) ,
148+ inspect :async ( ) => ( { pid :123 , bundlePath :"/Applications/Test.app" , fingerprint :"child" } ) ,
149+ intentFingerprint :( ) => "newer-fingerprint" , routing :( ) => "native" ,
150+ stop :async ( ) => { stopped = true ; return { schemaVersion :1 , action :"stop" , ok :true , state :"stopped" , changed :true , pid :null , port :null , message :"stopped" } ; } ,
151+ } ) ) . toBe ( true ) ;
152+ expect ( stopped ) . toBe ( true ) ; authority . releaseAll ( ) ;
153+ } ) ;
154+ test . skipIf ( process . platform === "win32" ) ( "independent or newer supervisor supersedes ambiguous native recovery" , async ( ) => {
155+ const f = fixture ( ) ; await performMacOSUpdateCommand ( prepare , f . io ) ; f . replace ( ) ;
156+ const authority = await f . io . authority ! ( { includeStart :true } ) ;
157+ for ( const service of [ { kind :"independent" as const , fingerprint :null , active :true } , { kind :"bundle" as const , fingerprint :"newer" , active :true } ] ) {
158+ expect ( await resumeProduction ( f . store . read ( ) ! , authority , false , {
159+ service :( ) => service ,
160+ intentFingerprint :( ) => "newer-fingerprint" , routing :( ) => "native" ,
161+ live :async ( ) => { throw Error ( "Independent/newer service must not be inspected" ) ; } ,
162+ ensure :async ( ) => { throw Error ( "Independent/newer service must not be resumed" ) ; } ,
163+ stop :async ( ) => { throw Error ( "Independent/newer service must not be stopped" ) ; } ,
164+ } ) ) . toBe ( true ) ;
165+ }
166+ authority . releaseAll ( ) ;
167+ } ) ;
112168test . skipIf ( process . platform === "win32" ) ( "newer OFF stops a recovery child or active bundle supervisor before clearing exclusion" , async ( ) => {
113169 const f = fixture ( ) ; await performMacOSUpdateCommand ( prepare , f . io ) ; await performMacOSUpdateCommand ( { action :"record-off" } , f . io ) ;
114170 const authority = await f . io . authority ! ( { includeStart :true } ) ; let stops = 0 ;
0 commit comments