diff options
| author | Nicolas Graves <ngraves@ngraves.fr> | 2026-03-18 09:10:54 +0100 |
|---|---|---|
| committer | Maxim Cournoyer <maxim@guixotic.coop> | 2026-08-27 10:59:20 +0900 |
| commit | e8e49ddeacd69759a7760fc94fdc39f6202d5103 (patch) | |
| tree | cc7335850682625e11b2514addaa72d0d5b2ebd5 | |
| parent | 1bb9bdaf432d0fc8fa9675ba9f535a0043149a81 (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-- | CODEOWNERS | 1 | ||||
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rwxr-xr-x | etc/teams.scm | 3 | ||||
| -rw-r--r-- | guix/build/json-utils.scm | 2 | ||||
| -rw-r--r-- | tests/json-utils.scm | 265 |
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 | |||
| 287 | guix/build/node-build-system\.scm @guix/javascript | 287 | guix/build/node-build-system\.scm @guix/javascript |
| 288 | guix/import/npm-binary\.scm @guix/javascript | 288 | guix/import/npm-binary\.scm @guix/javascript |
| 289 | guix/scripts/import/npm-binary\.scm @guix/javascript | 289 | guix/scripts/import/npm-binary\.scm @guix/javascript |
| 290 | tests/json-utils\.scm @guix/javascript | ||
| 290 | 291 | ||
| 291 | gnu/packages/julia(-.+|)\.scm$ @guix/julia | 292 | gnu/packages/julia(-.+|)\.scm$ @guix/julia |
| 292 | guix/build/julia-build-system\.scm @guix/julia | 293 | guix/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, | ||
| 49 | captures 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: | ||
