wikimusic-54e25aaf-a730

job failed

→ running command: cd "/etc/byggsteg/job-clone//wikimusic/trunk" && git reset --hard && git pull
HEAD is now at b6447a1 docs: Update README
From https://codeberg.org/jjba23/wikimusic
   b6447a1..e568737  trunk      -> origin/trunk
Updating b6447a1..e568737
Fast-forward
 README.org | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

→ running command: cd /etc/byggsteg/job-clone/wikimusic/trunk && uname -a
Linux wolk-jjba 7.1.5-gnu #1 SMP PREEMPT_DYNAMIC 1 x86_64 GNU/Linux

→ running command: cd /etc/byggsteg/job-clone/wikimusic/trunk && git log --name-status HEAD^..HEAD
commit e568737c295540b13d72dfe41c27e29f436801a1
Author: Josep Bigorra <jjbigorra@gmail.com>
Date:   Tue Oct 20 00:18:19 2026 +0200

    docs: README update

M       README.org

→ running command: cd /etc/byggsteg/job-clone/wikimusic/trunk && maak test

scanning directories for unit tests...
scanning present working directory for unit tests...

shuffled the files with seed 34900809 (replay with VERITAS_SEED=34900809)

found unit tests: (controller-spec.scm error-pages-spec.scm guitarpro-view-spec.scm guitarpro-spec.scm views-spec.scm about-spec.scm sql-spec.scm auth-spec.scm domain-spec.scm config-spec.scm assets-spec.scm)


running unit tests from file: controller-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] The guard a mistyped address reaches
  [🧪 test] a row that was found is handed on as it is
    [⚙️ assert] and nothing about it is rewritten
    ✅ got: ((identifier . 1c9a5b7e-2f3a-4c5d-9e0f-1a2b3c4d5e6f) (display-name . blue))
    [⚙️ assert] what it is called does not decide what it returns
    ✅ got: #t
    [⚙️ assert] the very row comes back, not a copy
    ✅ got: #t
  [🧪 test] a lookup that found nothing is answered with a 404
    [⚙️ assert] and its arguments come as a list
    ✅ got: #t
  [🧪 test] the answer says what was looked for, and where
    [⚙️ assert] the message names the kind of thing and the identifier
    ✅ got: no genre with the identifier a-genre-that-is-not-there
  [🧪 test] a lookup that found an empty list is a lookup that found nothing
    [⚙️ assert] an empty list is answered like #f
    ✅ got: 404
+----------------------------------------------+
[📑 suite] The ownership half of who may change a row
  [🧪 test] the super grant changes any row, whoever made it
    [⚙️ assert] and a row of its own
    ✅ got: #t
    [⚙️ assert] a row another user created
    ✅ got: #t
  [🧪 test] a contributor changes the row they made
    [⚙️ assert] their own row is theirs to change
    ✅ got: #t
  [🧪 test] a contributor does not change a row somebody else made
    [⚙️ assert] someone else's row is refused
    ✅ got: #f
  [🧪 test] a row that names no creator is only the super grant's to change
    [⚙️ assert] a row with no creator is refused
    ✅ got: #f
    [⚙️ assert] while the super grant may still change it
    ✅ got: #t
ℹ️ info - ran 2 tasks in: 0 seconds, 3095 microseconds

running unit tests from file: error-pages-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] The classes an error page asks for
  [🧪 test] every class the page names is one the sheets define
    [⚙️ assert] and the page does name a fair few
    ✅ got: #t
