const { test, beforeEach } = require('node:test') const assert = require('node:assert/strict') const shardIngest = require('../src/utils/shardIngest') // Protocol 3.0 vendor.listing / vendor.listing.remove routing. Same shape as // shardIngest.points.test.js: stubbed deps, asserting where the dispatcher sends // the frame and whether it is appended to the event log. function makeDeps() { const calls = { upserts: [], removes: [], appended: [], broadcast: [] } const noop = async () => {} return { calls, shardEvents: { append: async (row) => { calls.appended.push(row); return true } }, shardState: { // Present so any stray routing is a harmless no-op rather than a crash. clearOnline: noop, upsertOnline: noop, setOffline: noop, upsertHouse: noop, addEconomySample: noop, setRuleset: noop, upsertPointsBoard: noop, }, shardMarket: { upsertVendor: async (ev) => { calls.upserts.push(ev) }, removeVendor: async (serial) => { calls.removes.push(serial) }, }, shardLinks: { removeByAccount: noop }, uoLinkConfig: { recordStatus: noop }, broadcast: (ev) => { calls.broadcast.push(ev) }, pushDispatch: async () => {}, log: { warn() {}, info() {}, error() {} }, } } const FRAME = { kind: 'vendor.listing', t: 1000, serial: '0x40001234', shopName: "Darrow's Bargains", ownerSerial: '0x1A2B', ownerName: 'Darrow', location: { map: 'Trammel', x: 1421, y: 1699, z: 0, region: 'Britain', house: "Darrow's Villa" }, count: 2, total: 2, truncated: false, items: [ { serial: '0x40012ABC', itemId: 3922, hue: 0, amount: 1, price: 25000, name: null, cliloc: 1023721 }, { serial: '0x40012ABD', itemId: 7026, hue: 1157, amount: 3, price: 500, name: 'a shard sigil', cliloc: 1041243 }, ], } beforeEach(() => shardIngest.reset()) test('vendor.listing routes to the market model with the whole frame', async () => { const deps = makeDeps() await shardIngest.ingest(FRAME, deps) assert.equal(deps.calls.upserts.length, 1) const stored = deps.calls.upserts[0] assert.equal(stored.serial, '0x40001234') assert.equal(stored.location.region, 'Britain') assert.equal(stored.items.length, 2) }) test('vendor.listing.remove routes to removeVendor with the serial', async () => { const deps = makeDeps() await shardIngest.ingest({ kind: 'vendor.listing.remove', t: 2000, serial: '0x40001234' }, deps) assert.deepEqual(deps.calls.removes, ['0x40001234']) assert.equal(deps.calls.upserts.length, 0) }) // The market IS the state. One frame carries up to 250 listings and the sweep // re-emits a shop on any price change, so logging would turn shard_events into a // price history nobody reads — the strongest case of the three v3 kinds. test('neither market kind is appended to the event log', async () => { const deps = makeDeps() const a = await shardIngest.ingest(FRAME, deps) const b = await shardIngest.ingest({ kind: 'vendor.listing.remove', serial: '0x40001234' }, deps) assert.equal(a.logged, false) assert.equal(b.logged, false) assert.equal(deps.calls.appended.length, 0) assert.equal(shardIngest.LOGGED_KINDS.has('vendor.listing'), false) assert.equal(shardIngest.LOGGED_KINDS.has('vendor.listing.remove'), false) }) // Broadcast is unconditional at this layer — whether it actually reaches anyone // is shardBroadcast's call, and the market feature ships with its stream off. test('vendor.listing is handed to the broadcaster', async () => { const deps = makeDeps() await shardIngest.ingest(FRAME, deps) assert.equal(deps.calls.broadcast.length, 1) assert.equal(deps.calls.broadcast[0].kind, 'vendor.listing') }) test('a backfilled vendor.listing still stores but does not broadcast', async () => { const deps = makeDeps() await shardIngest.ingest(FRAME, { ...deps, fromBackfill: true }) assert.equal(deps.calls.upserts.length, 1) assert.equal(deps.calls.broadcast.length, 0) }) // The reconnect backfill replays the whole index through this path, so a single // bad vendor must not abort it. test('an upsertVendor failure does not throw or stop the broadcast', async () => { const deps = makeDeps() deps.shardMarket.upsertVendor = async () => { throw new Error('db down') } const r = await shardIngest.ingest(FRAME, deps) assert.equal(r.logged, false) assert.equal(deps.calls.broadcast.length, 1) }) // Each vendor is its own row; the frame is authoritative for that vendor only. test('two vendors are stored independently', async () => { const deps = makeDeps() await shardIngest.ingest(FRAME, deps) await shardIngest.ingest({ ...FRAME, serial: '0x40009999', shopName: 'Second Shop' }, deps) assert.deepEqual(deps.calls.upserts.map((v) => v.serial), ['0x40001234', '0x40009999']) })