Merge remote-tracking branch 'origin/staging-next' into staging
[jackhill/guix/guix.git] / tests / publish.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
3 ;;; Copyright © 2020 by Amar M. Singh <nly@disroot.org>
4 ;;; Copyright © 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
5 ;;;
6 ;;; This file is part of GNU Guix.
7 ;;;
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
12 ;;;
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
17 ;;;
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21 ;; Avoid interference.
22 (unsetenv "http_proxy")
23
24 (define-module (test-publish)
25 #:use-module (guix scripts publish)
26 #:use-module (guix tests)
27 #:use-module (guix config)
28 #:use-module (guix utils)
29 #:use-module (gcrypt hash)
30 #:use-module (guix store)
31 #:use-module (guix derivations)
32 #:use-module (guix gexp)
33 #:use-module (guix base32)
34 #:use-module (guix base64)
35 #:use-module ((guix records) #:select (recutils->alist))
36 #:use-module ((guix serialization) #:select (restore-file))
37 #:use-module (gcrypt pk-crypto)
38 #:use-module ((guix pki) #:select (%public-key-file %private-key-file))
39 #:use-module (zlib)
40 #:use-module (lzlib)
41 #:use-module (web uri)
42 #:use-module (web client)
43 #:use-module (web response)
44 #:use-module (rnrs bytevectors)
45 #:use-module (ice-9 binary-ports)
46 #:use-module (srfi srfi-1)
47 #:use-module (srfi srfi-26)
48 #:use-module (srfi srfi-64)
49 #:use-module (ice-9 threads)
50 #:use-module (ice-9 format)
51 #:use-module (ice-9 match)
52 #:use-module (ice-9 rdelim))
53
54 (define %store
55 (open-connection-for-tests))
56
57 (define %reference (add-text-to-store %store "ref" "foo"))
58
59 (define %item (add-text-to-store %store "item" "bar" (list %reference)))
60
61 (define (http-get-body uri)
62 (call-with-values (lambda () (http-get uri))
63 (lambda (response body) body)))
64
65 (define (http-get-port uri)
66 (let ((socket (open-socket-for-uri uri)))
67 ;; Make sure to use an unbuffered port so that we can then peek at the
68 ;; underlying file descriptor via 'call-with-gzip-input-port'.
69 (setvbuf socket 'none)
70 (call-with-values
71 (lambda ()
72 (http-get uri #:port socket #:streaming? #t))
73 (lambda (response port)
74 ;; Don't (setvbuf port 'none) because of <http://bugs.gnu.org/19610>
75 ;; (PORT might be a custom binary input port).
76 port))))
77
78 (define (publish-uri route)
79 (string-append "http://localhost:6789" route))
80
81 (define-syntax-rule (with-separate-output-ports exp ...)
82 ;; Since ports aren't thread-safe in Guile 2.0, duplicate the output and
83 ;; error ports to make sure the two threads don't end up stepping on each
84 ;; other's toes.
85 (with-output-to-port (duplicate-port (current-output-port) "w")
86 (lambda ()
87 (with-error-to-port (duplicate-port (current-error-port) "w")
88 (lambda ()
89 exp ...)))))
90
91 ;; Run a local publishing server in a separate thread.
92 (with-separate-output-ports
93 (call-with-new-thread
94 (lambda ()
95 (guix-publish "--port=6789" "-C0")))) ;attempt to avoid port collision
96
97 (define (wait-until-ready port)
98 ;; Wait until the server is accepting connections.
99 (let ((conn (socket PF_INET SOCK_STREAM 0)))
100 (let loop ()
101 (unless (false-if-exception
102 (connect conn AF_INET (inet-pton AF_INET "127.0.0.1") port))
103 (loop)))))
104
105 (define (wait-for-file file)
106 ;; Wait until FILE shows up.
107 (let loop ((i 20))
108 (cond ((file-exists? file)
109 #t)
110 ((zero? i)
111 (error "file didn't show up" file))
112 (else
113 (pk 'wait-for-file file)
114 (sleep 1)
115 (loop (- i 1))))))
116
117 (define %gzip-magic-bytes
118 ;; Magic bytes of gzip file.
119 #vu8(#x1f #x8b))
120
121 ;; Wait until the two servers are ready.
122 (wait-until-ready 6789)
123
124 ;; Initialize the public/private key SRFI-39 parameters.
125 (%public-key (read-file-sexp %public-key-file))
126 (%private-key (read-file-sexp %private-key-file))
127
128 \f
129 (test-begin "publish")
130
131 (test-equal "/nix-cache-info"
132 (format #f "StoreDir: ~a\nWantMassQuery: 0\nPriority: 100\n"
133 %store-directory)
134 (http-get-body (publish-uri "/nix-cache-info")))
135
136 (test-equal "/*.narinfo"
137 (let* ((info (query-path-info %store %item))
138 (unsigned-info
139 (format #f
140 "StorePath: ~a
141 URL: nar/~a
142 Compression: none
143 FileSize: ~a
144 NarHash: sha256:~a
145 NarSize: ~d
146 References: ~a~%"
147 %item
148 (basename %item)
149 (path-info-nar-size info)
150 (bytevector->nix-base32-string
151 (path-info-hash info))
152 (path-info-nar-size info)
153 (basename (first (path-info-references info)))))
154 (signature (base64-encode
155 (string->utf8
156 (canonical-sexp->string
157 (signed-string unsigned-info))))))
158 (format #f "~aSignature: 1;~a;~a~%"
159 unsigned-info (gethostname) signature))
160 (utf8->string
161 (http-get-body
162 (publish-uri
163 (string-append "/" (store-path-hash-part %item) ".narinfo")))))
164
165 (test-equal "/*.narinfo with properly encoded '+' sign"
166 ;; See <http://bugs.gnu.org/21888>.
167 (let* ((item (add-text-to-store %store "fake-gtk+" "Congrats!"))
168 (info (query-path-info %store item))
169 (unsigned-info
170 (format #f
171 "StorePath: ~a
172 URL: nar/~a
173 Compression: none
174 FileSize: ~a
175 NarHash: sha256:~a
176 NarSize: ~d
177 References: ~%"
178 item
179 (uri-encode (basename item))
180 (path-info-nar-size info)
181 (bytevector->nix-base32-string
182 (path-info-hash info))
183 (path-info-nar-size info)))
184 (signature (base64-encode
185 (string->utf8
186 (canonical-sexp->string
187 (signed-string unsigned-info))))))
188 (format #f "~aSignature: 1;~a;~a~%"
189 unsigned-info (gethostname) signature))
190
191 (let ((item (add-text-to-store %store "fake-gtk+" "Congrats!")))
192 (utf8->string
193 (http-get-body
194 (publish-uri
195 (string-append "/" (store-path-hash-part item) ".narinfo"))))))
196
197 (test-equal "/nar/*"
198 "bar"
199 (call-with-temporary-output-file
200 (lambda (temp port)
201 (let ((nar (utf8->string
202 (http-get-body
203 (publish-uri
204 (string-append "/nar/" (basename %item)))))))
205 (call-with-input-string nar (cut restore-file <> temp)))
206 (call-with-input-file temp read-string))))
207
208 (test-equal "/nar/gzip/*"
209 "bar"
210 (call-with-temporary-output-file
211 (lambda (temp port)
212 (let ((nar (http-get-port
213 (publish-uri
214 (string-append "/nar/gzip/" (basename %item))))))
215 (call-with-gzip-input-port nar
216 (cut restore-file <> temp)))
217 (call-with-input-file temp read-string))))
218
219 (test-equal "/nar/gzip/* is really gzip"
220 %gzip-magic-bytes
221 ;; Since 'gzdopen' (aka. 'call-with-gzip-input-port') transparently reads
222 ;; uncompressed gzip, the test above doesn't check whether it's actually
223 ;; gzip. This is what this test does. See <https://bugs.gnu.org/30184>.
224 (let ((nar (http-get-port
225 (publish-uri
226 (string-append "/nar/gzip/" (basename %item))))))
227 (get-bytevector-n nar (bytevector-length %gzip-magic-bytes))))
228
229 (test-equal "/nar/lzip/*"
230 "bar"
231 (call-with-temporary-output-file
232 (lambda (temp port)
233 (let ((nar (http-get-port
234 (publish-uri
235 (string-append "/nar/lzip/" (basename %item))))))
236 (call-with-lzip-input-port nar
237 (cut restore-file <> temp)))
238 (call-with-input-file temp read-string))))
239
240 (test-equal "/*.narinfo with compression"
241 `(("StorePath" . ,%item)
242 ("URL" . ,(string-append "nar/gzip/" (basename %item)))
243 ("Compression" . "gzip"))
244 (let ((thread (with-separate-output-ports
245 (call-with-new-thread
246 (lambda ()
247 (guix-publish "--port=6799" "-C5"))))))
248 (wait-until-ready 6799)
249 (let* ((url (string-append "http://localhost:6799/"
250 (store-path-hash-part %item) ".narinfo"))
251 (body (http-get-port url)))
252 (filter (lambda (item)
253 (match item
254 (("Compression" . _) #t)
255 (("StorePath" . _) #t)
256 (("URL" . _) #t)
257 (_ #f)))
258 (recutils->alist body)))))
259
260 (test-equal "/*.narinfo with lzip compression"
261 `(("StorePath" . ,%item)
262 ("URL" . ,(string-append "nar/lzip/" (basename %item)))
263 ("Compression" . "lzip"))
264 (let ((thread (with-separate-output-ports
265 (call-with-new-thread
266 (lambda ()
267 (guix-publish "--port=6790" "-Clzip"))))))
268 (wait-until-ready 6790)
269 (let* ((url (string-append "http://localhost:6790/"
270 (store-path-hash-part %item) ".narinfo"))
271 (body (http-get-port url)))
272 (filter (lambda (item)
273 (match item
274 (("Compression" . _) #t)
275 (("StorePath" . _) #t)
276 (("URL" . _) #t)
277 (_ #f)))
278 (recutils->alist body)))))
279
280 (test-equal "/*.narinfo for a compressed file"
281 '("none" "nar") ;compression-less nar
282 ;; Assume 'guix publish -C' is already running on port 6799.
283 (let* ((item (add-text-to-store %store "fake.tar.gz"
284 "This is a fake compressed file."))
285 (url (string-append "http://localhost:6799/"
286 (store-path-hash-part item) ".narinfo"))
287 (body (http-get-port url))
288 (info (recutils->alist body)))
289 (list (assoc-ref info "Compression")
290 (dirname (assoc-ref info "URL")))))
291
292 (test-equal "/*.narinfo with lzip + gzip"
293 `((("StorePath" . ,%item)
294 ("URL" . ,(string-append "nar/gzip/" (basename %item)))
295 ("Compression" . "gzip")
296 ("URL" . ,(string-append "nar/lzip/" (basename %item)))
297 ("Compression" . "lzip"))
298 200
299 200)
300 (call-with-temporary-directory
301 (lambda (cache)
302 (let ((thread (with-separate-output-ports
303 (call-with-new-thread
304 (lambda ()
305 (guix-publish "--port=6793" "-Cgzip:2" "-Clzip:2"))))))
306 (wait-until-ready 6793)
307 (let* ((base "http://localhost:6793/")
308 (part (store-path-hash-part %item))
309 (url (string-append base part ".narinfo"))
310 (body (http-get-port url)))
311 (list (take (recutils->alist body) 5)
312 (response-code
313 (http-get (string-append base "nar/gzip/"
314 (basename %item))))
315 (response-code
316 (http-get (string-append base "nar/lzip/"
317 (basename %item))))))))))
318
319 (test-equal "custom nar path"
320 ;; Serve nars at /foo/bar/chbouib instead of /nar.
321 (list `(("StorePath" . ,%item)
322 ("URL" . ,(string-append "foo/bar/chbouib/" (basename %item)))
323 ("Compression" . "none"))
324 200
325 404)
326 (let ((thread (with-separate-output-ports
327 (call-with-new-thread
328 (lambda ()
329 (guix-publish "--port=6798" "-C0"
330 "--nar-path=///foo/bar//chbouib/"))))))
331 (wait-until-ready 6798)
332 (let* ((base "http://localhost:6798/")
333 (part (store-path-hash-part %item))
334 (url (string-append base part ".narinfo"))
335 (nar-url (string-append base "foo/bar/chbouib/"
336 (basename %item)))
337 (body (http-get-port url)))
338 (list (filter (lambda (item)
339 (match item
340 (("Compression" . _) #t)
341 (("StorePath" . _) #t)
342 (("URL" . _) #t)
343 (_ #f)))
344 (recutils->alist body))
345 (response-code (http-get nar-url))
346 (response-code
347 (http-get (string-append base "nar/" (basename %item))))))))
348
349 (test-equal "/nar/ with properly encoded '+' sign"
350 "Congrats!"
351 (let ((item (add-text-to-store %store "fake-gtk+" "Congrats!")))
352 (call-with-temporary-output-file
353 (lambda (temp port)
354 (let ((nar (utf8->string
355 (http-get-body
356 (publish-uri
357 (string-append "/nar/" (uri-encode (basename item))))))))
358 (call-with-input-string nar (cut restore-file <> temp)))
359 (call-with-input-file temp read-string)))))
360
361 (test-equal "/nar/invalid"
362 404
363 (begin
364 (call-with-output-file (string-append (%store-prefix) "/invalid")
365 (lambda (port)
366 (display "This file is not a valid store item." port)))
367 (response-code (http-get (publish-uri (string-append "/nar/invalid"))))))
368
369 (test-equal "/file/NAME/sha256/HASH"
370 "Hello, Guix world!"
371 (let* ((data "Hello, Guix world!")
372 (hash (call-with-input-string data port-sha256))
373 (drv (run-with-store %store
374 (gexp->derivation "the-file.txt"
375 #~(call-with-output-file #$output
376 (lambda (port)
377 (display #$data port)))
378 #:hash-algo 'sha256
379 #:hash hash)))
380 (out (build-derivations %store (list drv))))
381 (utf8->string
382 (http-get-body
383 (publish-uri
384 (string-append "/file/the-file.txt/sha256/"
385 (bytevector->nix-base32-string hash)))))))
386
387 (test-equal "/file/NAME/sha256/INVALID-NIX-BASE32-STRING"
388 404
389 (let ((uri (publish-uri
390 "/file/the-file.txt/sha256/not-a-nix-base32-string")))
391 (response-code (http-get uri))))
392
393 (test-equal "/file/NAME/sha256/INVALID-HASH"
394 404
395 (let ((uri (publish-uri
396 (string-append "/file/the-file.txt/sha256/"
397 (bytevector->nix-base32-string
398 (call-with-input-string "" port-sha256))))))
399 (response-code (http-get uri))))
400
401 (test-equal "with cache"
402 (list #t
403 `(("StorePath" . ,%item)
404 ("URL" . ,(string-append "nar/gzip/" (basename %item)))
405 ("Compression" . "gzip"))
406 200 ;nar/gzip/…
407 #t ;Content-Length
408 #t ;FileSize
409 404) ;nar/…
410 (call-with-temporary-directory
411 (lambda (cache)
412 (let ((thread (with-separate-output-ports
413 (call-with-new-thread
414 (lambda ()
415 (guix-publish "--port=6797" "-C2"
416 (string-append "--cache=" cache)
417 "--cache-bypass-threshold=0"))))))
418 (wait-until-ready 6797)
419 (let* ((base "http://localhost:6797/")
420 (part (store-path-hash-part %item))
421 (url (string-append base part ".narinfo"))
422 (nar-url (string-append base "nar/gzip/" (basename %item)))
423 (cached (string-append cache "/gzip/" (basename %item)
424 ".narinfo"))
425 (nar (string-append cache "/gzip/"
426 (basename %item) ".nar"))
427 (response (http-get url)))
428 (and (= 404 (response-code response))
429
430 ;; We should get an explicitly short TTL for 404 in this case
431 ;; because it's going to become 200 shortly.
432 (match (assq-ref (response-headers response) 'cache-control)
433 ((('max-age . ttl))
434 (< ttl 3600)))
435
436 (wait-for-file cached)
437 (let* ((body (http-get-port url))
438 (compressed (http-get nar-url))
439 (uncompressed (http-get (string-append base "nar/"
440 (basename %item))))
441 (narinfo (recutils->alist body)))
442 (list (file-exists? nar)
443 (filter (lambda (item)
444 (match item
445 (("Compression" . _) #t)
446 (("StorePath" . _) #t)
447 (("URL" . _) #t)
448 (_ #f)))
449 narinfo)
450 (response-code compressed)
451 (= (response-content-length compressed)
452 (stat:size (stat nar)))
453 (= (string->number
454 (assoc-ref narinfo "FileSize"))
455 (stat:size (stat nar)))
456 (response-code uncompressed)))))))))
457
458 (test-equal "with cache, lzip + gzip"
459 '(200 200 404)
460 (call-with-temporary-directory
461 (lambda (cache)
462 (let ((thread (with-separate-output-ports
463 (call-with-new-thread
464 (lambda ()
465 (guix-publish "--port=6794" "-Cgzip:2" "-Clzip:2"
466 (string-append "--cache=" cache)
467 "--cache-bypass-threshold=0"))))))
468 (wait-until-ready 6794)
469 (let* ((base "http://localhost:6794/")
470 (part (store-path-hash-part %item))
471 (url (string-append base part ".narinfo"))
472 (nar-url (cute string-append "nar/" <> "/"
473 (basename %item)))
474 (cached (cute string-append cache "/" <> "/"
475 (basename %item) ".narinfo"))
476 (nar (cute string-append cache "/" <> "/"
477 (basename %item) ".nar"))
478 (response (http-get url)))
479 (wait-for-file (cached "gzip"))
480 (let* ((body (http-get-port url))
481 (narinfo (recutils->alist body))
482 (uncompressed (string-append base "nar/"
483 (basename %item))))
484 (and (file-exists? (nar "gzip"))
485 (file-exists? (nar "lzip"))
486 (equal? (take (pk 'narinfo/gzip+lzip narinfo) 7)
487 `(("StorePath" . ,%item)
488 ("URL" . ,(nar-url "gzip"))
489 ("Compression" . "gzip")
490 ("FileSize" . ,(number->string
491 (stat:size (stat (nar "gzip")))))
492 ("URL" . ,(nar-url "lzip"))
493 ("Compression" . "lzip")
494 ("FileSize" . ,(number->string
495 (stat:size (stat (nar "lzip")))))))
496 (list (response-code
497 (http-get (string-append base (nar-url "gzip"))))
498 (response-code
499 (http-get (string-append base (nar-url "lzip"))))
500 (response-code
501 (http-get uncompressed))))))))))
502
503 (let ((item (add-text-to-store %store "fake-compressed-thing.tar.gz"
504 (random-text))))
505 (test-equal "with cache, uncompressed"
506 (list #t
507 (* 42 3600) ;TTL on narinfo
508 `(("StorePath" . ,item)
509 ("URL" . ,(string-append "nar/" (basename item)))
510 ("Compression" . "none"))
511 200 ;nar/…
512 (* 42 3600) ;TTL on nar/…
513 (path-info-nar-size
514 (query-path-info %store item)) ;FileSize
515 404) ;nar/gzip/…
516 (call-with-temporary-directory
517 (lambda (cache)
518 (let ((thread (with-separate-output-ports
519 (call-with-new-thread
520 (lambda ()
521 (guix-publish "--port=6796" "-C2" "--ttl=42h"
522 (string-append "--cache=" cache)
523 "--cache-bypass-threshold=0"))))))
524 (wait-until-ready 6796)
525 (let* ((base "http://localhost:6796/")
526 (part (store-path-hash-part item))
527 (url (string-append base part ".narinfo"))
528 (cached (string-append cache "/none/"
529 (basename item) ".narinfo"))
530 (nar (string-append cache "/none/"
531 (basename item) ".nar"))
532 (response (http-get url)))
533 (and (= 404 (response-code response))
534
535 (wait-for-file cached)
536 (let* ((response (http-get url))
537 (body (http-get-port url))
538 (compressed (http-get (string-append base "nar/gzip/"
539 (basename item))))
540 (uncompressed (http-get (string-append base "nar/"
541 (basename item))))
542 (narinfo (recutils->alist body)))
543 (list (file-exists? nar)
544 (match (assq-ref (response-headers response)
545 'cache-control)
546 ((('max-age . ttl)) ttl)
547 (_ #f))
548
549 (filter (lambda (item)
550 (match item
551 (("Compression" . _) #t)
552 (("StorePath" . _) #t)
553 (("URL" . _) #t)
554 (_ #f)))
555 narinfo)
556 (response-code uncompressed)
557 (match (assq-ref (response-headers uncompressed)
558 'cache-control)
559 ((('max-age . ttl)) ttl)
560 (_ #f))
561
562 (string->number
563 (assoc-ref narinfo "FileSize"))
564 (response-code compressed))))))))))
565
566 (test-equal "with cache, vanishing item" ;<https://bugs.gnu.org/33897>
567 200
568 (call-with-temporary-directory
569 (lambda (cache)
570 (let ((thread (with-separate-output-ports
571 (call-with-new-thread
572 (lambda ()
573 (guix-publish "--port=6795"
574 (string-append "--cache=" cache)))))))
575 (wait-until-ready 6795)
576
577 ;; Make sure that, even if ITEM disappears, we're still able to fetch
578 ;; it.
579 (let* ((base "http://localhost:6795/")
580 (item (add-text-to-store %store "random" (random-text)))
581 (part (store-path-hash-part item))
582 (url (string-append base part ".narinfo"))
583 (cached (string-append cache "/gzip/"
584 (basename item)
585 ".narinfo"))
586 (response (http-get url)))
587 (and (= 200 (response-code response)) ;we're below the threshold
588 (wait-for-file cached)
589 (begin
590 (delete-paths %store (list item))
591 (response-code (pk 'response (http-get url))))))))))
592
593 (test-equal "with cache, cache bypass"
594 200
595 (call-with-temporary-directory
596 (lambda (cache)
597 (let ((thread (with-separate-output-ports
598 (call-with-new-thread
599 (lambda ()
600 (guix-publish "--port=6788" "-C" "gzip"
601 (string-append "--cache=" cache)))))))
602 (wait-until-ready 6788)
603
604 (let* ((base "http://localhost:6788/")
605 (item (add-text-to-store %store "random" (random-text)))
606 (part (store-path-hash-part item))
607 (narinfo (string-append base part ".narinfo"))
608 (nar (string-append base "nar/gzip/" (basename item)))
609 (cached (string-append cache "/gzip/" (basename item)
610 ".narinfo")))
611 ;; We're below the default cache bypass threshold, so NAR and NARINFO
612 ;; should immediately return 200. The NARINFO request should trigger
613 ;; caching, and the next request to NAR should return 200 as well.
614 (and (let ((response (pk 'r1 (http-get nar))))
615 (and (= 200 (response-code response))
616 (not (response-content-length response)))) ;not known
617 (= 200 (response-code (http-get narinfo)))
618 (begin
619 (wait-for-file cached)
620 (let ((response (pk 'r2 (http-get nar))))
621 (and (> (response-content-length response)
622 (stat:size (stat item)))
623 (response-code response))))))))))
624
625 (test-equal "with cache, cache bypass, unmapped hash part"
626 200
627
628 ;; This test reproduces the bug described in <https://bugs.gnu.org/44442>:
629 ;; the daemon connection would be closed as a side effect of a nar request
630 ;; for a non-existing file name.
631 (call-with-temporary-directory
632 (lambda (cache)
633 (let ((thread (with-separate-output-ports
634 (call-with-new-thread
635 (lambda ()
636 (guix-publish "--port=6787" "-C" "gzip"
637 (string-append "--cache=" cache)))))))
638 (wait-until-ready 6787)
639
640 (let* ((base "http://localhost:6787/")
641 (item (add-text-to-store %store "random" (random-text)))
642 (part (store-path-hash-part item))
643 (narinfo (string-append base part ".narinfo"))
644 (nar (string-append base "nar/gzip/" (basename item)))
645 (cached (string-append cache "/gzip/" (basename item)
646 ".narinfo")))
647 ;; The first response used to be 500 and to terminate the daemon
648 ;; connection as a side effect.
649 (and (= (response-code
650 (http-get (string-append base "nar/gzip/"
651 (make-string 32 #\e)
652 "-does-not-exist")))
653 404)
654 (= 200 (response-code (http-get nar)))
655 (= 200 (response-code (http-get narinfo)))
656 (begin
657 (wait-for-file cached)
658 (response-code (http-get nar)))))))))
659
660 (test-equal "/log/NAME"
661 `(200 #t application/x-bzip2)
662 (let ((drv (run-with-store %store
663 (gexp->derivation "with-log"
664 #~(call-with-output-file #$output
665 (lambda (port)
666 (display "Hello, build log!"
667 (current-error-port))
668 (display #$(random-text) port)))))))
669 (build-derivations %store (list drv))
670 (let* ((response (http-get
671 (publish-uri (string-append "/log/"
672 (basename (derivation->output-path drv))))
673 #:decode-body? #f))
674 (base (basename (derivation-file-name drv)))
675 (log (string-append (dirname %state-directory)
676 "/log/guix/drvs/" (string-take base 2)
677 "/" (string-drop base 2) ".bz2")))
678 (list (response-code response)
679 (= (response-content-length response) (stat:size (stat log)))
680 (first (response-content-type response))))))
681
682 (test-equal "/log/NAME not found"
683 404
684 (let ((uri (publish-uri "/log/does-not-exist")))
685 (response-code (http-get uri))))
686
687 (test-equal "/signing-key.pub"
688 200
689 (response-code (http-get (publish-uri "/signing-key.pub"))))
690
691 (test-equal "non-GET query"
692 '(200 404)
693 (let ((path (string-append "/" (store-path-hash-part %item)
694 ".narinfo")))
695 (map response-code
696 (list (http-get (publish-uri path))
697 (http-post (publish-uri path))))))
698
699 (test-end "publish")