✔ zoned uses the IANA zone across DST (13.576673ms)
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (59.501778ms)
✔ two blocking decisions get two DMs with different nonces (53.051795ms)
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.204649ms)
✔ a failed DM is journaled, backs off, and is retried until it lands (47.675935ms)
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (46.418663ms)
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (53.547704ms)
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (46.317859ms)
✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (108.858391ms)
✖ a restart after the second refusal does not send before that refusal's 30 min are up (79.688197ms)
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (55.259672ms)
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (42.118885ms)
✔ an inbox read failure is logged and the next poll retries (0.64199ms)
✔ no Discord id reaches the journal or the log (46.664543ms)
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (10.28155ms)
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (14.793831ms)
✔ the journal: a whole file that is one torn line truncates to empty (9.68584ms)
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.597578ms)
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.571636ms)
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.872573ms)
✔ the journal: a symlinked directory refuses and says it is a link (0.302665ms)
✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.455729ms)
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.364107ms)
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.452604ms)
✔ digest content stays within Discord's 2000 characters (0.270363ms)
✔ runLoop never overlaps ticks and stops after the one in flight (110.198965ms)
ℹ tests 26
ℹ suites 0
ℹ pass 25
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 867.94662

✖ failing tests:

test at packages/cli/tests/notifier.test.mjs:201:1
✖ a restart after the second refusal does not send before that refusal's 30 min are up (79.688197ms)
  AssertionError [ERR_ASSERTION]: nothing between the restart and +60 min
  + actual - expected
  
    [
  +   31.5
  -   60
    ]
  
      at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r45b/wt/packages/cli/tests/notifier.test.mjs:208:10)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: [ 31.5 ],
    expected: [ 60 ],
    operator: 'deepStrictEqual',
    diff: 'simple'
  }