+----------------------------------------------+
[📑 suite] What an error page says
  [🧪 test] a page is drawn in the tone its class of refusal is read in
    [⚙️ assert] a request that timed out has a page
    ✅ got: #t
  [🧪 test] an error page describes what the reader can do
    [⚙️ assert] a 500 offers to be tried again
    ✅ got: #t
    [⚙️ assert] a 404 points at what is still there
    ✅ got: #t
  [🧪 test] a page leads with a headline of its own
    [⚙️ assert] a 404 has a headline
    ✅ got: #t
    [⚙️ assert] and something to explain itself
    ✅ got: #t
    [⚙️ assert] the two codes do not share wording
    ✅ got: #t
    [⚙️ assert] nor do their explanations
    ✅ got: #t
  [🧪 test] a status with no wording of its own still describes itself
    [⚙️ assert] which is not the wording of a status that has one
    ✅ got: #t
    [⚙️ assert] and it still has an explanation
    ✅ got: #t
    [⚙️ assert] an unnamed status is named in its headline
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Rendering an error page
  [🧪 test] the page is serialized the way the rest of the site is
    [⚙️ assert] and is not escaped into nonsense
    ✅ got: #t
  [🧪 test] a reader who is signed in is not invited to sign in again
    [⚙️ assert] the navigation differs with the session
    ✅ got: #t
  [🧪 test] the page says which status it is
  [🧪 test] the wording is translated through the procedure it is given
    [⚙️ assert] and so does the detail
    ✅ got: #t
  [🧪 test] the page is a whole document of the site's own shell
    [⚙️ assert] and the footer
    ✅ got: #t
  [🧪 test] the reader is offered a way out
ℹ️ info - ran 3 tasks in: 0 seconds, 14459 microseconds

running unit tests from file: guitarpro-view-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] The section a version's tab is offered in
  [🧪 test] a tab whose header cannot be read says so and is still a download
    [⚙️ assert] the sentence
    ✅ got: #t
  [🧪 test] a version carrying no tab is offered none
    [⚙️ assert] and a version whose tab is not a tab at all
    ✅ got: ()
    [⚙️ assert] a field a reader never touched
    ✅ got: ()
  [🧪 test] and the offer to draw it here, which is a button and not a script
    [⚙️ assert] the button
    ✅ got: #t
  [🧪 test] the controls and the score are on the page before they are used
    [⚙️ assert] the place a score is drawn in
    ✅ got: #t
    [⚙️ assert] the controls start hidden
    ✅ got: #t
  [🧪 test] playing and stopping are offered once there is something to play
    [⚙️ assert] and stopping
    ✅ got: #t
    [⚙️ assert] playing
    ✅ got: #t
  [🧪 test] the words the player shows are the page's own
    [⚙️ assert] the state while loading is a place the script reaches
    ✅ got: #t
    [⚙️ assert] and when it cannot be drawn
    ✅ got: #t
    [⚙️ assert] while it loads
    ✅ got: #t
    [⚙️ assert] and the state that failed
    ✅ got: #t
    [⚙️ assert] once it is drawn
    ✅ got: #t
    [⚙️ assert] and the state once drawn
    ✅ got: #t
  [🧪 test] each version's section is named after the version it is
    [⚙️ assert] and not the first version's
    ✅ got: #t
  [🧪 test] a version carrying a tab offers it for download
    [⚙️ assert] and the name it is saved under
    ✅ got: #t
+----------------------------------------------+
[📑 suite] What a tab's own header is shown as
  [🧪 test] the numbers a header always carries are always listed
    [⚙️ assert] the tracks
    ✅ got: 3
  [🧪 test] a field its author left empty is not listed at all
    [⚙️ assert] a key with no name
    ✅ got: #f
  [🧪 test] a key is listed by the name of the key and not the count
    [⚙️ assert] two flats is B flat
    ✅ got: Bb
  [🧪 test] every field a tab's author filled in is listed
    [⚙️ assert] who tabbed it
    ✅ got: a tabber
  [🧪 test] a tempo is written as a reader reads it
    [⚙️ assert] a tempo in beats per minute
    ✅ got: 132 bpm
+----------------------------------------------+
[📑 suite] The name a tab is saved under
  [🧪 test] the characters a file name cannot hold are replaced
    [⚙️ assert] a song whose name holds a slash and a colon
    ✅ got: A-B- C-D-.gp5
  [🧪 test] the name says which song and which version it is
    [⚙️ assert] the two together
    ✅ got: A Song - v1.gp5
  [🧪 test] a name is still made of nothing
    [⚙️ assert] a version with no name of its own
    ✅ got: wikimusic-tab.gp5
    [⚙️ assert] and a song the page has not read yet
    ✅ got: wikimusic-tab.gp5
ℹ️ info - ran 3 tasks in: 0 seconds, 4125 microseconds

