test(teams): the refusals, which is most of what a notification feature is

A notification feature is mostly things that correctly do NOT happen, and each of
these is invisible until it goes wrong in production: a departed member and a
revoked guest are not recipients; a mute subtracts per Team and leaves the user's
other Teams alone; the author of a post never receives the notification about it;
forums switched off silences the forum streams including the digest; a Team's
first roster wakes nobody; a failed send does not stamp `last_digest_at`.

Two real defects came out of writing them.

`Number(null)` is 0 and 0 is an integer, so a null in a caller's id list survived
`filter(Number.isInteger)` and rode into an IN clause as user id 0. No row has id
0, so it was harmless — which is exactly why it would never have been noticed.
Fixed in all three places that filter ids.

`recipientIds: db.recipientIds` in the model captured the function OBJECT at
require time, so the layer below could never be substituted. That is not only
untestable; it means the model was not really the seam it claimed to be. Wrapped
so `db.x` resolves at call time.

The registries catalog assertion is now an exact five-element list, so a
shard-content stream creeping back into core's registration fails here rather
than shipping.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-18 14:35:23 -05:00
parent b458c1f46f
commit 5fa88baa0a
7 changed files with 719 additions and 2 deletions

View File

@@ -44,11 +44,22 @@ function tryApply(owner, build) {
test('registerCore registers exactly what core owns, and nothing else', () => {
registries.registerCore()
// One stream, one leg, no filled slot. Before Phase 3 this was eight streams,
// Five streams, one leg, no filled slot. Before Phase 3 this was eight streams,
// two legs and a core-filled `admin.users.detail` — core was holding shard
// CONTENT so the seam would be exercised on every boot before a module first
// used it. module-uo registers all of it now, through the same door.
assert.deepEqual(registries.allStreams().map((s) => s.id), ['news.post'])
//
// The four `team.*` streams arrived with Teams phase 6 and ARE core's: a module
// supplies who is in a Team, but who may be told about it is the access
// resolver's answer. Asserted as an exact list so a shard-content stream
// creeping back into core's registration fails here rather than shipping.
assert.deepEqual(registries.allStreams().map((s) => s.id), [
'news.post',
'team.member.joined',
'team.leadership.changed',
'team.forum.post',
'team.announcement',
])
assert.deepEqual(registries.announceLegIds(), ['discord'])
assert.equal(registries.slotFilledBy('admin.users.detail'), null)
assert.equal(registries.isCoreRegistered(), true)