@@ -2,8 +2,6 @@ import assert from 'node:assert/strict';
22import { test } from 'vitest' ;
33import { AppError } from '@agent-device/kernel/errors' ;
44import { runAndroidHostAdb , withAndroidHostAdbTransport } from '../adb-host-transport.ts' ;
5- import { listAndroidAdbSerialsQuick } from '../ime-lifecycle.ts' ;
6- import { createLimrunRuntimeDependencies } from '../../../sdk/limrun-runtime-dependencies.ts' ;
75import {
86 withCommandExecutorOverride ,
97 type CommandExecutorOverride ,
@@ -70,10 +68,12 @@ test('without a transport host adb falls back to local runCmd with argv and opti
7068} ) ;
7169
7270test ( 'transport results and errors pass through with their ExecResult shape' , async ( ) => {
71+ // Nonzero results only pass through under allowFailure; the throw contract
72+ // has its own tests below.
7373 const scripted : ExecResult = { stdout : '' , stderr : 'boom' , exitCode : 7 } ;
7474 const result = await withAndroidHostAdbTransport (
7575 async ( ) => scripted ,
76- async ( ) => await runAndroidHostAdb ( [ 'devices' ] ) ,
76+ async ( ) => await runAndroidHostAdb ( [ 'devices' ] , { allowFailure : true } ) ,
7777 ) ;
7878 assert . deepEqual ( result , scripted ) ;
7979
@@ -82,7 +82,7 @@ test('transport results and errors pass through with their ExecResult shape', as
8282 const sloppy = { stdout : undefined , stderr : null , exitCode : '1' } as unknown as ExecResult ;
8383 const coerced = await withAndroidHostAdbTransport (
8484 async ( ) => sloppy ,
85- async ( ) => await runAndroidHostAdb ( [ 'devices' ] ) ,
85+ async ( ) => await runAndroidHostAdb ( [ 'devices' ] , { allowFailure : true } ) ,
8686 ) ;
8787 assert . deepEqual ( coerced , { stdout : '' , stderr : '' , exitCode : 1 } ) ;
8888
@@ -98,6 +98,36 @@ test('transport results and errors pass through with their ExecResult shape', as
9898 ) ;
9999} ) ;
100100
101+ test ( 'a transport nonzero exit without allowFailure throws the classified adb failure' , async ( ) => {
102+ const error = await withAndroidHostAdbTransport (
103+ async ( ) => ( { stdout : '' , stderr : 'error: device offline' , exitCode : 1 } ) ,
104+ async ( ) =>
105+ await runAndroidHostAdb ( [ 'devices' ] ) . then (
106+ ( ) => assert . fail ( 'expected the host adb call to reject' ) ,
107+ ( err : unknown ) => err ,
108+ ) ,
109+ ) ;
110+
111+ assert . ok ( error instanceof AppError ) ;
112+ assert . equal ( error . code , 'COMMAND_FAILED' ) ;
113+ assert . equal ( error . message , 'adb devices exited with code 1' ) ;
114+ assert . equal ( error . details ?. adbFailure , 'device_offline' ) ;
115+ assert . equal ( error . details ?. retriable , true ) ;
116+ assert . match ( String ( error . details ?. hint ) , / a d b r e c o n n e c t / i) ;
117+ assert . equal ( error . details ?. stderr , 'error: device offline' ) ;
118+ } ) ;
119+
120+ test ( 'a transport nonzero exit with allowFailure returns the result untouched' , async ( ) => {
121+ const scripted : ExecResult = { stdout : '' , stderr : 'error: device offline' , exitCode : 1 } ;
122+
123+ const result = await withAndroidHostAdbTransport (
124+ async ( ) => scripted ,
125+ async ( ) => await runAndroidHostAdb ( [ 'devices' ] , { allowFailure : true } ) ,
126+ ) ;
127+
128+ assert . deepEqual ( result , scripted ) ;
129+ } ) ;
130+
101131test ( 'nested transport scopes compose innermost-wins and restore on scope end' , async ( ) => {
102132 const calls : string [ ] = [ ] ;
103133 const transportFor = ( name : string ) => async ( ) => {
@@ -120,44 +150,3 @@ test('nested transport scopes compose innermost-wins and restore on scope end',
120150 assert . deepEqual ( calls , [ 'outer' , 'inner' , 'outer' ] ) ;
121151 assert . equal ( driver . calls . length , 1 ) ;
122152} ) ;
123-
124- test ( 'listAndroidAdbSerialsQuick routes its global devices call through the transport' , async ( ) => {
125- const seenArgs : string [ ] [ ] = [ ] ;
126-
127- const serials = await withAndroidHostAdbTransport (
128- async ( args ) => {
129- seenArgs . push ( [ ...args ] ) ;
130- return {
131- stdout : 'List of devices attached\nemulator-5554\tdevice\n' ,
132- stderr : '' ,
133- exitCode : 0 ,
134- } ;
135- } ,
136- async ( ) => await listAndroidAdbSerialsQuick ( ) ,
137- ) ;
138-
139- assert . deepEqual ( serials , [ 'emulator-5554' ] ) ;
140- assert . deepEqual ( seenArgs , [ [ 'devices' ] ] ) ;
141- } ) ;
142-
143- test ( 'limrun host.runAdb keeps its exported shape and routes through the transport' , async ( ) => {
144- const dependencies = createLimrunRuntimeDependencies ( ) ;
145- const seen : Array < { args : string [ ] ; options ?: ExecOptions } > = [ ] ;
146-
147- const result = await withAndroidHostAdbTransport (
148- async ( args , options ) => {
149- seen . push ( { args, ...( options ? { options } : { } ) } ) ;
150- return { stdout : 'ok' , stderr : '' , exitCode : 0 } ;
151- } ,
152- async ( ) =>
153- await dependencies . host . runAdb ( [ 'disconnect' , 'emulator-5554' ] , {
154- allowFailure : true ,
155- timeoutMs : 10_000 ,
156- } ) ,
157- ) ;
158-
159- assert . deepEqual ( result , { stdout : 'ok' , stderr : '' , exitCode : 0 } ) ;
160- assert . deepEqual ( seen , [
161- { args : [ 'disconnect' , 'emulator-5554' ] , options : { allowFailure : true , timeoutMs : 10_000 } } ,
162- ] ) ;
163- } ) ;
0 commit comments