running unit tests from file: guitarpro-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] What the header of a Guitar Pro 5 file declares
  [🧪 test] a header written by a release after 5.0 is read the same way
    [⚙️ assert] the track count of a 5.1 file
    ✅ got: 5
    [⚙️ assert] the track count of a 5.0 file
    ✅ got: 5
    [⚙️ assert] the tempo of a 5.1 file
    ✅ got: 155
    [⚙️ assert] the tempo of a 5.0 file
    ✅ got: 155
  [🧪 test] every field of a written file is read back
    [⚙️ assert] the artist, two fields along
    ✅ got: an artist
    [⚙️ assert] the measures
    ✅ got: 42
    [⚙️ assert] the notice lines, in order
    ✅ got: (first notice second notice)
    [⚙️ assert] the tempo
    ✅ got: 132
    [⚙️ assert] the release
    ✅ got: (5 10)
    [⚙️ assert] the key it names
    ✅ got: Bb
    [⚙️ assert] the lyrics
    ✅ got: (the words of the song)
    [⚙️ assert] the title
    ✅ got: a title
    [⚙️ assert] the key signature
    ✅ got: -2
    [⚙️ assert] the tracks
    ✅ got: 3
  [🧪 test] a file whose counts cannot be true is refused
    [⚙️ assert] a negative measure count
    ✅ got: #f
  [🧪 test] a file of the right generation that is cut short is refused
    [⚙️ assert] a file missing its whole header
    ✅ got: #f
    [⚙️ assert] a header missing its last fields
    ✅ got: #f
  [🧪 test] a file that is not a readable Guitar Pro 5 file is answered with #f
    [⚙️ assert] nothing at all
    ✅ got: #f
    [⚙️ assert] a text file
    ✅ got: #f
    [⚙️ assert] a Guitar Pro 4 file
    ✅ got: #f
    [⚙️ assert] a pdf
    ✅ got: #f
  [🧪 test] the reader consumes exactly the header and nothing more
    [⚙️ assert] a file with no title
    ✅ got: 1056
    [⚙️ assert] a file with two notice lines
    ✅ got: 1092
    [⚙️ assert] the plain file
    ✅ got: 1063
    [⚙️ assert] a file with numbers at their widest
    ✅ got: 1063
    [⚙️ assert] a file of the earlier release
    ✅ got: 1058
    [⚙️ assert] a file with a long title
    ✅ got: 1093
  [🧪 test] a field whose two lengths disagree is refused
    [⚙️ assert] a string that claims a length it does not have
    ✅ got: #f
