✔ zoned uses the IANA zone across DST (25.948049ms)
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (59.385534ms)
✔ two blocking decisions get two DMs with different nonces (48.096062ms)
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.210986ms)
✔ a failed DM is journaled, backs off, and is retried until it lands (56.248398ms)
✖ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (61.968033ms)
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (62.44892ms)
✖ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (55.157105ms)
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (63.63229ms)
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (60.85209ms)
✔ an inbox read failure is logged and the next poll retries (2.854273ms)
✔ no Discord id reaches the journal or the log (52.426152ms)
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (11.22238ms)
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (16.275646ms)
✔ the journal: a whole file that is one torn line truncates to empty (10.011137ms)
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.931897ms)
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.724113ms)
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (2.795814ms)
✔ the journal: a symlinked directory refuses and says it is a link (0.37236ms)
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.666603ms)
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.399524ms)
✔ digest content stays within Discord's 2000 characters (0.296185ms)
✔ runLoop never overlaps ticks and stops after the one in flight (111.743409ms)
ℹ tests 23
ℹ suites 0
ℹ pass 21
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 775.811359

✖ failing tests:

test at packages/cli/tests/notifier.test.mjs:112:1
✖ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (61.968033ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
  + actual - expected
  
  + []
  - [
  -   'notify: dm fa665b80 refused 5 times; gave up, not retried'
  - ]
  
      at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r45/wt/packages/cli/tests/notifier.test.mjs:133:10)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: [],
    expected: [ 'notify: dm fa665b80 refused 5 times; gave up, not retried' ],
    operator: 'deepStrictEqual',
    diff: 'simple'
  }

test at packages/cli/tests/notifier.test.mjs:160:1
✖ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (55.157105ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
  
  0 !== 1
  
      at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r45/wt/packages/cli/tests/notifier.test.mjs:169:10)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: 0,
    expected: 1,
    operator: 'strictEqual',
    diff: 'simple'
  }
