test: re-point core's suite at what core still owns
All checks were successful
PR Checks / bot-install (pull_request) Successful in 18s
PR Checks / client-build (pull_request) Successful in 27s
PR Checks / server-tests (pull_request) Successful in 29s

25 of 82 test files left with the module. Three that core keeps needed splitting
rather than moving, and the split is the boundary in each case.

announceJobs.test.js keeps the announce PIPELINE -- the shared backoff schedule,
the parent-status rollup, core's Discord leg -- and loses the town-crier text
building and classification, which are a module's leg. pushDispatch.test.js
keeps the SSRF guard and publish() delivering a content-free tickle, and loses
mapShardEvent and the shard fan-out, which are a module's catalog.

playerRouteAccess.test.js is the one worth explaining. It guards a real past bug
-- an admin 403'd off their own characters -- and it did so through
/player/shard/accounts, which is now module-owned. The guarantee it protects is
CORE's, though: /player/* is role-agnostic self-service, staff are a superset of
players. So it stays here and asserts that through /player/appeals, a core route
with the same gate. Moving it would have left core with no test of its own tier
rule, which is precisely what regressed once before.

The remaining updates are core's own tests catching up: ctx has four more
members, registerCore now registers only what core owns (one stream, one leg, no
filled slot), and the extension-slot test asks for the DECLARED slot's router
rather than the filled one, since core declares it and a module fills it. The
gated-surface floor drops from >100 to >50 -- it is there so a filter matching
nothing fails loudly, not to track core's exact route count.

616 core tests and 160 client tests pass; the module's own suite is 351.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 12:08:26 -05:00
committed by Claude
parent 39d731d87a
commit f5e6025dcc
6 changed files with 75 additions and 206 deletions

View File

@@ -41,17 +41,16 @@ function tryApply(owner, build) {
// ── Core goes through the same door ────────────────────────────────────────
test('registerCore registers core AND the not-yet-extracted shard content', () => {
test('registerCore registers exactly what core owns, and nothing else', () => {
registries.registerCore()
const ids = registries.allStreams().map((s) => s.id)
// Core's one stream first, then the seven that leave with module-uo.
assert.equal(ids[0], 'news.post')
assert.equal(ids.length, 8)
assert.ok(ids.includes('vendor.sale'))
assert.deepEqual(registries.announceLegIds(), ['discord', 'towncrier'])
assert.equal(registries.slotFilledBy('admin.users.detail'), 'core')
// One stream, 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'])
assert.deepEqual(registries.announceLegIds(), ['discord'])
assert.equal(registries.slotFilledBy('admin.users.detail'), null)
assert.equal(registries.isCoreRegistered(), true)
})
@@ -63,15 +62,26 @@ test('registerCore is idempotent — a second call registers nothing twice', ()
})
test('the wire shape of a stream survives registration', () => {
registries.registerCore()
const personal = registries.allStreams().find((s) => s.id === 'vendor.sale')
// Asserted against a REGISTERED stream rather than a core one, because the
// shape is what a module hands over and core republishes. `vendor.sale` used
// to be the subject here and is module-uo's now; a synthetic registration
// tests the same contract without core needing a personal stream of its own.
assert.equal(tryApply('uo', (api) => api.registerNotificationStreams([{
id: 'uo.vendorsale',
label: 'Vendor sales',
description: 'One of your vendors sold something.',
personal: true,
requiresLinkedAccount: true,
}])), null)
const personal = registries.allStreams().find((s) => s.id === 'uo.vendorsale')
// Two booleans, not the contract's single `scope`: this object is the body of
// GET /auth/me/notifications/streams and a shipped Android client reads both.
assert.equal(personal.personal, true)
assert.equal(personal.requiresLinkedAccount, true)
assert.ok(personal.description.length > 0)
assert.ok(registries.personalStreams().has('vendor.sale'))
assert.equal(registries.isValidStream('vendor.sale'), true)
assert.ok(registries.personalStreams().has('uo.vendorsale'))
assert.equal(registries.isValidStream('uo.vendorsale'), true)
assert.equal(registries.isValidStream('nope.nope'), false)
})
@@ -185,10 +195,12 @@ test('only a declared slot can be filled, and only once', () => {
assert.throws(() => api.registerExtension('admin.invented', router), /unknown extension slot/)
assert.throws(() => api.registerExtension('admin.users.detail', 'not a router'), /is not a router/)
registries.registerCore() // core fills it
// Core no longer fills it — module-uo does. Two registrants racing for the
// same slot is still the case worth testing, so the first fill is a module's.
assert.equal(tryApply('uo', (a) => a.registerExtension('admin.users.detail', router)), null)
assert.match(
tryApply('uo', (a) => a.registerExtension('admin.users.detail', router)),
/already filled by "core"/,
tryApply('rust', (a) => a.registerExtension('admin.users.detail', router)),
/already filled by "uo"/,
)
})
@@ -208,9 +220,11 @@ test('the filled slots router is findable in the live app, at the resource pa
const { findMountPrefix } = require('../swagger/slotSpecs')
/* eslint-enable global-require */
const [slot] = registries.filledSlots()
assert.ok(slot, 'app.js must have registered core, filling the slot')
assert.equal(slot.slot, 'admin.users.detail')
assert.ok(slot.specFile, 'core names the file its slot router is generated from')
assert.equal(findMountPrefix(app._router.stack, slot.router), '/api/v1/admin/users/:id')
// The slot is DECLARED by core and filled by whichever module is installed —
// none, in core's own test run. What must keep working regardless is the
// recovery of its mount prefix from the live stack, because that is what
// slotSpecs.js needs and what fails silently when it breaks.
const router = registries.declaredSlotRouter('admin.users.detail')
assert.ok(router, 'core declares the slot at require time, filled or not')
assert.equal(findMountPrefix(app._router.stack, router), '/api/v1/admin/users/:id')
})