+----------------------------------------------+
[📑 suite] What a base64 text spells
  [🧪 test] the encoding a browser does round-trips through the decoder
    [⚙️ assert] a text whose bytes are not its characters
    ✅ got: a title with an accent: café
    [⚙️ assert] one whole group of three
    ✅ got: abc
    [⚙️ assert] one character, the shortest text that needs padding
    ✅ got: a
    [⚙️ assert] a word
    ✅ got: wikimusic
  [🧪 test] a text that is not base64 is refused rather than guessed at
    [⚙️ assert] a character outside the alphabet
    ✅ success catching error: misc-error: (#f base64-decode-string: not a base64 character ~S (*) #f)
    [⚙️ assert] a text that ends in a lone character
    ✅ success catching error: misc-error: (#f base64-decode-string: the text ends in a lone base64 character () #f)
  [🧪 test] a text broken across lines is read as one
    [⚙️ assert] whitespace between the characters is ignored
    ✅ got: hello
  [🧪 test] a text whose bytes are known decodes to them
    [⚙️ assert] three characters
    ✅ got: hi
    [⚙️ assert] six characters is two whole groups
    ✅ got: hello!
    [⚙️ assert] two characters
    ✅ got: h
    [⚙️ assert] five characters
    ✅ got: hello
+----------------------------------------------+
[📑 suite] What a data URL holds
  [🧪 test] a version's own file is read through its data URL
    [⚙️ assert] the tempo
    ✅ got: 90
    [⚙️ assert] the title
    ✅ got: through a data url
  [🧪 test] a data URL that holds something other than base64 holds no bytes
    [⚙️ assert] a base64 text that does not decode
    ✅ got: #f
    [⚙️ assert] a text data URL
    ✅ got: #f
  [🧪 test] a base64 data URL is one, and its bytes come back
    [⚙️ assert] the bytes after the comma
    ✅ got: hi
    [⚙️ assert] a file posted by a browser
    ✅ got: #t
  [🧪 test] an empty value is not a data URL
    [⚙️ assert] the empty string
    ✅ got: #f
    [⚙️ assert] nor is a path
    ✅ got: #f
+----------------------------------------------+
[📑 suite] The key a header names
  [🧪 test] a number no signature has is not named at all
    [⚙️ assert] one flat too many
    ✅ got: #f
    [⚙️ assert] and one sharp too many
    ✅ got: #f
  [🧪 test] each signature of the fifteen is named
    [⚙️ assert] no accidentals is C
    ✅ got: C
    [⚙️ assert] seven sharps is C sharp
    ✅ got: C#
    [⚙️ assert] one sharp is G
    ✅ got: G
    [⚙️ assert] seven flats is C flat
    ✅ got: Cb
    [⚙️ assert] two flats is B flat
    ✅ got: Bb
ℹ️ info - ran 4 tasks in: 0 seconds, 5468 microseconds

running unit tests from file: views-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] A page a reader is reading
  [🧪 test] the navigation is pinned while browsing
    [⚙️ assert] just below the top edge
    ✅ got: #t
  [🧪 test] the whole template carries the reading mode down to the bar
    [⚙️ assert] and every other page keeps it
    ✅ got: #t
  [🧪 test] the navigation is left to scroll away while reading
    [⚙️ assert] and it does not float above the text
    ✅ got: #f
+----------------------------------------------+
[📑 suite] The languages a page offers
  [🧪 test] a compiled catalog stands behind each offered language
  [🧪 test] each language is named in that language
  [🧪 test] every offered language is one this instance ships
    [⚙️ assert] the four catalogs are offered
    ✅ got: (en_US nl_NL es_ES ja_JP)
+----------------------------------------------+
[📑 suite] A page a browser can read
  [🧪 test] no non-void element is closed as a self-closed tag
    [⚙️ assert] each one is closed, not reopened
    ✅ got: #f
  [🧪 test] the body the caller passed is the body that is served
    [⚙️ assert] and it stands inside the document
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Where the shell reads its own files
  [🧪 test] an instance that serves its own reads them off itself
    [⚙️ assert] the stylesheet the page is laid out with
    ✅ got: #t
    [⚙️ assert] and the icon the tab is marked with
    ✅ got: #t
  [🧪 test] no file of the interface is somebody else's to serve
    [⚙️ assert] and none of the hosts a face was fetched from
    ✅ got: #f
    [⚙️ assert] and no typeface read from somebody's static assets
    ✅ got: #f
    [⚙️ assert] no font host
    ✅ got: #f
    [⚙️ assert] no icon CDN
    ✅ got: #f
  [🧪 test] a deployment that serves them elsewhere moves every one of them
    [⚙️ assert] the glyphs its controls wear
    ✅ got: #t
    [⚙️ assert] the stylesheet
    ✅ got: #t
    [⚙️ assert] the type the page is set in
    ✅ got: #t
    [⚙️ assert] and the icon, with no address left behind
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Choosing a language
  [🧪 test] the page states the language it is being read in
    [⚙️ assert] and a language not being read is not
    ✅ got: #f
  [🧪 test] every offered language is a row of the menu
  [🧪 test] the control posts the choice to the preference route
    [⚙️ assert] the menu is the posted field
    ✅ got: #t
  [🧪 test] the control names itself for a reader and for assistive tech
    [⚙️ assert] and says what it is
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The footer
  [🧪 test] no class is asked for twice
    [⚙️ assert] a clean class attribute
    ✅ got: #f
  [🧪 test] it states the version this instance runs
    [⚙️ assert] the version is named
    ✅ got: #t
  [🧪 test] it points at the code, the manual and the licence
    [⚙️ assert] where the source is
    ✅ got: #t
    [⚙️ assert] where the manual is
    ✅ got: #t
    [⚙️ assert] and where the licence is written out
    ✅ got: #t
  [🧪 test] it offers the way back to the top
    [⚙️ assert] and it is the one that scrolls
    ✅ got: #t
    [⚙️ assert] the control says what it does
    ✅ got: #t
ℹ️ info - ran 6 tasks in: 0 seconds, 4392 microseconds

running unit tests from file: about-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] What the page tells a crawler
  [🧪 test] the title names the community
    [⚙️ assert] the page is titled
    ✅ got: #t
  [🧪 test] the page says what kind of page it is
    [⚙️ assert] and names the community as its subject
    ✅ got: #t
    [⚙️ assert] the page states its own type
    ✅ got: #t
  [🧪 test] the description is the page's own one
    [⚙️ assert] a search result shows the community
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The word the link is offered in
  [🧪 test] es_ES has a word for it
    [⚙️ assert] the label is translated
    ✅ got: #t
  [🧪 test] nl_NL has a word for it
    [⚙️ assert] the label is translated
    ✅ got: #t
  [🧪 test] ja_JP has a word for it
    [⚙️ assert] the label is translated
    ✅ got: #t
  [🧪 test] en_US has a word for it
    [⚙️ assert] the label is translated
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The pages of an instance that asks for nothing
  [🧪 test] the about page asks for nothing
    [⚙️ assert] there is no control
    ✅ got: #f
    [⚙️ assert] and nothing about what the money pays for
    ✅ got: #f
    [⚙️ assert] and none of the words that are on one
    ✅ got: #f
  [🧪 test] and the front page asks for nothing either
    [⚙️ assert] and none of the words that are on one
    ✅ got: #f
    [⚙️ assert] there is no control
    ✅ got: #f
  [🧪 test] what the pages are for is still said
    [⚙️ assert] the promises are still made
    ✅ got: #t
    [⚙️ assert] and the front page still invites
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The words the two pages share
  [🧪 test] the one ask is the same control on both pages
    [⚙️ assert] and so does the front page
    ✅ got: #t
    [⚙️ assert] offered in the project's own words
    ✅ got: #t
    [⚙️ assert] the about page asks
    ✅ got: #t
  [🧪 test] both pages name the place the same way
    [⚙️ assert] and so does the front page
    ✅ got: #t
    [⚙️ assert] the about page says it
    ✅ got: #t
  [🧪 test] the account of the project belongs to the about page
    [⚙️ assert] the front page is still the collection
    ✅ got: #f
  [🧪 test] both pages close on the same invitation
    [⚙️ assert] and so does the front page
    ✅ got: #t
    [⚙️ assert] the about page invites
    ✅ got: #t
