1+ import { assert , test } from 'vitest' ;
2+ import { DockerClient } from '../lib/docker-client.js' ;
3+ import { Logger } from '../lib/logs.js' ;
4+
5+ // Test Docker Exec API functionality
6+
7+ test ( 'should execute ps command in running container and capture output' , async ( ) => {
8+ const client = await DockerClient . fromDockerConfig ( ) ;
9+ let containerId : string | undefined ;
10+
11+ try {
12+ // Pull alpine image first
13+ console . log ( ' Pulling alpine image...' ) ;
14+ await client . imageCreate (
15+ ( event ) => {
16+ if ( event . status ) console . log ( ` ${ event . status } ` ) ;
17+ } ,
18+ {
19+ fromImage : 'docker.io/library/alpine' ,
20+ tag : 'latest' ,
21+ } ,
22+ ) ;
23+
24+ // Create container with sleep infinity to keep it running
25+ console . log ( ' Creating Alpine container with sleep infinity...' ) ;
26+ const createResponse = await client . containerCreate ( {
27+ Image : 'docker.io/library/alpine:latest' ,
28+ Cmd : [ 'sleep' , 'infinity' ] ,
29+ Labels : {
30+ 'test.type' : 'exec-test' ,
31+ } ,
32+ } ) ;
33+
34+ containerId = createResponse . Id ;
35+ assert . isNotNull ( containerId ) ;
36+ console . log ( ` Container created: ${ containerId . substring ( 0 , 12 ) } ` ) ;
37+
38+ // Start the container
39+ console . log ( ' Starting container...' ) ;
40+ await client . containerStart ( containerId ) ;
41+ console . log ( ' Container started' ) ;
42+
43+ // Create exec instance for 'ps' command
44+ console . log ( ' Creating exec instance for ps command...' ) ;
45+ const execResponse = await client . containerExec ( containerId , {
46+ AttachStdout : true ,
47+ AttachStderr : true ,
48+ Cmd : [ 'ps' ] ,
49+ } ) ;
50+
51+ const execId = execResponse . Id ;
52+ assert . isNotNull ( execId ) ;
53+ console . log ( ` Exec instance created: ${ execId . substring ( 0 , 12 ) } ` ) ;
54+
55+ // Set up streams to capture output
56+ const stdoutData : string [ ] = [ ] ;
57+ const stderrData : string [ ] = [ ] ;
58+
59+ const stdoutLogger = new Logger ( ( line : string ) => {
60+ stdoutData . push ( line ) ;
61+ } ) ;
62+
63+ const stderrLogger = new Logger ( ( line : string ) => {
64+ stderrData . push ( line ) ;
65+ } ) ;
66+
67+ // Start exec instance with stream capture
68+ console . log ( ' Starting exec instance...' ) ;
69+ await client . execStart ( execId , stdoutLogger , stderrLogger ) ;
70+ console . log ( ' Exec completed' ) ;
71+
72+ // Verify the output
73+ console . log ( ' Verifying output...' ) ;
74+ console . log ( ` Captured stdout data: ${ JSON . stringify ( stdoutData ) } ` ) ;
75+ console . log ( ` Captured stderr data: ${ JSON . stringify ( stderrData ) } ` ) ;
76+
77+ // Check that we received process information in stdout
78+ const allStdout = stdoutData . join ( '\n' ) ;
79+ assert . include ( allStdout , 'sleep' , 'Should find sleep process in ps output' ) ;
80+
81+ // Inspect the exec instance to verify it completed successfully
82+ console . log ( ' Inspecting exec instance...' ) ;
83+ const execInfo = await client . execInspect ( execId ) ;
84+ console . log ( ` Exec exit code: ${ execInfo . ExitCode } ` ) ;
85+
86+ assert . equal ( execInfo . ExitCode , 0 , 'Exec should complete successfully' ) ;
87+ assert . equal ( execInfo . Running , false , 'Exec should not be running anymore' ) ;
88+
89+ console . log ( ' ✓ Test passed: exec lifecycle completed successfully' ) ;
90+ } finally {
91+ // Clean up: delete container
92+ if ( containerId ) {
93+ console . log ( ' Cleaning up container...' ) ;
94+ try {
95+ await client . containerDelete ( containerId , { force : true } ) ;
96+ console . log ( ' Container deleted' ) ;
97+ } catch ( error ) {
98+ console . warn ( ` Warning: Failed to delete container: ${ error } ` ) ;
99+ }
100+ }
101+
102+ // Close client connection
103+ await client . close ( ) ;
104+ console . log ( ' Client connection closed' ) ;
105+ }
106+ } ) ;
0 commit comments