projects/guix·blob javascript-team · refs

538 lines · 18.0 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 Christopher Baines <[email protected]>
3;;; Copyright © 2020, 2022 Marius Bakke <[email protected]>
4;;; Copyright © 2025 Giacomo Leidi <[email protected]>
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(define-module (gnu tests databases)
22 #:use-module (gnu tests)
23 #:use-module (gnu system)
24 #:use-module (gnu system file-systems)
25 #:use-module (gnu system shadow)
26 #:use-module (gnu system vm)
27 #:use-module (gnu services)
28 #:use-module (gnu services databases)
29 #:use-module (gnu services networking)
30 #:use-module (gnu packages databases)
31 #:use-module (guix gexp)
32 #:use-module (guix packages)
33 #:use-module (guix store)
34 #:use-module (srfi srfi-1)
35 #:export (%test-memcached
36 %test-postgresql
37 %test-timescaledb
38 %test-mysql))
39
40;;;
41;;; The Memcached service.
42;;;
43
44(define %memcached-os
45 (simple-operating-system
46 (service dhcpcd-service-type)
47 (service memcached-service-type)))
48
49(define* (run-memcached-test #:optional (port 11211))
50 "Run tests in %MEMCACHED-OS, forwarding PORT."
51 (define os
52 (marionette-operating-system
53 %memcached-os
54 #:imported-modules '((gnu services herd)
55 (guix combinators))))
56
57 (define vm
58 (virtual-machine
59 (operating-system os)
60 (port-forwardings `((11211 . ,port)))))
61
62 (define test
63 (with-imported-modules '((gnu build marionette))
64 #~(begin
65 (use-modules (srfi srfi-11) (srfi srfi-64)
66 (gnu build marionette)
67 (ice-9 rdelim))
68
69 (define marionette
70 (make-marionette (list #$vm)))
71
72 (test-runner-current (system-test-runner #$output))
73 (test-begin "memcached")
74
75 ;; Wait for memcached to be up and running.
76 (test-assert "service running"
77 (marionette-eval
78 '(begin
79 (use-modules (gnu services herd))
80 (match (start-service 'memcached)
81 (#f #f)
82 (('service response-parts ...)
83 (match (assq-ref response-parts 'running)
84 ((pid) pid)))))
85 marionette))
86
87 (let* ((ai (car (getaddrinfo "localhost"
88 #$(number->string port))))
89 (s (socket (addrinfo:fam ai)
90 (addrinfo:socktype ai)
91 (addrinfo:protocol ai)))
92 (key "testkey")
93 (value "guix"))
94 (connect s (addrinfo:addr ai))
95
96 (test-equal "set"
97 "STORED\r"
98 (begin
99 (simple-format s "set ~A 0 60 ~A\r\n~A\r\n"
100 key
101 (string-length value)
102 value)
103 (read-line s)))
104
105 (test-equal "get"
106 (simple-format #f "VALUE ~A 0 ~A\r~A\r"
107 key
108 (string-length value)
109 value)
110 (begin
111 (simple-format s "get ~A\r\n" key)
112 (string-append
113 (read-line s)
114 (read-line s))))
115
116 (close-port s))
117
118 ;; There should be a log file in here.
119 (test-assert "log file"
120 (marionette-eval
121 '(file-exists? "/var/log/memcached")
122 marionette))
123
124 (test-end))))
125
126 (gexp->derivation "memcached-test" test))
127
128(define %test-memcached
129 (system-test
130 (name "memcached")
131 (description "Connect to a running MEMCACHED server.")
132 (value (run-memcached-test))))
133
134
135;;;
136;;; The PostgreSQL service.
137;;;
138
139(define %postgresql-log-directory
140 "/var/log/postgresql")
141
142(define %role-log-file
143 "/var/log/postgresql_roles.log")
144
145(define %postgresql-os
146 (simple-operating-system
147 (extra-special-file "/password"
148 (plain-file "password" "hello"))
149 (service postgresql-service-type
150 (postgresql-configuration
151 (postgresql postgresql)
152 (config-file
153 (postgresql-config-file
154 (extra-config
155 '(("session_preload_libraries" "auto_explain")
156 ("random_page_cost" 2)
157 ("auto_explain.log_min_duration" "100 ms")
158 ("work_mem" "500 MB")
159 ("debug_print_plan" #t)))))))
160 (service postgresql-role-service-type
161 (postgresql-role-configuration
162 (roles
163 (list (postgresql-role
164 (name "root")
165 (create-database? #t))
166 (postgresql-role
167 (name "a_database")
168 (password-file "/password")
169 (create-database? #t))))))))
170
171(define (run-postgresql-test)
172 "Run tests in %POSTGRESQL-OS."
173 (define os
174 (marionette-operating-system
175 %postgresql-os
176 #:imported-modules '((gnu services herd)
177 (guix combinators))))
178
179 (define vm
180 (virtual-machine
181 (operating-system os)
182 (memory-size 512)))
183
184 (define test
185 (with-imported-modules '((gnu build marionette))
186 #~(begin
187 (use-modules (srfi srfi-64)
188 (gnu build marionette))
189
190 (define marionette
191 (make-marionette (list #$vm)))
192
193 (test-runner-current (system-test-runner #$output))
194 (test-begin "postgresql")
195
196 (test-assert "service running"
197 (marionette-eval
198 '(begin
199 (use-modules (gnu services herd))
200 (start-service 'postgres))
201 marionette))
202
203 (test-assert "log-file"
204 (marionette-eval
205 '(begin
206 (use-modules (ice-9 ftw)
207 (ice-9 match)
208 (rnrs io ports))
209
210 (current-output-port
211 (open-file "/dev/console" "w0"))
212 (let ((server-log-file
213 (string-append #$%postgresql-log-directory
214 "/pg_ctl.log")))
215 (and (file-exists? server-log-file)
216 (display
217 (call-with-input-file server-log-file
218 get-string-all)))
219 #t))
220 marionette))
221
222 (test-assert "database ready"
223 (begin
224 (marionette-eval
225 '(begin
226 (let loop ((i 10))
227 (unless (or (zero? i)
228 (and (file-exists? #$%role-log-file)
229 (string-contains
230 (call-with-input-file #$%role-log-file
231 get-string-all)
232 ";\nCREATE DATABASE")))
233 (sleep 1)
234 (loop (- i 1)))))
235 marionette)))
236
237 (test-assert "database creation"
238 (marionette-eval
239 '(begin
240 (use-modules (gnu services herd)
241 (srfi srfi-1)
242 (ice-9 popen))
243 (current-output-port
244 (open-file "/dev/console" "w0"))
245 (every
246 (lambda (role)
247 (let* ((port (open-pipe*
248 OPEN_READ
249 #$(file-append postgresql "/bin/psql")
250 "-tA" "-c"
251 (string-append
252 "SELECT 1 FROM pg_database WHERE"
253 " datname='" role "'")))
254 (output (get-string-all port)))
255 (close-pipe port)
256 (string-contains output "1")))
257 '("root" "a_database")))
258 marionette))
259
260 (test-assert "database use fails without a password"
261 (marionette-eval
262 '(begin
263 (setgid (passwd:gid (getpwnam "alice")))
264 (setuid (passwd:uid (getpw "alice")))
265 (not (zero?
266 (system* #$(file-append postgresql "/bin/psql")
267 "-tA" "-h" "localhost" "-U" "a_database" "-c"
268 (string-append "SELECT 1 FROM pg_database "
269 "WHERE datname='a_database'")))))
270 marionette))
271
272 (test-assert "database passwords are set"
273 (marionette-eval
274 '(begin
275 (use-modules (ice-9 popen))
276 (setgid (passwd:gid (getpwnam "alice")))
277 (setuid (passwd:uid (getpw "alice")))
278 (setenv "PGPASSWORD"
279 (call-with-input-file "/password" get-string-all))
280 (let* ((port (open-pipe*
281 OPEN_READ
282 #$(file-append postgresql "/bin/psql")
283 "-U" "a_database" "-tA" "-h" "localhost" "-c"
284 "SELECT 1 FROM pg_database WHERE datname='a_database'"))
285 (output (get-string-all port)))
286 (close-pipe port)
287 (string=? output "1\n")))
288 marionette))
289
290 (test-end))))
291
292 (gexp->derivation "postgresql-test" test))
293
294(define %test-postgresql
295 (system-test
296 (name "postgresql")
297 (description "Start the PostgreSQL service.")
298 (value (run-postgresql-test))))
299
300;; Test TimescaleDB, a PostgreSQL extension.
301(define %timescaledb-os
302 (let* ((postgresql-services (operating-system-services %postgresql-os))
303 (postgresql-service-configuration
304 (service-value (find (lambda (svc)
305 (eq? (service-kind svc) postgresql-service-type))
306 postgresql-services)))
307 (postgresql-role-service-configuration
308 (service-value (find (lambda (svc)
309 (eq? (service-kind svc)
310 postgresql-role-service-type))
311 postgresql-services))))
312 (simple-operating-system
313 (service postgresql-service-type
314 (postgresql-configuration
315 (inherit postgresql-service-configuration)
316 (postgresql (car (assoc-ref (package-inputs timescaledb)
317 "postgresql")))
318 (extension-packages (list timescaledb))
319 (config-file
320 (postgresql-config-file
321 (inherit (postgresql-configuration-file
322 postgresql-service-configuration))
323 (extra-config
324 (append '(("shared_preload_libraries" "timescaledb"))
325 (postgresql-config-file-extra-config
326 (postgresql-configuration-file
327 postgresql-service-configuration))))))))
328 (service postgresql-role-service-type
329 (postgresql-role-configuration
330 (inherit postgresql-role-service-configuration))))))
331
332(define (run-timescaledb-test)
333 "Run tests in %TIMESCALEDB-OS."
334 (define os
335 (marionette-operating-system
336 %timescaledb-os
337 #:imported-modules '((gnu services herd)
338 (guix combinators))))
339
340 (define vm
341 (virtual-machine
342 (operating-system os)
343 (memory-size 512)))
344
345 (define test
346 (with-imported-modules '((gnu build marionette))
347 #~(begin
348 (use-modules (srfi srfi-64)
349 (gnu build marionette))
350
351 (define marionette
352 (make-marionette (list #$vm)))
353
354 (test-runner-current (system-test-runner #$output))
355 (test-begin "timescaledb")
356
357 (test-assert "PostgreSQL running"
358 (marionette-eval
359 '(begin
360 (use-modules (gnu services herd))
361 (start-service 'postgres))
362 marionette))
363
364 (test-assert "database ready"
365 (begin
366 (marionette-eval
367 '(begin
368 (use-modules (rnrs io ports))
369
370 (let loop ((i 10))
371 (unless (or (zero? i)
372 (and (file-exists? #$%role-log-file)
373 (string-contains
374 (call-with-input-file #$%role-log-file
375 get-string-all)
376 ";\nCREATE DATABASE")))
377 (sleep 1)
378 (loop (- i 1)))))
379 marionette)))
380
381 (test-assert "database creation"
382 (marionette-eval
383 '(begin
384 (use-modules (guix build utils))
385
386 (current-output-port (open-file "/dev/console" "w0"))
387 (invoke "psql" "-tA" "-c" "CREATE DATABASE test"))
388 marionette))
389
390 (test-assert "load extension"
391 (marionette-eval
392 '(begin
393 (current-output-port (open-file "/dev/console" "w0"))
394 ;; Capture stderr for the next test.
395 (current-error-port (open-file "timescaledb.stderr" "w0"))
396 (invoke "psql"
397 "-tA" "-c" "CREATE EXTENSION timescaledb"
398 "test"))
399 marionette))
400
401 (test-assert "telemetry is disabled"
402 (marionette-eval
403 '(begin
404 (let* ((log-file "/var/log/messages")
405 (log (call-with-input-file log-file get-string-all)))
406 (string-contains log "functionality not supported")))
407 marionette))
408
409 (test-assert "create hypertable"
410 (marionette-eval
411 '(begin
412 (current-output-port (open-file "/dev/console" "w0"))
413 (invoke "psql"
414 "-tA" "-c" "CREATE TABLE ht (
415time TIMESTAMP NOT NULL,
416data double PRECISION NULL
417)"
418 "test")
419 (invoke "psql"
420 "-tA" "-c" "SELECT create_hypertable('ht','time')"
421 "test"))
422 marionette))
423
424 (test-end))))
425
426 (gexp->derivation "timescaledb-test" test))
427
428(define %test-timescaledb
429 (system-test
430 (name "timescaledb")
431 (description "Test the TimescaleDB PostgreSQL extension.")
432 (value (run-timescaledb-test))))
433
434
435;;;
436;;; The MySQL service.
437;;;
438
439(define %mysql-os
440 (simple-operating-system
441 (service mysql-service-type
442 (mysql-configuration
443 ;; Disable O_DIRECT since it's not supported on overlayfs.
444 ;; See <https://jira.mariadb.org/browse/MDEV-28751>.
445 (extra-content "innodb-flush-method = fsync")))))
446
447(define* (run-mysql-test)
448 "Run tests in %MYSQL-OS."
449 (define os
450 (marionette-operating-system
451 %mysql-os
452 #:imported-modules '((gnu services herd)
453 (guix combinators))))
454
455 (define vm
456 (virtual-machine
457 (operating-system os)
458 (memory-size 512)))
459
460 (define test
461 (with-imported-modules '((gnu build marionette))
462 #~(begin
463 (use-modules (srfi srfi-11) (srfi srfi-64)
464 (gnu build marionette))
465
466 (define marionette
467 (make-marionette (list #$vm)))
468
469 (test-runner-current (system-test-runner #$output))
470 (test-begin "mysql")
471
472 (test-assert "service running"
473 (marionette-eval
474 '(begin
475 (use-modules (gnu services herd))
476 (match (start-service 'mysql)
477 (#f #f)
478 (('service response-parts ...)
479 (match (assq-ref response-parts 'running)
480 ((pid) pid)))))
481 marionette))
482
483 (test-assert "mysql_upgrade completed"
484 (wait-for-file "/var/lib/mysql/mysql_upgrade_info" marionette))
485
486 (test-assert "socket is ready"
487 (wait-for-unix-socket "/run/mysqld/mysqld.sock" marionette))
488
489 (test-eq "create database"
490 0
491 (marionette-eval
492 '(begin
493 (system* #$(file-append mariadb "/bin/mysql")
494 "-e" "CREATE DATABASE guix;"))
495 marionette))
496
497 (test-eq "create table"
498 0
499 (marionette-eval
500 '(begin
501 (system*
502 #$(file-append mariadb "/bin/mysql") "guix"
503 "-e" "CREATE TABLE facts (id INT, data VARCHAR(12));"))
504 marionette))
505
506 (test-eq "insert data"
507 0
508 (marionette-eval
509 '(begin
510 (system* #$(file-append mariadb "/bin/mysql") "guix"
511 "-e" "INSERT INTO facts VALUES (1, 'awesome')"))
512 marionette))
513
514 (test-equal "retrieve data"
515 "awesome\n"
516 (marionette-eval
517 '(begin
518 (use-modules (ice-9 popen)
519 (rnrs io ports))
520
521 (let* ((port (open-pipe*
522 OPEN_READ
523 #$(file-append mariadb "/bin/mysql") "guix"
524 "-NB" "-e" "SELECT data FROM facts WHERE id=1;"))
525 (output (get-string-all port)))
526 (close-pipe port)
527 output))
528 marionette))
529
530 (test-end))))
531
532 (gexp->derivation "mysql-test" test))
533
534(define %test-mysql
535 (system-test
536 (name "mysql")
537 (description "Start the MySQL service.")
538 (value (run-mysql-test))))