summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Graves <ngraves@ngraves.fr>2026-03-18 09:10:54 +0100
committerMaxim Cournoyer <maxim@guixotic.coop>2026-08-27 10:59:20 +0900
commite8e49ddeacd69759a7760fc94fdc39f6202d5103 (patch)
treecc7335850682625e11b2514addaa72d0d5b2ebd5
parent1bb9bdaf432d0fc8fa9675ba9f535a0043149a81 (diff)
tests: Add tests for (guix build json-utils).
* tests/json-utils.scm: New file. * CODEOWNERS, etc/teams.scm, Makefile.am: Record tests/json-utils.scm. Change-Id: Iab107b04d3234c3b26186c56aed96d00050bb425 Signed-off-by: Jelle Licht <jlicht@fsfe.org>
-rw-r--r--CODEOWNERS1
-rw-r--r--Makefile.am1
-rwxr-xr-xetc/teams.scm3
-rw-r--r--guix/build/json-utils.scm2
-rw-r--r--tests/json-utils.scm265
5 files changed, 270 insertions, 2 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index 774f0c3b89c..0c81de49cec 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -287,6 +287,7 @@ guix/build/json-utils\.scm @guix/javascript
287guix/build/node-build-system\.scm @guix/javascript 287guix/build/node-build-system\.scm @guix/javascript
288guix/import/npm-binary\.scm @guix/javascript 288guix/import/npm-binary\.scm @guix/javascript
289guix/scripts/import/npm-binary\.scm @guix/javascript 289guix/scripts/import/npm-binary\.scm @guix/javascript
290tests/json-utils\.scm @guix/javascript
290 291
291gnu/packages/julia(-.+|)\.scm$ @guix/julia 292gnu/packages/julia(-.+|)\.scm$ @guix/julia
292guix/build/julia-build-system\.scm @guix/julia 293guix/build/julia-build-system\.scm @guix/julia
diff --git a/Makefile.am b/Makefile.am
index 5e1f252b9cc..061980e33ad 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -587,6 +587,7 @@ SCM_TESTS = \
587 tests/http-client.scm \ 587 tests/http-client.scm \
588 tests/inferior.scm \ 588 tests/inferior.scm \
589 tests/ipfs.scm \ 589 tests/ipfs.scm \
590 tests/json-utils.scm \
590 tests/ld-wrapper.scm \ 591 tests/ld-wrapper.scm \
591 tests/lint.scm \ 592 tests/lint.scm \
592 tests/modules.scm \ 593 tests/modules.scm \
diff --git a/etc/teams.scm b/etc/teams.scm
index 244f681ced1..c70ff61cb0e 100755
--- a/etc/teams.scm
+++ b/etc/teams.scm
@@ -884,7 +884,8 @@ and the maven-build-system."
884 "guix/build/json-utils.scm" 884 "guix/build/json-utils.scm"
885 "guix/build/node-build-system.scm" 885 "guix/build/node-build-system.scm"
886 "guix/import/npm-binary.scm" 886 "guix/import/npm-binary.scm"
887 "guix/scripts/import/npm-binary.scm"))) 887 "guix/scripts/import/npm-binary.scm"
888 "tests/json-utils.scm")))
888 889
889(define-team julia 890(define-team julia
890 (team 'julia 891 (team 'julia
diff --git a/guix/build/json-utils.scm b/guix/build/json-utils.scm
index 67025a5b1ae..9d94fd85c4d 100644
--- a/guix/build/json-utils.scm
+++ b/guix/build/json-utils.scm
@@ -172,7 +172,7 @@ invalid field value provided, expected string or list of strings, got ~s~%")
172 (data (if (and field-missing? insert?) 172 (data (if (and field-missing? insert?)
173 (acons key '() data) 173 (acons key '() data)
174 data))) 174 data)))
175 (if field-missing? 175 (if (and field-missing? (not insert?))
176 (if strict? 176 (if strict?
177 (raise (make-compound-condition 177 (raise (make-compound-condition
178 (condition (&modify-json-missing-key-error 178 (condition (&modify-json-missing-key-error
diff --git a/tests/json-utils.scm b/tests/json-utils.scm
new file mode 100644
index 00000000000..3f0ad0b8a0d
--- /dev/null
+++ b/tests/json-utils.scm
@@ -0,0 +1,265 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2026 Nicolas Graves <ngraves@ngraves.fr>
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 (test-json-utils)
20 #:use-module (guix build json-utils)
21 #:use-module (guix tests)
22 #:use-module (json)
23 #:use-module (srfi srfi-26)
24 #:use-module (srfi srfi-64))
25
26(define sample-json
27 '(("name" . "my-package")
28 ("version" . "1.0.0")
29 ("dependencies" . (("foo" . "^1.0.0")
30 ("bar" . "^2.0.0")))
31 ("devDependencies" . (("baz" . "^3.0.0")
32 ("qux" . "^4.0.0")))
33 ("scripts" . (("build" . "make build")
34 ("test" . "make test")))))
35
36;; Many json-utils procedures use assoc-set! and assoc-remove! which
37;; mutate alist structure in place. Use copy-tree to obtain a fresh
38;; deep copy for each test.
39(define (fresh-sample-json)
40 (copy-tree sample-json))
41
42(define (package.json)
43 "Sample package.json file as a JSON string."
44 (call-with-output-string
45 (cute scm->json sample-json <> #:pretty #t)))
46
47(define (with-atomic-file-replacement/mock _ proc)
48 "Mock for with-atomic-file-replacement: reads from package.json procedure,
49captures the written output as a string."
50 (call-with-input-string (package.json)
51 (lambda (in)
52 (call-with-output-string
53 (cute proc in <>)))))
54
55(define* (modify-json* #:rest all-arguments)
56 "Mock modify-json* with input from the package.json procedure."
57 (mock ((guix build utils) with-atomic-file-replacement
58 with-atomic-file-replacement/mock)
59 (json-string->scm (apply modify-json all-arguments))))
60
61(test-begin "json-utils")
62
63;;;
64;;; with-atomic-json-file-replacement
65;;;
66
67(test-equal "with-atomic-json-file-replacement, modify top-level field"
68 "2.0.0"
69 (assoc-ref
70 (modify-json* (cut assoc-set! <> "version" "2.0.0"))
71 "version"))
72
73;;;
74;;; delete-json-fields
75;;;
76
77(test-assert "delete-json-fields, single top-level field"
78 (let ((result ((delete-json-fields '("version")) (fresh-sample-json))))
79 (and (not (assoc-ref result "version"))
80 (assoc-ref result "name"))))
81
82(test-assert "delete-json-fields, multiple top-level fields"
83 (let ((result ((delete-json-fields '("version" "name")) (fresh-sample-json))))
84 (and (not (assoc-ref result "version"))
85 (not (assoc-ref result "name"))
86 (assoc-ref result "dependencies"))))
87
88(test-assert "delete-json-fields, nested field with dot syntax"
89 (let ((result ((delete-json-fields '("dependencies.foo")) (fresh-sample-json))))
90 (and (not (assoc-ref (assoc-ref result "dependencies") "foo"))
91 (assoc-ref (assoc-ref result "dependencies") "bar"))))
92
93(test-assert "delete-json-fields, nested field with list syntax"
94 (let ((result ((delete-json-fields '(("dependencies" "bar")))
95 (fresh-sample-json))))
96 (and (not (assoc-ref (assoc-ref result "dependencies") "bar"))
97 (assoc-ref (assoc-ref result "dependencies") "foo"))))
98
99(test-assert "delete-json-fields, missing field with strict? #f"
100 (let ((result ((delete-json-fields '("nonexistent") #:strict? #f)
101 (fresh-sample-json))))
102 (assoc-ref result "name")))
103
104(test-error "delete-json-fields, missing field with strict? #t raises error"
105 &modify-json-missing-key-error
106 ((delete-json-fields '("nonexistent") #:strict? #t) (fresh-sample-json)))
107
108(test-assert "delete-json-fields, missing nested field with strict? #f"
109 (let ((result ((delete-json-fields '("dependencies.nonexistent")
110 #:strict? #f)
111 (fresh-sample-json))))
112 (assoc-ref (assoc-ref result "dependencies") "foo")))
113
114;;;
115;;; replace-json-fields
116;;;
117
118(test-equal "replace-json-fields, single top-level field"
119 "new-name"
120 (assoc-ref
121 ((replace-json-fields '(("name" . "new-name"))) (fresh-sample-json))
122 "name"))
123
124(test-equal "replace-json-fields, nested field with dot syntax"
125 "^5.0.0"
126 (assoc-ref
127 (assoc-ref
128 ((replace-json-fields '(("dependencies.foo" . "^5.0.0")))
129 (fresh-sample-json))
130 "dependencies")
131 "foo"))
132
133(test-equal "replace-json-fields, nested field with list syntax"
134 "^6.0.0"
135 (assoc-ref
136 (assoc-ref
137 ((replace-json-fields '((("dependencies" "bar") . "^6.0.0")))
138 (fresh-sample-json))
139 "dependencies")
140 "bar"))
141
142(test-equal "replace-json-fields, multiple replacements"
143 '("new-name" . "2.0.0")
144 (let ((result ((replace-json-fields
145 '(("name" . "new-name")
146 ("version" . "2.0.0")))
147 (fresh-sample-json))))
148 (cons (assoc-ref result "name")
149 (assoc-ref result "version"))))
150
151(test-error "replace-json-fields, missing field with strict? #t raises error"
152 &modify-json-missing-key-error
153 ((replace-json-fields '(("nonexistent" . "value")) #:strict? #t)
154 (fresh-sample-json)))
155
156(test-assert "replace-json-fields, missing field with strict? #f"
157 (let ((result ((replace-json-fields '(("nonexistent" . "value"))
158 #:strict? #f)
159 (fresh-sample-json))))
160 (equal? (assoc-ref result "name") "my-package")))
161
162;;;
163;;; modify-json-fields
164;;;
165
166(test-equal "modify-json-fields, custom modifier on top-level field"
167 "MY-PACKAGE"
168 (assoc-ref
169 ((modify-json-fields '("name")
170 (lambda (field data key)
171 (assoc-set! data key (string-upcase (assoc-ref data key)))))
172 (fresh-sample-json))
173 "name"))
174
175(test-equal "modify-json-fields, custom modifier on nested field"
176 "^1.0.0-patched"
177 (assoc-ref
178 (assoc-ref
179 ((modify-json-fields '("dependencies.foo")
180 (lambda (field data key)
181 (assoc-set! data key
182 (string-append (assoc-ref data key) "-patched"))))
183 (fresh-sample-json))
184 "dependencies")
185 "foo"))
186
187(test-assert "modify-json-fields, insert? creates missing field"
188 (let ((result ((modify-json-fields '("newField")
189 (lambda (field data key)
190 (assoc-set! data key "inserted"))
191 #:insert? #t)
192 (fresh-sample-json))))
193 (equal? (assoc-ref result "newField") "inserted")))
194
195(test-assert "modify-json-fields, insert? creates nested missing fields"
196 (let ((result ((modify-json-fields '("newSection.newKey")
197 (lambda (field data key)
198 (assoc-set! data key "deep-insert"))
199 #:insert? #t)
200 (fresh-sample-json))))
201 (equal? (assoc-ref (assoc-ref result "newSection") "newKey")
202 "deep-insert")))
203
204(test-assert "modify-json-fields, field-path-mapper"
205 (let ((result ((modify-json-fields '(("name" . "REPLACED"))
206 (lambda (field data key)
207 (assoc-set! data key (cdr field)))
208 #:field-path-mapper car)
209 (fresh-sample-json))))
210 (equal? (assoc-ref result "name") "REPLACED")))
211
212(test-error "modify-json-fields, invalid field-path raises error"
213 &modify-json-invalid-field-value-error
214 ((modify-json-fields '(42) (lambda (field data key) data))
215 (fresh-sample-json)))
216
217;;;
218;;; modify-json (deprecated wrapper)
219;;;
220
221(test-equal "modify-json, single modification"
222 "modified"
223 (assoc-ref
224 (modify-json* (cut assoc-set! <> "name" "modified"))
225 "name"))
226
227(test-equal "modify-json, chained modifications"
228 '("chain-name" . "3.0.0")
229 (let ((result (modify-json*
230 (cut assoc-set! <> "name" "chain-name")
231 (cut assoc-set! <> "version" "3.0.0"))))
232 (cons (assoc-ref result "name")
233 (assoc-ref result "version"))))
234
235;;;
236;;; Integration: delete-json-fields and replace-json-fields with modify-json
237;;;
238
239(test-equal "modify-json + delete-json-fields integration"
240 #f
241 (assoc-ref
242 (modify-json* (delete-json-fields '("devDependencies")))
243 "devDependencies"))
244
245(test-equal "modify-json + replace-json-fields integration"
246 "^9.0.0"
247 (assoc-ref
248 (assoc-ref
249 (modify-json* (replace-json-fields '(("dependencies.foo" . "^9.0.0"))))
250 "dependencies")
251 "foo"))
252
253(test-equal "modify-json + chained delete and replace integration"
254 '(#f . "replaced-name")
255 (let ((result (modify-json*
256 (delete-json-fields '("devDependencies"))
257 (replace-json-fields '(("name" . "replaced-name"))))))
258 (cons (assoc-ref result "devDependencies")
259 (assoc-ref result "name"))))
260
261(test-end "json-utils")
262
263;; Local Variables:
264;; eval: (put 'modify-json-fields 'scheme-indent-function 1)
265;; End: