@@ -8,7 +8,7 @@ import { SessionStore } from '../../session-store.ts';
88import type { DaemonRequest , DaemonResponse , SessionState } from '../../types.ts' ;
99
1010function makeStore ( ) : SessionStore {
11- const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'agent-device-session-reinstall -' ) ) ;
11+ const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'agent-device-session-app-deploy -' ) ) ;
1212 return new SessionStore ( path . join ( tempRoot , 'sessions' ) ) ;
1313}
1414
@@ -22,7 +22,7 @@ function makeSession(name: string, device: SessionState['device']): SessionState
2222}
2323
2424const invoke = async ( _req : DaemonRequest ) : Promise < DaemonResponse > => {
25- return { ok : false , error : { code : 'INVALID_ARGS' , message : 'invoke should not be called in reinstall tests' } } ;
25+ return { ok : false , error : { code : 'INVALID_ARGS' , message : 'invoke should not be called in app deploy tests' } } ;
2626} ;
2727
2828test ( 'reinstall requires active session or explicit device selector' , async ( ) => {
@@ -229,3 +229,122 @@ test('reinstall succeeds on active Android session with normalized appId', async
229229 assert . equal ( response . data ?. appPath , appPath ) ;
230230 }
231231} ) ;
232+
233+ test ( 'install requires active session or explicit device selector' , async ( ) => {
234+ const sessionStore = makeStore ( ) ;
235+ const response = await handleSessionCommands ( {
236+ req : {
237+ token : 't' ,
238+ session : 'default' ,
239+ command : 'install' ,
240+ positionals : [ 'com.example.app' , '/tmp/app.apk' ] ,
241+ flags : { } ,
242+ } ,
243+ sessionName : 'default' ,
244+ logPath : '/tmp/daemon.log' ,
245+ sessionStore,
246+ invoke,
247+ } ) ;
248+ assert . ok ( response ) ;
249+ assert . equal ( response . ok , false ) ;
250+ if ( ! response . ok ) {
251+ assert . equal ( response . error . code , 'INVALID_ARGS' ) ;
252+ assert . match ( response . error . message , / a c t i v e s e s s i o n o r a n e x p l i c i t d e v i c e s e l e c t o r / i) ;
253+ }
254+ } ) ;
255+
256+ test ( 'install succeeds on active iOS simulator session and records action' , async ( ) => {
257+ const sessionStore = makeStore ( ) ;
258+ const session = makeSession ( 'default' , {
259+ platform : 'ios' ,
260+ id : 'sim-1' ,
261+ name : 'iPhone' ,
262+ kind : 'simulator' ,
263+ booted : true ,
264+ } ) ;
265+ sessionStore . set ( 'default' , session ) ;
266+ const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'agent-device-install-success-ios-' ) ) ;
267+ const appPath = path . join ( tempRoot , 'Sample.app' ) ;
268+ fs . writeFileSync ( appPath , 'placeholder' ) ;
269+
270+ const response = await handleSessionCommands ( {
271+ req : {
272+ token : 't' ,
273+ session : 'default' ,
274+ command : 'install' ,
275+ positionals : [ 'com.example.app' , appPath ] ,
276+ flags : { } ,
277+ } ,
278+ sessionName : 'default' ,
279+ logPath : '/tmp/daemon.log' ,
280+ sessionStore,
281+ invoke,
282+ installOps : {
283+ ios : async ( _device , app , pathToBinary ) => {
284+ assert . equal ( app , 'com.example.app' ) ;
285+ assert . equal ( pathToBinary , appPath ) ;
286+ return { bundleId : 'com.example.app' } ;
287+ } ,
288+ android : async ( ) => {
289+ throw new Error ( 'unexpected android install' ) ;
290+ } ,
291+ } ,
292+ } ) ;
293+
294+ assert . ok ( response ) ;
295+ assert . equal ( response . ok , true ) ;
296+ if ( response . ok ) {
297+ assert . equal ( response . data ?. platform , 'ios' ) ;
298+ assert . equal ( response . data ?. appId , 'com.example.app' ) ;
299+ assert . equal ( response . data ?. bundleId , 'com.example.app' ) ;
300+ assert . equal ( response . data ?. appPath , appPath ) ;
301+ }
302+ assert . equal ( session . actions . length , 1 ) ;
303+ assert . equal ( session . actions [ 0 ] ?. command , 'install' ) ;
304+ } ) ;
305+
306+ test ( 'install omits app id fields when platform op cannot resolve them' , async ( ) => {
307+ const sessionStore = makeStore ( ) ;
308+ sessionStore . set (
309+ 'default' ,
310+ makeSession ( 'default' , {
311+ platform : 'android' ,
312+ id : 'emulator-5554' ,
313+ name : 'Pixel' ,
314+ kind : 'emulator' ,
315+ booted : true ,
316+ } ) ,
317+ ) ;
318+ const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'agent-device-install-fallback-appid-' ) ) ;
319+ const appPath = path . join ( tempRoot , 'Sample.apk' ) ;
320+ fs . writeFileSync ( appPath , 'placeholder' ) ;
321+
322+ const response = await handleSessionCommands ( {
323+ req : {
324+ token : 't' ,
325+ session : 'default' ,
326+ command : 'install' ,
327+ positionals : [ 'Demo' , appPath ] ,
328+ flags : { } ,
329+ } ,
330+ sessionName : 'default' ,
331+ logPath : '/tmp/daemon.log' ,
332+ sessionStore,
333+ invoke,
334+ installOps : {
335+ ios : async ( ) => {
336+ throw new Error ( 'unexpected ios install' ) ;
337+ } ,
338+ android : async ( ) => ( { } ) ,
339+ } ,
340+ } ) ;
341+
342+ assert . ok ( response ) ;
343+ assert . equal ( response . ok , true ) ;
344+ if ( response . ok ) {
345+ assert . equal ( response . data ?. platform , 'android' ) ;
346+ assert . equal ( response . data ?. appId , undefined ) ;
347+ assert . equal ( response . data ?. package , undefined ) ;
348+ assert . equal ( response . data ?. appPath , appPath ) ;
349+ }
350+ } ) ;
0 commit comments