@@ -9,17 +9,18 @@ import com.sun.net.httpserver.HttpHandler
9
9
import com.sun.net.httpserver.HttpServer
10
10
import spock.lang.Requires
11
11
import spock.lang.Shared
12
+ import spock.lang.Specification
12
13
import spock.lang.Unroll
13
14
14
15
import java.nio.file.Files
15
16
import java.nio.file.Path
16
17
import java.nio.file.StandardCopyOption
18
+ import java.security.MessageDigest
17
19
18
20
@Unroll
19
- class CoderCLIManagerTest extends spock.lang . Specification {
21
+ class CoderCLIManagerTest extends Specification {
20
22
@Shared
21
23
private Path tmpdir = Path . of(System . getProperty(" java.io.tmpdir" )). resolve(" coder-gateway-test" )
22
- private String mockBinaryContent = " #!/bin/sh\n echo Coder"
23
24
24
25
/**
25
26
* Create, start, and return a server that mocks Coder.
@@ -32,22 +33,20 @@ class CoderCLIManagerTest extends spock.lang.Specification {
32
33
// TODO: Is there some simple way to create an executable file
33
34
// on Windows without having to execute something to generate
34
35
// said executable or having to commit one to the repo?
35
- String response = mockBinaryContent
36
-
36
+ String response = " #!/bin/sh\n echo 'http://localhost:${ srv.address.port} '"
37
37
String [] etags = exchange. requestHeaders. get(" If-None-Match" )
38
- if (etags != null && etags. contains(" \" 2f1960264fc0f332a2a7fef2fe678f258dcdff9c\" " )) {
39
- code = HttpURLConnection . HTTP_NOT_MODIFIED
40
- response = " not modified"
41
- }
42
-
43
- if (! exchange. requestURI. path. startsWith(" /bin/coder-" )) {
38
+ if (exchange. requestURI. path == " /bin/override" ) {
39
+ code = HttpURLConnection . HTTP_OK
40
+ response = " #!/bin/sh\n echo 'override binary'"
41
+ } else if (! exchange. requestURI. path. startsWith(" /bin/coder-" )) {
44
42
code = HttpURLConnection . HTTP_NOT_FOUND
45
43
response = " not found"
46
- }
47
-
48
- if (errorCode != 0 ) {
44
+ } else if (errorCode != 0 ) {
49
45
code = errorCode
50
- response = " error code ${ code} "
46
+ response = " error code $code "
47
+ } else if (etags != null && etags. contains(" \" ${ sha1(response)} \" " )) {
48
+ code = HttpURLConnection . HTTP_NOT_MODIFIED
49
+ response = " not modified"
51
50
}
52
51
53
52
byte [] body = response. getBytes()
@@ -60,6 +59,23 @@ class CoderCLIManagerTest extends spock.lang.Specification {
60
59
return [srv, " http://localhost:" + srv. address. port]
61
60
}
62
61
62
+ String sha1 (String input ) {
63
+ MessageDigest md = MessageDigest . getInstance(" SHA-1" )
64
+ md. update(input. getBytes(" UTF-8" ))
65
+ return new BigInteger (1 , md. digest()). toString(16 )
66
+ }
67
+
68
+ def " hashes correctly" () {
69
+ expect :
70
+ sha1(input) == output
71
+
72
+ where :
73
+ input | output
74
+ " #!/bin/sh\n echo Coder" | " 2f1960264fc0f332a2a7fef2fe678f258dcdff9c"
75
+ " #!/bin/sh\n echo 'override binary'" | " 1b562a4b8f2617b2b94a828479656daf2dde3619"
76
+ " #!/bin/sh\n echo 'http://localhost:5678'" | " fd8d45a8a74475e560e2e57139923254aab75989"
77
+ }
78
+
63
79
void setupSpec () {
64
80
// Clean up from previous runs otherwise they get cluttered since the
65
81
// mock server port is random.
@@ -140,7 +156,7 @@ class CoderCLIManagerTest extends spock.lang.Specification {
140
156
// The mock does not serve a binary that works on Windows so do not
141
157
// actually execute. Checking the contents works just as well as proof
142
158
// that the binary was correctly downloaded anyway.
143
- ccm. localBinaryPath. toFile(). readBytes() == mockBinaryContent . getBytes()
159
+ ccm. localBinaryPath. toFile(). text == " #!/bin/sh \n echo ' $u rl ' "
144
160
145
161
cleanup :
146
162
srv. stop(0 )
@@ -161,6 +177,7 @@ class CoderCLIManagerTest extends spock.lang.Specification {
161
177
downloaded
162
178
ccm. localBinaryPath. toFile(). readBytes() != " cli" . getBytes()
163
179
ccm. localBinaryPath. toFile(). lastModified() > 0
180
+ ccm. localBinaryPath. toFile(). text == " #!/bin/sh\n echo '$url '"
164
181
165
182
cleanup :
166
183
srv. stop(0 )
@@ -197,14 +214,37 @@ class CoderCLIManagerTest extends spock.lang.Specification {
197
214
198
215
then :
199
216
ccm1. localBinaryPath != ccm2. localBinaryPath
200
- ccm1. localBinaryPath. toFile(). exists()
201
- ccm2. localBinaryPath. toFile(). exists()
217
+ ccm1. localBinaryPath. toFile(). text == " #!/bin/sh \n echo ' $u rl1 ' "
218
+ ccm2. localBinaryPath. toFile(). text == " #!/bin/sh \n echo ' $u rl2 ' "
202
219
203
220
cleanup :
204
221
srv1. stop(0 )
205
222
srv2. stop(0 )
206
223
}
207
224
225
+ def " overrides binary URL" () {
226
+ given :
227
+ def (srv, url) = mockServer()
228
+ def ccm = new CoderCLIManager (new URL (url), tmpdir, override. replace(" {{url}}" , url))
229
+
230
+ when :
231
+ def downloaded = ccm. downloadCLI()
232
+
233
+ then :
234
+ downloaded
235
+ ccm. localBinaryPath. toFile(). text == " #!/bin/sh\n echo '${ expected.replace("{{url}}", url)} '"
236
+
237
+ cleanup :
238
+ srv. stop(0 )
239
+
240
+ where :
241
+ override | expected
242
+ " /bin/override" | " override binary"
243
+ " {{url}}/bin/override" | " override binary"
244
+ " bin/override" | " override binary"
245
+ " " | " {{url}}"
246
+ }
247
+
208
248
Map<String , String > testEnv = [
209
249
" APPDATA" : " /tmp/coder-gateway-test/appdata" ,
210
250
" LOCALAPPDATA" : " /tmp/coder-gateway-test/localappdata" ,
@@ -323,7 +363,7 @@ class CoderCLIManagerTest extends spock.lang.Specification {
323
363
def " configures an SSH file" () {
324
364
given :
325
365
def sshConfigPath = tmpdir. resolve(input + " _to_" + output + " .conf" )
326
- def ccm = new CoderCLIManager (new URL (" https://test.coder.invalid" ), tmpdir, sshConfigPath)
366
+ def ccm = new CoderCLIManager (new URL (" https://test.coder.invalid" ), tmpdir, null , sshConfigPath)
327
367
if (input != null ) {
328
368
Files . createDirectories(sshConfigPath. getParent())
329
369
def originalConf = Path . of(" src/test/fixtures/inputs" ). resolve(input + " .conf" ). toFile(). text
@@ -368,7 +408,7 @@ class CoderCLIManagerTest extends spock.lang.Specification {
368
408
def " fails if config is malformed" () {
369
409
given :
370
410
def sshConfigPath = tmpdir. resolve(" configured" + input + " .conf" )
371
- def ccm = new CoderCLIManager (new URL (" https://test.coder.invalid" ), tmpdir, sshConfigPath)
411
+ def ccm = new CoderCLIManager (new URL (" https://test.coder.invalid" ), tmpdir, null , sshConfigPath)
372
412
Files . createDirectories(sshConfigPath. getParent())
373
413
Files . copy(
374
414
Path . of(" src/test/fixtures/inputs" ). resolve(input + " .conf" ),
0 commit comments