22 test files moved from core, plus the two that were split out of files core keeps. 351 tests pass. One change runs through every moved test, and it is the boundary rather than a chore: core internals can no longer be stubbed by requiring them, because there are none to require. `../utils/db` and `../model/settings` do not exist here. What a test controls instead is the ctx core would have handed over, installed once by test/_setup.js -- which is a better seam anyway, since it is exactly the surface the contract promises and nothing wider. The ctx _setup installs is deliberately unfrozen. Core freezes what it hands a module and entry.test.js still asserts against a frozen one; but a test that needs settings.get to return a path has to be able to say so. Two tests changed SHAPE, and that is the boundary too. fromShardEvent used to assert through publish() into pushDevices and a captured fetch -- which endpoints were hit, how many requests went out. None of that is this module's any more: publish is ctx.push.publish, and the device registry and the relay are behind it. Reaching for them from here would be reaching past ctx. What remains is what the module owns and is the part worth guarding: a game account resolves to a website user, a personal target that resolves to nobody is dropped rather than published, and a sensitive kind never reaches publish at all. Co-Authored-By: Claude <noreply@anthropic.com>
122 lines
5.3 KiB
JavaScript
122 lines
5.3 KiB
JavaScript
const { test, beforeEach } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
|
|
const shardIngest = require('../utils/shardIngest')
|
|
|
|
// Stub deps recording the Protocol 2.0 board calls the dispatcher makes. Only the
|
|
// methods the tested kinds touch need to be real; the rest are no-op async so
|
|
// ingest() never throws on an unrelated kind.
|
|
function makeDeps() {
|
|
const calls = {
|
|
guildUpsert: [], guildRemove: [],
|
|
governorUpsert: [],
|
|
presenceSet: [],
|
|
houseRegistry: [], houseRemove: [],
|
|
linkRemove: [],
|
|
appended: [], broadcast: [],
|
|
}
|
|
const noop = async () => {}
|
|
return {
|
|
calls,
|
|
shardEvents: { append: async (row) => { calls.appended.push(row); return true } },
|
|
shardState: {
|
|
upsertGuild: async (ev) => { calls.guildUpsert.push(ev) },
|
|
removeGuild: async (id) => { calls.guildRemove.push(id) },
|
|
upsertGovernor: async (ev) => { calls.governorUpsert.push(ev) },
|
|
setPresence: async (ev) => { calls.presenceSet.push(ev) },
|
|
upsertHouseRegistry: async (ev) => { calls.houseRegistry.push(ev) },
|
|
removeHouse: async (serial) => { calls.houseRemove.push(serial) },
|
|
// Present so any stray routing is a harmless no-op.
|
|
clearOnline: noop, upsertOnline: noop, setOffline: noop, upsertHouse: noop,
|
|
addEconomySample: noop,
|
|
},
|
|
shardLinks: { removeByAccount: async (account) => { calls.linkRemove.push(account) } },
|
|
uoLinkConfig: { recordStatus: noop },
|
|
broadcast: (ev) => { calls.broadcast.push(ev) },
|
|
// No-op push fan-out so ingest() stays hermetic (no real relay/DB).
|
|
pushDispatch: async () => {},
|
|
log: { warn() {}, info() {}, error() {} },
|
|
}
|
|
}
|
|
|
|
beforeEach(() => shardIngest.reset())
|
|
|
|
test('guild.update routes to upsertGuild and is not logged; guild.remove routes to removeGuild', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest({ kind: 'guild.update', id: 1042, name: 'TSH', t: 1 }, deps)
|
|
assert.equal(deps.calls.guildUpsert.length, 1)
|
|
assert.equal(deps.calls.guildUpsert[0].id, 1042)
|
|
assert.equal(r.logged, false) // board state, not appended to shard_events
|
|
await shardIngest.ingest({ kind: 'guild.remove', id: 1042, t: 2 }, deps)
|
|
assert.deepEqual(deps.calls.guildRemove, [1042])
|
|
})
|
|
|
|
test('guild.join is appended to the event log (real-time joins feed) and broadcast', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest(
|
|
{ kind: 'guild.join', id: 1042, who: { name: 'Bran' }, t: 3 }, deps)
|
|
assert.equal(r.logged, true)
|
|
assert.equal(deps.calls.appended.length, 1)
|
|
assert.equal(deps.calls.appended[0].kind, 'guild.join')
|
|
assert.equal(deps.calls.broadcast.length, 1)
|
|
})
|
|
|
|
test('city.update routes to upsertGovernor (which also captures term history)', async () => {
|
|
const deps = makeDeps()
|
|
await shardIngest.ingest(
|
|
{ kind: 'city.update', city: 'Britain', governor: { serial: '0x1', name: 'Darrow' }, t: 4 }, deps)
|
|
assert.equal(deps.calls.governorUpsert.length, 1)
|
|
assert.equal(deps.calls.governorUpsert[0].city, 'Britain')
|
|
})
|
|
|
|
test('presence.online routes to setPresence and is not logged', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest(
|
|
{ kind: 'presence.online', count: 42, byRegion: { Britain: 18 }, t: 5 }, deps)
|
|
assert.equal(deps.calls.presenceSet.length, 1)
|
|
assert.equal(deps.calls.presenceSet[0].count, 42)
|
|
assert.equal(r.logged, false)
|
|
})
|
|
|
|
test('house.update routes to upsertHouseRegistry; house.remove routes to removeHouse', async () => {
|
|
const deps = makeDeps()
|
|
await shardIngest.ingest({ kind: 'house.update', serial: '0x40001234', name: 'Anvil', t: 6 }, deps)
|
|
assert.equal(deps.calls.houseRegistry.length, 1)
|
|
assert.equal(deps.calls.houseRegistry[0].serial, '0x40001234')
|
|
await shardIngest.ingest({ kind: 'house.remove', serial: '0x40001234', t: 7 }, deps)
|
|
assert.deepEqual(deps.calls.houseRemove, ['0x40001234'])
|
|
})
|
|
|
|
test('region.enter is broadcast-only — not logged, no state side effect', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest(
|
|
{ kind: 'region.enter', from: 'Britain', to: 'Despise', who: { name: 'Darrow' }, t: 8 }, deps)
|
|
assert.equal(r.logged, false)
|
|
assert.equal(deps.calls.appended.length, 0)
|
|
assert.equal(deps.calls.broadcast.length, 1) // still surfaced live
|
|
})
|
|
|
|
test('account.unlinked reconciles the local link mirror and is logged', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest(
|
|
{ kind: 'account.unlinked', origin: 'in-game', account: 'bob', websiteUserId: '9931', t: 9 }, deps)
|
|
assert.deepEqual(deps.calls.linkRemove, ['bob']) // mirror dropped
|
|
assert.equal(r.logged, true) // provisioning audit trail
|
|
assert.equal(deps.calls.appended[0].kind, 'account.unlinked')
|
|
})
|
|
|
|
test('account.audit is logged (provisioning history) but has no state side effect', async () => {
|
|
const deps = makeDeps()
|
|
const r = await shardIngest.ingest(
|
|
{ kind: 'account.audit', origin: 'web', action: 'create', actor: 'web:jane', target: 'bob', t: 10 }, deps)
|
|
assert.equal(r.logged, true)
|
|
assert.equal(deps.calls.linkRemove.length, 0)
|
|
assert.equal(deps.calls.appended[0].kind, 'account.audit')
|
|
})
|
|
|
|
test('account.audit / account.unlinked are NOT on the public SSE allowlist', () => {
|
|
const broadcast = require('../utils/shardBroadcast')
|
|
assert.equal(broadcast.PUBLIC_KINDS.has('account.audit'), false)
|
|
assert.equal(broadcast.PUBLIC_KINDS.has('account.unlinked'), false)
|
|
})
|