summaryrefslogtreecommitdiff
path: root/gnu/tests
diff options
context:
space:
mode:
authorMaxim Cournoyer <maxim@guixotic.coop>2026-04-28 11:18:58 +0900
committerMaxim Cournoyer <maxim@guixotic.coop>2026-06-01 15:44:59 +0900
commite8ac9c3c3aee52866c8c48dc0aacccb27fc88d5a (patch)
tree2d8e9c7cdf2af8fa37cf782e33e4e3266b83b946 /gnu/tests
parentf036ac2b19bb7ff2f74eef6edc68a543c23c1a89 (diff)
services: Add forgejo-service-type.
* gnu/services/forgejo.scm: New file. * gnu/tests/forgejo.scm: Likewise. * doc/guix.texi (Forge Services): Document it. Change-Id: I2c4e7b570d137b8d2fb7c02a29cb9aaf5366b9e1
Diffstat (limited to 'gnu/tests')
-rw-r--r--gnu/tests/forgejo.scm416
1 files changed, 416 insertions, 0 deletions
diff --git a/gnu/tests/forgejo.scm b/gnu/tests/forgejo.scm
new file mode 100644
index 00000000000..d13114228f7
--- /dev/null
+++ b/gnu/tests/forgejo.scm
@@ -0,0 +1,416 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2026 Maxim Cournoyer <maxim@guixotic.coop>
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(define-module (gnu tests forgejo)
20 #:use-module (gnu packages databases)
21 #:use-module (gnu packages guile)
22 #:use-module (gnu packages linux)
23 #:use-module (gnu packages sqlite)
24 #:use-module (gnu packages version-control)
25 #:use-module (gnu tests)
26 #:use-module (gnu system)
27 #:use-module (gnu system vm)
28 #:use-module (gnu services)
29 #:use-module (gnu services base)
30 #:use-module (gnu services databases)
31 #:use-module (gnu services forgejo)
32 #:use-module (gnu services networking)
33 #:use-module (gnu services ssh)
34 #:use-module (guix gexp)
35 #:use-module (ice-9 match)
36 #:use-module (srfi srfi-1)
37 #:export (%test-forgejo
38 %test-forgejo-mysql
39 %test-forgejo-postgres))
40
41;;; Sample SSH key generated with 'ssh-keygen -t ed25519'.
42(define dummy-ssh-private-key "\
43-----BEGIN OPENSSH PRIVATE KEY-----
44b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
45QyNTUxOQAAACAsDitIHHy6wlmXz7cJO0UQQbrszdpLvBSiTZk1l08z0AAAAJBl4y2eZeMt
46ngAAAAtzc2gtZWQyNTUxOQAAACAsDitIHHy6wlmXz7cJO0UQQbrszdpLvBSiTZk1l08z0A
47AAAEDxsrZIsEgI6cehDgFNsP/FQ3aefvb7fUtp/RF6sZf3GiwOK0gcfLrCWZfPtwk7RRBB
48uuzN2ku8FKJNmTWXTzPQAAAAC21heGltQHRlcnJhAQI=
49-----END OPENSSH PRIVATE KEY-----
50")
51
52(define dummy-ssh-public-key "\
53ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICwOK0gcfLrCWZfPtwk7RRBBuuzN2ku8FK\
54JNmTWXTzPQ dummy")
55
56;;; For a manual test/inspection:
57#;(
58
59$(./pre-inst-env guix system vm \
60-e '(@@ (gnu tests forgejo) %forgejo-os)' --no-graphic) \
61-nic user,model=virtio-net-pci,hostfwd=tcp::3000-:3000 -m 1024
62
63)
64
65(define %forgejo-os
66 (let ((base %simple-os))
67 (operating-system
68 (inherit %simple-os)
69 (packages (cons* git-minimal git-lfs
70 ;;sqlite strace ;for debug
71 %base-packages))
72 (services
73 (cons* (service dhcpcd-service-type)
74 (service openssh-service-type)
75 (service forgejo-service-type
76 (forgejo-configuration
77 (application-slogan "In Code We Trust")
78 (run-mode "dev")
79 (log-level "debug")
80 (user-push-to-create? #t)
81 (default-push-to-create-private? #f)
82 (mailer? #t)
83 (mail-from "Forgejo Test <test@example.com>")
84 (mail-protocol "dummy")
85 (mail-notification? #t)
86 (mail-notification-on-new-user-signin? #t)))
87 %base-services)))))
88
89(define %forgejo-os/postgres
90 (operating-system
91 (inherit %forgejo-os)
92 (services
93 (cons*
94 (service postgresql-service-type
95 (postgresql-configuration
96 (postgresql postgresql)))
97 (service postgresql-role-service-type
98 (postgresql-role-configuration
99 (roles (list (postgresql-role
100 (name "forgejo")
101 (create-database? #t))))))
102 (modify-services (operating-system-user-services %forgejo-os)
103 (forgejo-service-type
104 config =>
105 (forgejo-configuration
106 (inherit config)
107 (database-type "postgres"))))))))
108
109(define %forgejo-os/mysql
110 (operating-system
111 (inherit %forgejo-os)
112 (services
113 (cons
114 (service mysql-service-type)
115 (modify-services (operating-system-user-services %forgejo-os)
116 (forgejo-service-type
117 config =>
118 (forgejo-configuration
119 (inherit config)
120 (database-type "mysql")
121 (database-host "/run/mysqld/mysqld.sock"))))))))
122
123(define (os->forgejo-configuration os)
124 (let ((forgejo-service
125 (find (lambda (x)
126 (eq? forgejo-service-type (service-kind x)))
127 (operating-system-user-services os))))
128 (service-value forgejo-service)))
129
130(define* (make-forgejo-test name #:key (os %forgejo-os))
131 "Return a test of an OS running the Forgejo service."
132
133 (define forgejo-config
134 (os->forgejo-configuration os))
135
136 (define database-type
137 (forgejo-configuration-database-type forgejo-config))
138
139 (define database-name
140 (forgejo-configuration-database-name forgejo-config))
141
142 (define database-user
143 (forgejo-configuration-database-user forgejo-config))
144
145 (define vm
146 (virtual-machine
147 (operating-system (marionette-operating-system
148 os
149 #:imported-modules '((gnu services herd))
150 #:requirements (match database-type
151 ("mysql" '(mysql))
152 ("postgres" '(postgres))
153 ("sqlite3" '()))))
154 (memory-size 1024)
155 (port-forwardings '((3333 . 3000))))) ;3333 on host
156
157 (define test
158 (with-extensions (list guile-json-4)
159 (with-imported-modules '((gnu build dbus-service)
160 (gnu build marionette)
161 (guix base64))
162 #~(begin
163 (use-modules (gnu build dbus-service)
164 (gnu build marionette)
165 (guix base64)
166 (json)
167 (rnrs base)
168 (rnrs bytevectors)
169 (srfi srfi-64)
170 (srfi srfi-71)
171 (web client)
172 (web response)
173 (web uri))
174
175 (define marionette
176 (make-marionette (list #$vm)))
177
178 (test-runner-current (system-test-runner #$output))
179 (test-begin "forgejo")
180
181 (unless (string=? "mysql" #$database-type)
182 (test-skip 2))
183 (test-assert "wait for mysql socket"
184 ;; This can be removed after Shepherd gains systemd notify
185 ;; support (mysql uses it to notify when it's ready).
186 (wait-for-unix-socket
187 #$(forgejo-configuration-database-host forgejo-config)
188 marionette))
189 (test-assert "mysql setup"
190 (marionette-eval
191 '(begin
192 (use-modules (guix build utils))
193 (invoke "mysql" "-e" (string-append "\
194CREATE USER '" #$database-user "'@'localhost';
195CREATE DATABASE " #$database-name ";
196GRANT ALL PRIVILEGES ON " #$database-name ".*"
197"TO '" #$database-user "'@'localhost';
198FLUSH PRIVILEGES;")))
199 marionette))
200
201 (test-assert "forgejo can be restarted"
202 (marionette-eval
203 '(begin
204 (use-modules (gnu services herd))
205 (restart-service 'forgejo))
206 marionette))
207
208 (test-assert "forgejo runs"
209 (marionette-eval
210 '(begin
211 (use-modules (gnu services herd))
212 (start-service 'forgejo))
213 marionette))
214
215 (test-assert "shepherd listens on tcp port 3000"
216 (wait-for-tcp-port 3000 marionette))
217
218 (test-assert "slogan can be retrieved from main page"
219 ;; This socket-activates Forgejo, so we must wait a bit while it
220 ;; becomes ready.
221 (with-retries 20 1
222 (let (((values response body)
223 (http-get "http://localhost:3333")))
224 (assert (= 200 (response-code response)))
225 (string-contains body "In Code We Trust"))))
226
227 (test-assert "create test forgejo user"
228 (marionette-eval
229 '(begin
230 (use-modules (guix build utils))
231 (invoke "su" "-l" "forgejo" "-c"
232 "FORGEJO_WORK_DIR=/var/lib/forgejo forgejo \
233admin user create --username dummy --password dummy \
234--email dummy@localhost --must-change-password=false"))
235 marionette))
236
237 (test-assert "upload ssh key for dummy forgejo user"
238 ;; We use the Forgejo API to get a token, then use it to set the
239 ;; SSH public key of the user, which is needed for
240 ;; non-interactively pushing to a repository.
241 (let (((values response body)
242 (http-post "http://localhost:3333/api/v1/user/keys"
243 #:headers
244 `((Accept . "application/json")
245 (Authorization
246 . ,(string-append
247 "Basic " (base64-encode
248 (string->utf8 "dummy:dummy"))))
249 (Content-Type . "application/json"))
250 #:body (string->utf8
251 (scm->json-string
252 '(("key" . #$dummy-ssh-public-key)
253 ("read_only" . #t)
254 ("title" . "Dummy's SSH key")))))))
255 (= 201 (response-code response))))
256
257 (test-assert "configure git client for dummy user"
258 (marionette-eval
259 '(begin
260 (setenv "HOME" "/root")
261 (chdir "/root")
262
263 ;; Populate private SSH key.
264 (mkdir ".ssh")
265 (call-with-output-file ".ssh/id_ed25519"
266 (lambda (port)
267 (display #$dummy-ssh-private-key port)))
268 (chmod ".ssh/id_ed25519" #o700)
269
270 ;; To avoid interactive prompts.
271 (call-with-output-file ".ssh/config"
272 (lambda (port)
273 (display "StrictHostKeyChecking no\n" port)))
274
275 ;; Init git.
276 (invoke "git" "config" "--global" "user.name" "dummy")
277 (invoke "git" "config" "--global" "user.email"
278 "dummy@localhost"))
279 marionette))
280
281 (test-assert "git create dummy local repo"
282 (marionette-eval
283 '(begin
284 (mkdir "dummy-repo")
285 (chdir "dummy-repo")
286 (invoke "git" "init")
287 (invoke "git" "remote" "add" "origin"
288 "forgejo@localhost:dummy/hello") ;SSH remote
289 (call-with-output-file "hello.txt"
290 (lambda (port)
291 (display "is anybody here?\n" port)))
292 (invoke "git" "add" "hello.txt")
293 (invoke "git" "commit" "-m" "init repo"))
294 marionette))
295
296 (test-assert "git push repo to forgejo via ssh"
297 (marionette-eval
298 '(begin
299 (invoke "git" "push" "origin" "master"))
300 marionette))
301
302 (test-assert "git clone repo from forgejo via http"
303 (marionette-eval
304 '(begin
305 (use-modules (ice-9 textual-ports))
306 ;; FIXME: Switch back 127.0.0.1 to localhost after
307 ;; <https://codeberg.org/shepherd/shepherd/issues/122> is
308 ;; resolved.
309 (invoke "git" "clone" "http://127.0.0.1:3000/dummy/hello"
310 "/tmp/hello-copy")
311 (call-with-input-file "/tmp/hello-copy/hello.txt"
312 (lambda (port)
313 (string-contains (get-string-all port)
314 "is anybody here?"))))
315 marionette))
316
317 (test-assert "setup git lfs"
318 (marionette-eval
319 '(begin
320 (invoke "git" "lfs" "install")
321 (invoke "git" "lfs" "track" "*.blob")
322 (invoke "git" "add" ".gitattributes"))
323 marionette))
324
325 (test-assert "commit blob file with git lfs"
326 (marionette-eval
327 '(begin
328 (use-modules (ice-9 binary-ports)
329 (rnrs base)
330 ((scheme base) #:select (read-bytevector
331 write-bytevector)))
332
333 (call-with-output-file "one-mebibyte.blob"
334 (lambda (out)
335 (write-bytevector
336 (call-with-input-file "/dev/random"
337 (lambda (in)
338 (read-bytevector (expt 2 20) in))
339 #:binary #t)
340 out))
341 #:binary #t)
342
343 (invoke "git" "add" "one-mebibyte.blob")
344 (invoke "git" "commit" "-m" "add binary blob")
345 (zero?
346 (system "git lfs ls-files|grep -Fq one-mebibyte.blob")))
347 marionette))
348
349 (test-assert "push lfs tracked files to forgejo"
350 (marionette-eval
351 '(begin
352 (invoke "git" "push" "origin" "master"))
353 marionette))
354
355 (test-assert "cloned repo contains lfs tracked files"
356 (marionette-eval
357 '(begin
358 ;; FIXME: Switch back 127.0.0.1 to localhost after
359 ;; <https://codeberg.org/shepherd/shepherd/issues/122> is
360 ;; resolved.
361 (invoke "git" "clone" "http://127.0.0.1:3000/dummy/hello"
362 "/tmp/hello-with-blob")
363 (= (expt 2 20)
364 (stat:size
365 (stat "/tmp/hello-with-blob/one-mebibyte.blob"))))
366 marionette))
367
368 (test-assert "email notification works"
369 (begin
370 (let (((values response body)
371 (http-post
372 "http://localhost:3333/user/forgot_password"
373 #:headers `((Content-Type
374 . "application/x-www-form-urlencoded"))
375 #:body (string->utf8
376 (string-append
377 "email=" (uri-encode "dummy@localhost"))))))
378 (assert (= 200 (response-code response))))
379
380 (with-retries 10 1
381 (marionette-eval
382 '(begin
383 (use-modules (ice-9 textual-ports))
384 (string-contains (call-with-input-file
385 "/var/log/forgejo.log"
386 get-string-all)
387 "Subject: Recover your account"))
388 marionette))))
389
390 (test-end)))))
391
392 (gexp->derivation (string-append "forgejo-test-" name) test))
393
394(define %test-forgejo
395 (system-test
396 (name "forgejo")
397 (description "Basic tests for the Forgejo service.")
398 (value (make-forgejo-test "sqlite"))))
399
400(define %test-forgejo-mysql
401 (system-test
402 (name "forgejo-mysql")
403 (description "Basic tests for the Forgejo service, using a MySQL
404database.")
405 (value (make-forgejo-test "mysql" #:os %forgejo-os/mysql))))
406
407(define %test-forgejo-postgres
408 (system-test
409 (name "forgejo-postgres")
410 (description "Basic tests for the Forgejo service, using a PostgreSQL
411database.")
412 (value (make-forgejo-test "postgres" #:os %forgejo-os/postgres))))
413
414;; Local Variables:
415;; eval: (put 'with-retries 'scheme-indent-function 2)
416;; End: