gnu: nss: Build for aarch64-linux with 64-bit support.
[jackhill/guix/guix.git] / tests / publish.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19 ;; Avoid interference.
20 (unsetenv "http_proxy")
21
22 (define-module (test-publish)
23 #:use-module (guix scripts publish)
24 #:use-module (guix tests)
25 #:use-module (guix config)
26 #:use-module (guix utils)
27 #:use-module (guix hash)
28 #:use-module (guix store)
29 #:use-module (guix derivations)
30 #:use-module (guix gexp)
31 #:use-module (guix base32)
32 #:use-module (guix base64)
33 #:use-module ((guix records) #:select (recutils->alist))
34 #:use-module ((guix serialization) #:select (restore-file))
35 #:use-module (guix pk-crypto)
36 #:use-module (guix zlib)
37 #:use-module (web uri)
38 #:use-module (web client)
39 #:use-module (web response)
40 #:use-module (rnrs bytevectors)
41 #:use-module (ice-9 binary-ports)
42 #:use-module (srfi srfi-1)
43 #:use-module (srfi srfi-26)
44 #:use-module (srfi srfi-64)
45 #:use-module (ice-9 format)
46 #:use-module (ice-9 match)
47 #:use-module (ice-9 rdelim))
48
49 (define %store
50 (open-connection-for-tests))
51
52 (define %reference (add-text-to-store %store "ref" "foo"))
53
54 (define %item (add-text-to-store %store "item" "bar" (list %reference)))
55
56 (define (http-get-body uri)
57 (call-with-values (lambda () (http-get uri))
58 (lambda (response body) body)))
59
60 (define (http-get-port uri)
61 (let ((socket (open-socket-for-uri uri)))
62 ;; Make sure to use an unbuffered port so that we can then peek at the
63 ;; underlying file descriptor via 'call-with-gzip-input-port'.
64 (setvbuf socket _IONBF)
65 (call-with-values
66 (lambda ()
67 (http-get uri #:port socket #:streaming? #t))
68 (lambda (response port)
69 ;; Don't (setvbuf port _IONBF) because of <http://bugs.gnu.org/19610>
70 ;; (PORT might be a custom binary input port).
71 port))))
72
73 (define (publish-uri route)
74 (string-append "http://localhost:6789" route))
75
76 (define-syntax-rule (with-separate-output-ports exp ...)
77 ;; Since ports aren't thread-safe in Guile 2.0, duplicate the output and
78 ;; error ports to make sure the two threads don't end up stepping on each
79 ;; other's toes.
80 (with-output-to-port (duplicate-port (current-output-port) "w")
81 (lambda ()
82 (with-error-to-port (duplicate-port (current-error-port) "w")
83 (lambda ()
84 exp ...)))))
85
86 ;; Run a local publishing server in a separate thread.
87 (with-separate-output-ports
88 (call-with-new-thread
89 (lambda ()
90 (guix-publish "--port=6789" "-C0")))) ;attempt to avoid port collision
91
92 (define (wait-until-ready port)
93 ;; Wait until the server is accepting connections.
94 (let ((conn (socket PF_INET SOCK_STREAM 0)))
95 (let loop ()
96 (unless (false-if-exception
97 (connect conn AF_INET (inet-pton AF_INET "127.0.0.1") port))
98 (loop)))))
99
100 ;; Wait until the two servers are ready.
101 (wait-until-ready 6789)
102
103 \f
104 (test-begin "publish")
105
106 (test-equal "/nix-cache-info"
107 (format #f "StoreDir: ~a\nWantMassQuery: 0\nPriority: 100\n"
108 %store-directory)
109 (http-get-body (publish-uri "/nix-cache-info")))
110
111 (test-equal "/*.narinfo"
112 (let* ((info (query-path-info %store %item))
113 (unsigned-info
114 (format #f
115 "StorePath: ~a
116 URL: nar/~a
117 Compression: none
118 NarHash: sha256:~a
119 NarSize: ~d
120 References: ~a~%"
121 %item
122 (basename %item)
123 (bytevector->nix-base32-string
124 (path-info-hash info))
125 (path-info-nar-size info)
126 (basename (first (path-info-references info)))))
127 (signature (base64-encode
128 (string->utf8
129 (canonical-sexp->string
130 ((@@ (guix scripts publish) signed-string)
131 unsigned-info))))))
132 (format #f "~aSignature: 1;~a;~a~%"
133 unsigned-info (gethostname) signature))
134 (utf8->string
135 (http-get-body
136 (publish-uri
137 (string-append "/" (store-path-hash-part %item) ".narinfo")))))
138
139 (test-equal "/*.narinfo with properly encoded '+' sign"
140 ;; See <http://bugs.gnu.org/21888>.
141 (let* ((item (add-text-to-store %store "fake-gtk+" "Congrats!"))
142 (info (query-path-info %store item))
143 (unsigned-info
144 (format #f
145 "StorePath: ~a
146 URL: nar/~a
147 Compression: none
148 NarHash: sha256:~a
149 NarSize: ~d
150 References: ~%"
151 item
152 (uri-encode (basename item))
153 (bytevector->nix-base32-string
154 (path-info-hash info))
155 (path-info-nar-size info)))
156 (signature (base64-encode
157 (string->utf8
158 (canonical-sexp->string
159 ((@@ (guix scripts publish) signed-string)
160 unsigned-info))))))
161 (format #f "~aSignature: 1;~a;~a~%"
162 unsigned-info (gethostname) signature))
163
164 (let ((item (add-text-to-store %store "fake-gtk+" "Congrats!")))
165 (utf8->string
166 (http-get-body
167 (publish-uri
168 (string-append "/" (store-path-hash-part item) ".narinfo"))))))
169
170 (test-equal "/nar/*"
171 "bar"
172 (call-with-temporary-output-file
173 (lambda (temp port)
174 (let ((nar (utf8->string
175 (http-get-body
176 (publish-uri
177 (string-append "/nar/" (basename %item)))))))
178 (call-with-input-string nar (cut restore-file <> temp)))
179 (call-with-input-file temp read-string))))
180
181 (unless (zlib-available?)
182 (test-skip 1))
183 (test-equal "/nar/gzip/*"
184 "bar"
185 (call-with-temporary-output-file
186 (lambda (temp port)
187 (let ((nar (http-get-port
188 (publish-uri
189 (string-append "/nar/gzip/" (basename %item))))))
190 (call-with-gzip-input-port nar
191 (cut restore-file <> temp)))
192 (call-with-input-file temp read-string))))
193
194 (unless (zlib-available?)
195 (test-skip 1))
196 (test-equal "/*.narinfo with compression"
197 `(("StorePath" . ,%item)
198 ("URL" . ,(string-append "nar/gzip/" (basename %item)))
199 ("Compression" . "gzip"))
200 (let ((thread (with-separate-output-ports
201 (call-with-new-thread
202 (lambda ()
203 (guix-publish "--port=6799" "-C5"))))))
204 (wait-until-ready 6799)
205 (let* ((url (string-append "http://localhost:6799/"
206 (store-path-hash-part %item) ".narinfo"))
207 (body (http-get-port url)))
208 (filter (lambda (item)
209 (match item
210 (("Compression" . _) #t)
211 (("StorePath" . _) #t)
212 (("URL" . _) #t)
213 (_ #f)))
214 (recutils->alist body)))))
215
216 (unless (zlib-available?)
217 (test-skip 1))
218 (test-equal "/*.narinfo for a compressed file"
219 '("none" "nar") ;compression-less nar
220 ;; Assume 'guix publish -C' is already running on port 6799.
221 (let* ((item (add-text-to-store %store "fake.tar.gz"
222 "This is a fake compressed file."))
223 (url (string-append "http://localhost:6799/"
224 (store-path-hash-part item) ".narinfo"))
225 (body (http-get-port url))
226 (info (recutils->alist body)))
227 (list (assoc-ref info "Compression")
228 (dirname (assoc-ref info "URL")))))
229
230 (test-equal "/nar/ with properly encoded '+' sign"
231 "Congrats!"
232 (let ((item (add-text-to-store %store "fake-gtk+" "Congrats!")))
233 (call-with-temporary-output-file
234 (lambda (temp port)
235 (let ((nar (utf8->string
236 (http-get-body
237 (publish-uri
238 (string-append "/nar/" (uri-encode (basename item))))))))
239 (call-with-input-string nar (cut restore-file <> temp)))
240 (call-with-input-file temp read-string)))))
241
242 (test-equal "/nar/invalid"
243 404
244 (begin
245 (call-with-output-file (string-append (%store-prefix) "/invalid")
246 (lambda (port)
247 (display "This file is not a valid store item." port)))
248 (response-code (http-get (publish-uri (string-append "/nar/invalid"))))))
249
250 (test-equal "/file/NAME/sha256/HASH"
251 "Hello, Guix world!"
252 (let* ((data "Hello, Guix world!")
253 (hash (call-with-input-string data port-sha256))
254 (drv (run-with-store %store
255 (gexp->derivation "the-file.txt"
256 #~(call-with-output-file #$output
257 (lambda (port)
258 (display #$data port)))
259 #:hash-algo 'sha256
260 #:hash hash)))
261 (out (build-derivations %store (list drv))))
262 (utf8->string
263 (http-get-body
264 (publish-uri
265 (string-append "/file/the-file.txt/sha256/"
266 (bytevector->nix-base32-string hash)))))))
267
268 (test-equal "/file/NAME/sha256/INVALID-NIX-BASE32-STRING"
269 404
270 (let ((uri (publish-uri
271 "/file/the-file.txt/sha256/not-a-nix-base32-string")))
272 (response-code (http-get uri))))
273
274 (test-equal "/file/NAME/sha256/INVALID-HASH"
275 404
276 (let ((uri (publish-uri
277 (string-append "/file/the-file.txt/sha256/"
278 (bytevector->nix-base32-string
279 (call-with-input-string "" port-sha256))))))
280 (response-code (http-get uri))))
281
282 (test-end "publish")