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>
71 lines
3.1 KiB
JavaScript
71 lines
3.1 KiB
JavaScript
// The town-crier leg's own tests, moved out of core's announceJobs.test.js in
|
|
// Phase 3 (MODULE_SYSTEM.md §2.7.1).
|
|
//
|
|
// Core keeps what it owns there — the backoff schedule, the parent-status rollup
|
|
// and the Discord leg — because those are the announce PIPELINE. What is here is
|
|
// this module's LEG: how a post becomes town-crier lines, and how the sidecar's
|
|
// answers classify into done / retry / terminal. The split is the same one the
|
|
// registry makes.
|
|
|
|
const { test } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
|
|
const { fakeCtx } = require('./_fakes')
|
|
require('../core').init(fakeCtx())
|
|
|
|
const townCrier = require('../utils/shardAnnounce')
|
|
|
|
// ── buildTownCrierText ───────────────────────────────────────────────────────
|
|
test('buildTownCrierText produces title, excerpt, and URL lines', () => {
|
|
const lines = townCrier.buildTownCrierText(
|
|
{ id: 7, title: 'Server Update', excerpt: 'Big things afoot.', body: null },
|
|
{ baseUrl: 'https://uom.example' },
|
|
)
|
|
assert.deepEqual(lines, ['Server Update', 'Big things afoot.', 'https://uom.example/site/news'])
|
|
})
|
|
|
|
test('buildTownCrierText falls back to a stripped body when excerpt is empty', () => {
|
|
const lines = townCrier.buildTownCrierText(
|
|
{ id: 1, title: 'T', excerpt: '', body: '<p>Hello <b>world</b></p>' },
|
|
{ baseUrl: 'https://uom.example' },
|
|
)
|
|
assert.equal(lines[1], 'Hello world')
|
|
})
|
|
|
|
test('buildTownCrierText clamps each line to the sidecar per-line cap', () => {
|
|
const longTitle = 'x'.repeat(500)
|
|
const lines = townCrier.buildTownCrierText(
|
|
{ id: 1, title: longTitle, excerpt: 'y'.repeat(500), body: null },
|
|
{ baseUrl: 'https://uom.example' },
|
|
)
|
|
for (const line of lines) assert.ok(line.length <= townCrier.MAX_LINE_LEN, `line too long: ${line.length}`)
|
|
assert.ok(lines[0].endsWith('…'))
|
|
assert.ok(lines.length <= townCrier.MAX_LINES)
|
|
})
|
|
|
|
test('buildTownCrierText omits the excerpt line when there is no excerpt or body', () => {
|
|
const lines = townCrier.buildTownCrierText(
|
|
{ id: 1, title: 'Only a title', excerpt: null, body: null },
|
|
{ baseUrl: 'https://uom.example' },
|
|
)
|
|
assert.deepEqual(lines, ['Only a title', 'https://uom.example/site/news'])
|
|
})
|
|
|
|
// ── classifyTownCrier ────────────────────────────────────────────────────────
|
|
test('town crier classify: 2xx is done', () => {
|
|
assert.equal(townCrier.classify({ ok: true, status: 200 }).outcome, 'done')
|
|
})
|
|
|
|
test('town crier classify: over-cap / auth / protocol errors are terminal (no retry)', () => {
|
|
for (const status of [400, 401, 409]) {
|
|
assert.equal(townCrier.classify({ ok: false, status }).outcome, 'terminal', `status ${status}`)
|
|
}
|
|
})
|
|
|
|
test('town crier classify: shard-transient and network errors retry', () => {
|
|
for (const status of [503, 504, 500, 0]) {
|
|
assert.equal(townCrier.classify({ ok: false, status }).outcome, 'retry', `status ${status}`)
|
|
}
|
|
})
|
|
|