@@ -2,63 +2,111 @@ const { stat, access } = require('fs').promises
2
2
const { constants } = require ( 'fs' )
3
3
const { execFile } = require ( 'child_process' )
4
4
const { promisify } = require ( 'util' )
5
- const { getDesktopAskpassTrampolinePath } = require ( '../index' )
5
+ const { getDesktopAskpassTrampolinePath, getDesktopCredentialHelperTrampolinePath } = require ( '../index' )
6
6
const split2 = require ( 'split2' )
7
7
const { createServer } = require ( 'net' )
8
8
9
- const trampolinePath = getDesktopAskpassTrampolinePath ( )
9
+ const askPassTrampolinePath = getDesktopAskpassTrampolinePath ( )
10
+ const helperTrampolinePath = getDesktopCredentialHelperTrampolinePath ( )
10
11
const run = promisify ( execFile )
11
12
12
13
describe ( 'desktop-trampoline' , ( ) => {
13
14
it ( 'exists and is a regular file' , async ( ) =>
14
- expect ( ( await stat ( trampolinePath ) ) . isFile ( ) ) . toBe ( true ) )
15
+ expect ( ( await stat ( askPassTrampolinePath ) ) . isFile ( ) ) . toBe ( true ) )
15
16
16
17
it ( 'can be executed by current process' , ( ) =>
17
- access ( trampolinePath , constants . X_OK ) )
18
+ access ( askPassTrampolinePath , constants . X_OK ) )
18
19
19
20
it ( 'fails when required environment variables are missing' , ( ) =>
20
- expect ( run ( trampolinePath , [ 'Username' ] ) ) . rejects . toThrow ( ) )
21
+ expect ( run ( askPassTrampolinePath , [ 'Username' ] ) ) . rejects . toThrow ( ) )
21
22
22
- it ( 'forwards arguments and valid environment variables correctly' , async ( ) => {
23
+ const captureSession = ( ) => {
23
24
const output = [ ]
25
+ let resolveOutput = null
26
+
27
+ const outputPromise = new Promise ( resolve => {
28
+ resolveOutput = resolve
29
+ } )
30
+
24
31
const server = createServer ( socket => {
32
+ let timeoutId = null
25
33
socket . pipe ( split2 ( / \0 / ) ) . on ( 'data' , data => {
26
34
output . push ( data . toString ( 'utf8' ) )
27
- } )
28
35
29
- // Don't send anything and just close the socket after the trampoline is
30
- // done forwarding data.
31
- socket . end ( )
36
+ // Hack: consider the session finished after 100ms of inactivity.
37
+ // In a real-world scenario, you'd have to parse the data to know when
38
+ // the session is finished.
39
+ if ( timeoutId !== null ) {
40
+ clearTimeout ( timeoutId )
41
+ timeoutId = null
42
+ }
43
+ timeoutId = setTimeout ( ( ) => {
44
+ resolveOutput ( output )
45
+ socket . end ( )
46
+ server . close ( )
47
+ } , 100 )
48
+ } )
32
49
} )
33
- server . unref ( )
34
-
35
- const startTrampolineServer = async ( ) => {
36
- return new Promise ( ( resolve , reject ) => {
37
- server . on ( 'error' , e => reject ( e ) )
38
- server . listen ( 0 , '127.0.0.1' , ( ) => {
39
- resolve ( server . address ( ) . port )
40
- } )
50
+
51
+ const serverPortPromise = new Promise ( ( resolve , reject ) => {
52
+ server . on ( 'error' , e => reject ( e ) )
53
+ server . listen ( 0 , '127.0.0.1' , ( ) => {
54
+ resolve ( server . address ( ) . port )
41
55
} )
42
- }
56
+ } )
57
+
58
+ return [ serverPortPromise , outputPromise ]
59
+ }
60
+
61
+ it ( 'forwards arguments and valid environment variables correctly' , async ( ) => {
62
+
63
+ const [ portPromise , outputPromise ] = captureSession ( )
64
+ const port = await portPromise
43
65
44
- const port = await startTrampolineServer ( )
45
66
const env = {
46
67
DESKTOP_TRAMPOLINE_TOKEN : '123456' ,
47
68
DESKTOP_PORT : port ,
48
69
INVALID_VARIABLE : 'foo bar' ,
49
70
}
50
71
const opts = { env }
51
72
52
- await run ( trampolinePath , [ 'baz' ] , opts )
73
+ await run ( askPassTrampolinePath , [ 'baz' ] , opts )
53
74
75
+ const output = await outputPromise
54
76
const outputArguments = output . slice ( 1 , 2 )
55
77
expect ( outputArguments ) . toStrictEqual ( [ 'baz' ] )
56
78
// output[2] is the number of env variables
57
79
const envc = parseInt ( output [ 2 ] )
58
80
const outputEnv = output . slice ( 3 , 3 + envc )
59
81
expect ( outputEnv ) . toHaveLength ( 1 )
60
82
expect ( outputEnv ) . toContain ( 'DESKTOP_TRAMPOLINE_TOKEN=123456' )
83
+ } )
84
+
85
+ it ( 'forwards stdin when running in credential-helper mode' , async ( ) => {
86
+
87
+ const [ portPromise , outputPromise ] = captureSession ( )
88
+ const port = await portPromise
89
+
90
+ const cp = run ( helperTrampolinePath , [ 'get' ] , { env : { DESKTOP_PORT : port } } )
91
+ cp . child . stdin . end ( 'oh hai\n' )
92
+
93
+ await cp
94
+
95
+ const output = await outputPromise
96
+ expect ( output . at ( - 1 ) ) . toBe ( 'oh hai\n' )
97
+ } )
98
+
99
+ it ( 'doesn\'t forward stdin when running in askpass mode' , async ( ) => {
100
+
101
+ const [ portPromise , outputPromise ] = captureSession ( )
102
+ const port = await portPromise
103
+
104
+ const cp = run ( askPassTrampolinePath , [ 'get' ] , { env : { DESKTOP_PORT : port } } )
105
+ cp . child . stdin . end ( 'oh hai\n' )
106
+
107
+ await cp
61
108
62
- server . close ( )
109
+ const output = await outputPromise
110
+ expect ( output . at ( - 1 ) ) . toBe ( '' )
63
111
} )
64
112
} )
0 commit comments