+----------------------------------------------+
[📑 suite] What the page says the project is
  [🧪 test] it names what the project is
    [⚙️ assert] the community is named
    ✅ got: #t
    [⚙️ assert] and what it works towards
    ✅ got: #t
  [🧪 test] it introduces the list of what a reader can do
    [⚙️ assert] the list is introduced
    ✅ got: #t
  [🧪 test] it says what the project is built on
    [⚙️ assert] the language is named
    ✅ got: #t
    [⚙️ assert] and so is the architecture
    ✅ got: #t
  [🧪 test] it ends on an invitation
    [⚙️ assert] the last line is the invitation
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The order the page says things in
  [🧪 test] the account of the place comes before the ask
    [⚙️ assert] nothing is asked for before anything is said
    ✅ got: #t
  [🧪 test] the promises keep the order they were written in
    [⚙️ assert] each one follows the one before it
    ✅ got: #t
  [🧪 test] the list stands under the sentence that introduces it
    [⚙️ assert] the introduction comes first
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The link to the page
  [🧪 test] every page offers it
    [⚙️ assert] the footer links to it
    ✅ got: #t
    [⚙️ assert] and says where it leads
    ✅ got: #t
+----------------------------------------------+
[📑 suite] The front page as a way in
  [🧪 test] the search control carries the words to the collection
    [⚙️ assert] and the field its words are read from
    ✅ got: #t
    [⚙️ assert] the form names the collection
    ✅ got: #t
  [🧪 test] the first screen says what is in the place
    [⚙️ assert] and it is read after it
    ✅ got: #t
    [⚙️ assert] the summary is read under the tagline
    ✅ got: #t
  [🧪 test] both shelves of the collection are shown, songs first
    [⚙️ assert] and so are the artists
    ✅ got: #t
    [⚙️ assert] with a way to the rest of them
    ✅ got: #t
    [⚙️ assert] in the order they are read
    ✅ got: #t
    [⚙️ assert] the songs are there
    ✅ got: #t
  [🧪 test] a page with no artists to show is still the whole page
    [⚙️ assert] no heading over nothing
    ✅ got: #f
