// Point the DB at a closed port BEFORE requiring the controllers (their models // build the pool). Every model call is monkeypatched, so no query runs; // db.close() at the end releases the pool so the process exits cleanly. process.env.DB_HOST = '127.0.0.1' process.env.DB_PORT = '59999' const { test, after, afterEach } = require('node:test') const assert = require('node:assert/strict') // The spawn atlas API, public and admin. What is worth asserting here is not the // SQL (that is the parser suite's job) but the contracts the two surfaces make: // // • the public reads project through the visibility framework — v3.md §3.6.1's // standing rule is that a read path returning shard data and not calling // projectFeature is a bug, and `atlas` declaring no sensitive fields TODAY is // exactly why the call has to be there before one does; // • the public /meta route reports the game world only, never the operator's // filesystem — the ServUO path, the per-file hashes and any pending refresh // stay on the admin route; // • a missing creature is a 404, not an empty 200; // • an unreadable ServUO tree is a 200 carrying `status: 'unavailable'`, NOT a // 500. The refresh contract reports outcomes rather than throwing (so boot is // never blocked by a bad tree), and the admin needs to be told what is wrong // with their path; // • a model failure degrades to a 500 rather than a thrown/uncaught error. const pub = require('../src/router/v1/public/atlas.controller') const admin = require('../src/router/v1/admin/shardAtlas.controller') const atlas = require('../src/model/shardAtlas/shardAtlas.model') const activity = require('../src/model/activity/activity.model') const visibility = require('../src/utils/shardVisibility') const db = require('../src/utils/db') after(() => db.close()) // Stub the visibility MODEL rather than the util's exports: project() calls the // module-internal getConfig, which an exports-level stub would not intercept — it // would hit the closed DB port and cost a ~10s pool timeout per test before // falling back to these same defaults. const visibilityModel = require('../src/model/shardVisibility/shardVisibility.model') visibilityModel.listAll = async () => [] // no overrides ⇒ compiled defaults visibility.viewerLevel = async (req) => req?.viewerLevel || 'anonymous' // The admin controller logs every action; keep it off the DB. activity.log = async () => {} function mockRes() { return { statusCode: 200, body: null, status(c) { this.statusCode = c return this }, json(b) { this.body = b return this }, } } const originals = { searchCreatures: atlas.searchCreatures, getCreature: atlas.getCreature, listRegions: atlas.listRegions, listLandmarks: atlas.listLandmarks, listChampions: atlas.listChampions, publicMeta: atlas.publicMeta, status: atlas.status, refresh: atlas.refresh, approvePending: atlas.approvePending, rejectPending: atlas.rejectPending, setServuoPath: atlas.setServuoPath, } afterEach(() => Object.assign(atlas, originals)) // ── Public reads ──────────────────────────────────────────────────────── test('getCreatures passes the search through and returns the page shape', async () => { let seen = null atlas.searchCreatures = async (opts) => { seen = opts return { total: 1, limit: 50, offset: 0, creatures: [{ slug: 'lizardman', name: 'Lizardman' }] } } const res = mockRes() await pub.getCreatures({ query: { q: ' lizard ', facet: 'Felucca', limit: '10', offset: '20' } }, res) assert.deepEqual(seen, { q: 'lizard', facet: 'Felucca', limit: 10, offset: 20 }) assert.equal(res.body.total, 1) assert.equal(res.body.creatures[0].slug, 'lizardman') }) test('getCreatures falls back to the documented defaults when nothing is passed', async () => { let seen = null atlas.searchCreatures = async (opts) => { seen = opts return { total: 0, limit: 50, offset: 0, creatures: [] } } await pub.getCreatures({ query: {} }, mockRes()) assert.deepEqual(seen, { q: '', facet: '', limit: 50, offset: 0 }) }) test('an unknown creature is a 404, not an empty 200', async () => { atlas.getCreature = async () => null const res = mockRes() await pub.getCreature({ params: { slug: 'nosuchthing' }, query: {} }, res) assert.equal(res.statusCode, 404) }) test('getCreature returns places and spawners, and `points` stays the COUNT', async () => { atlas.getCreature = async () => ({ slug: 'lizardman', name: 'Lizardman', total: 214, points: 62, places: [{ facet: 'Trammel', label: 'Shrines', spawners: 7, maxAlive: 21 }], spawners: [{ id: 1, facet: 'Trammel', label: 'Shrines', x: 1, y: 2 }], spawnersTruncated: false, alsoHere: [], }) const res = mockRes() await pub.getCreature({ params: { slug: 'lizardman' }, query: {} }, res) // The list route uses `points` as a number; the detail route must not quietly // turn the same key into an array. assert.equal(typeof res.body.points, 'number') assert.ok(Array.isArray(res.body.spawners)) assert.equal(res.body.places[0].label, 'Shrines') }) // ── The projection rule (§3.6.1) ──────────────────────────────────────── test('public reads run through projectFeature, so a locked field can never survive', async () => { // `atlas` declares no sensitive fields, so nothing here is stripped by a // FEATURE rule. acct/webId are stripped anyway — they are locked by meaning, // for every feature, and this is what proves the read path projects at all. atlas.searchCreatures = async () => ({ total: 1, limit: 50, offset: 0, creatures: [{ slug: 'lizardman', name: 'Lizardman', acct: 'someacct', ownerWebId: 7 }], }) const res = mockRes() await pub.getCreatures({ query: {}, viewerLevel: 'anonymous' }, res) const row = res.body.creatures[0] assert.equal(row.name, 'Lizardman') assert.ok(!('acct' in row), 'acct must never reach an anonymous caller') assert.ok(!('ownerWebId' in row), 'a flattened webId spelling is locked too') }) test('getMeta reports the game world only — never the operator’s filesystem', async () => { // The model is what enforces this; the assertion documents the boundary so a // future "just return status() here" shortcut fails loudly. atlas.publicMeta = async () => ({ importedAt: '2026-07-28T00:00:00.000Z', generatedAt: '2026-07-28T00:00:00.000Z', counts: { points: 6455, creatures: 800 }, facets: ['Felucca', 'Trammel'], }) const res = mockRes() await pub.getMeta({ query: {} }, res) assert.deepEqual(Object.keys(res.body).sort(), ['counts', 'facets', 'generatedAt', 'importedAt']) assert.ok(!('path' in res.body)) assert.ok(!('pending' in res.body)) }) test('a model failure degrades to a 500 rather than throwing', async () => { atlas.listChampions = async () => { throw new Error('table is gone') } const res = mockRes() await pub.getChampions({ query: {} }, res) assert.equal(res.statusCode, 500) }) // ── Admin ─────────────────────────────────────────────────────────────── test('an unreadable tree answers 200 with the reason, not a 500', async () => { atlas.refresh = async () => ({ status: 'unavailable', reason: 'no Spawns directory', path: '/bad' }) const res = mockRes() await admin.importAtlas({ body: {}, user: { id: 1 } }, res) assert.equal(res.statusCode, 200) assert.equal(res.body.status, 'unavailable') assert.equal(res.body.reason, 'no Spawns directory') }) test('import passes `force` through and coerces it to a boolean', async () => { let seen = null atlas.refresh = async (opts) => { seen = opts return { status: 'unchanged' } } await admin.importAtlas({ body: { force: true }, user: { id: 1 } }, mockRes()) assert.deepEqual(seen, { force: true }) }) test('approve applies a staged refresh (facet loss included)', async () => { let called = false atlas.approvePending = async () => { called = true return { status: 'imported', removedFacets: ['Malas'], counts: { points: 6162 } } } const res = mockRes() await admin.approve({ user: { id: 1 } }, res) assert.ok(called) assert.equal(res.body.status, 'imported') }) test('rejecting when nothing is staged is a 404', async () => { atlas.rejectPending = async () => ({ status: 'none' }) const res = mockRes() await admin.reject({ user: { id: 1 } }, res) assert.equal(res.statusCode, 404) }) test('setPath trims, persists, and answers with fresh status — it does not import', async () => { let saved = null let imported = false atlas.setServuoPath = async (value) => { saved = value } atlas.refresh = async () => { imported = true return { status: 'imported' } } atlas.status = async () => ({ configured: true, path: '/srv/servuo', treeReadable: true }) const res = mockRes() await admin.setPath({ body: { path: ' /srv/servuo ' }, user: { id: 3 } }, res) assert.equal(saved, '/srv/servuo') assert.equal(imported, false, 'changing the path must not reload the atlas as a side effect') assert.equal(res.body.path, '/srv/servuo') }) test('setPath accepts a blank path (clearing it turns the atlas off)', async () => { let saved = 'unset' atlas.setServuoPath = async (value) => { saved = value } atlas.status = async () => ({ configured: false, path: '' }) const res = mockRes() await admin.setPath({ body: {}, user: { id: 3 } }, res) assert.equal(saved, '') assert.equal(res.statusCode, 200) })