+----------------------------------------------+
[📑 suite] What the page promises a reader can do
  [🧪 test] “Organize information with structured met…” is promised
    [⚙️ assert] it is an item of the list
    ✅ got: #t
    [⚙️ assert] and it is said once
    ✅ got: 1
  [🧪 test] “Contribute by adding new compositions, a…” is promised
    [⚙️ assert] it is an item of the list
    ✅ got: #t
    [⚙️ assert] and it is said once
    ✅ got: 1
  [🧪 test] “Discover music from diverse genres and t…” is promised
    [⚙️ assert] and it is said once
    ✅ got: 1
    [⚙️ assert] it is an item of the list
    ✅ got: #t
  [🧪 test] “Learn through curated resources that con…” is promised
    [⚙️ assert] it is an item of the list
    ✅ got: #t
    [⚙️ assert] and it is said once
    ✅ got: 1
ℹ️ info - ran 9 tasks in: 0 seconds, 16582 microseconds

running unit tests from file: sql-spec.scm, entrypoint: spec


ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] Sorting
  [🧪 test] a clause renders its column and its direction
    [⚙️ assert] a column with no direction renders bare
    ✅ got: "display_name"
    [⚙️ assert] two clauses, in order
    ✅ got: "views" DESC, "display_name" ASC
    [⚙️ assert] and a hyphen becomes an underscore there too
    ✅ got: "last_edited_at" DESC
  [🧪 test] both spellings of a sort are accepted
    [⚙️ assert] and nothing sorts by nothing
    ✅ got: ()
    [⚙️ assert] one clause, not yet wrapped
    ✅ got: ((display-name asc))
    [⚙️ assert] a clause list is left as it is
    ✅ got: ((views desc) (display-name asc))
  [🧪 test] a direction that is not a direction is an error
    [⚙️ assert] so an unknown word cannot reach the statement
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Rendering a statement for the driver
  [🧪 test] a value is rendered as a literal, and its quotes are doubled
    [⚙️ assert] so every quote it carries is paired
    ✅ got: 4
    [⚙️ assert] a string is quoted
    ✅ got: SELECT * FROM "songs" WHERE "display_name" = 'x'
    [⚙️ assert] a value that means to end the string cannot
    ✅ got: SELECT * FROM "songs" WHERE "display_name" = '''; DROP TABLE users; --'
    [⚙️ assert] an apostrophe inside a value is doubled
    ✅ got: SELECT * FROM "songs" WHERE "display_name" = 'O''Brien'
  [🧪 test] a statement string is passed through untouched
    [⚙️ assert] a statement with a quoted value of its own
    ✅ got: SELECT 'x'
    [⚙️ assert] a plain string
    ✅ got: SELECT 1
  [🧪 test] a number and a boolean are not quoted
    [⚙️ assert] a number is written as itself
    ✅ got: SELECT * FROM "songs" WHERE "views" = 3
    [⚙️ assert] a true value is written as 1
    ✅ got: SELECT * FROM "songs" WHERE "views" = 1
  [🧪 test] column names are quoted as identifiers
    [⚙️ assert] a hyphenated symbol is spelled underscore
    ✅ got: UPDATE "users" SET "password" = 'argon2id$x' WHERE "identifier" = 'id-1'
+----------------------------------------------+
[📑 suite] Matching a set of identifiers
  [🧪 test] an IN clause binds one value per identifier, in order
    [⚙️ assert] two identifiers
    ✅ got: SELECT * FROM "songs" WHERE "identifier" IN ('a', 'b')
  [🧪 test] and a NOT IN over an empty set matches everything
    [⚙️ assert] NOT IN () becomes a true predicate
    ✅ got: SELECT * FROM "songs" WHERE 1 = 1
  [🧪 test] an empty set matches nothing rather than everything
    [⚙️ assert] IN () becomes a false predicate
    ✅ got: SELECT * FROM "songs" WHERE 1 = 0
+----------------------------------------------+
[📑 suite] Matching a term
  [🧪 test] the search statement a builder cannot write is still kracht's
    [⚙️ assert] and the paging is written as numbers
    ✅ got: #t
  [🧪 test] a LIKE condition renders as LIKE, with its term bound
    [⚙️ assert] a wildcard term
    ✅ got: SELECT * FROM "songs" WHERE "display_name" LIKE '%x%'
+----------------------------------------------+
[📑 suite] Accounting for placeholders
  [🧪 test] a rendered statement holds no placeholder any more
    [⚙️ assert] and the rest of the statement is intact
    ✅ got: #t
  [🧪 test] the number of values has to match the number of placeholders
    [⚙️ assert] a placeholder with no value is refused, not left behind
    ✅ got: #t
    [⚙️ assert] a value too few is an error rather than a guess
    ✅ got: #t
    [⚙️ assert] a value too many is an error too
    ✅ got: #t
  [🧪 test] something that is neither a query nor a string is refused
    [⚙️ assert] a number is not a statement
    ✅ got: #t
    [⚙️ assert] nor is a symbol
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Shaping a row
  [🧪 test] only the named columns survive, in the named order
    [⚙️ assert] the columns decide, not the attributes
    ✅ got: ((views . 2) (display-name . x))
  [🧪 test] a row holding none of the columns is an error
    [⚙️ assert] an insert with no columns is refused
    ✅ got: #t
  [🧪 test] a column the attributes do not carry is simply left out
    [⚙️ assert] so the table's own default can stand
    ✅ got: ((display-name . x))
  [🧪 test] an attribute for an unlisted column cannot reach the statement
    [⚙️ assert] a key wikimusic never names is dropped
    ✅ got: ((display-name . x))
ℹ️ info - ran 6 tasks in: 0 seconds, 7967 microseconds

running unit tests from file: auth-spec.scm, entrypoint: spec

error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting token: misc-error: (#f ~A (open-envelope: not a bewaking envelope (expected BWK3).) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)error decrypting token: misc-error: (#f ~A (bewaking: the sealed data could not be opened: unknown, retired or wrong key, or the data was altered.) #f)
ℹ️ info - shuffled with seed 34900809 (replay with VERITAS_SEED=34900809)
+----------------------------------------------+
[📑 suite] The permissions column
  [🧪 test] anything that is not a list of permissions is an empty list
    [⚙️ assert] a string
    ✅ got: ()
    [⚙️ assert] a bare symbol
    ✅ got: ()
    [⚙️ assert] an empty column
    ✅ got: ()
    [⚙️ assert] no column at all
    ✅ got: ()
    [⚙️ assert] an unreadable column
    ✅ got: ()
  [🧪 test] the column is read, never run
    [⚙️ assert] and so does a lambda
    ✅ got: (lambda (x) x)
    [⚙️ assert] a form naming a procedure comes back as data
    ✅ got: (system rm -rf /)
  [🧪 test] a list of permissions is read as written
    [⚙️ assert] one permission
    ✅ got: (wikimusic:song-create)
    [⚙️ assert] several, in order
    ✅ got: (wikimusic:song-create wikimusic:artist-delete)
  [🧪 test] a row written before the change still yields its permissions
    [⚙️ assert] a quasiquoted list is unwrapped
    ✅ got: (wikimusic:song-create)
    [⚙️ assert] the long spelling of it too
    ✅ got: (wikimusic:song-create)
+----------------------------------------------+
[📑 suite] Passwords on file
  [🧪 test] a hash names the scheme it was derived with
    [⚙️ assert] and carries iterations, salt and key
    ✅ got: 4
  [🧪 test] the same password hashed twice is never the same hash
    [⚙️ assert] the salt is drawn fresh every time
    ✅ got: #t
  [🧪 test] the recorded cost is at least the cost that was chosen
    [⚙️ assert] pbkdf2 does at least 210000 iterations
    ✅ got: #t
+----------------------------------------------+
[📑 suite] Checking a password
  [🧪 test] the password a hash was derived from verifies
    [⚙️ assert] the preferred scheme round trips
    ✅ got: #t
    [⚙️ assert] the salt column no longer decides
    ✅ got: #t
    [⚙️ assert] and so does the fallback scheme
    ✅ got: #t
inspect the job's lambda expression
(lambda()
  `((task . ("maak-test" "pull-and-restart" "maak-deploy"))
    (daemon-name . "wikimusic-daemon")
    (project . "wikimusic")
    (branch-name . "trunk")
    (clone-url . "https://codeberg.org/jjba23/wikimusic.git")))