feat(rust): Guarded Crate — feed, map, Warden, Place a guarded crate (PLAN_REDESIGNS §14.3.5)
All checks were successful
PR Checks / client-build (pull_request) Successful in 30s
PR Checks / server-tests (pull_request) Successful in 34s
PR Checks / frozen-manifest (pull_request) Successful in -1m34s

Stage 6 of the §14.3 integration plan, on protocol 14. Spec: docs
rust-link/PROTOCOL.md §20.9.

- gc.started and gc.ended public, gc.failed staff's; guardedCrate.js reads
  each row per viewer (the clearer's name for the presence audience, Steam id,
  position and key for staff, `won` for everyone).
- Triggers rust.gc.started and rust.gc.ended (a crate a player cleared only).
- The step rust.gc.place: an event from rust.options.gc_events and a world
  call's location; refused at verify for Guarded Crate absent, NpcSpawn missing
  (D364), console unavailable, or an unknown event. Kept (D373); the run log's
  success line says so (D380). Budget rust.gc.crates.
- Warden (D375): guarded_won, title category guardedwon, Crates cleared column.
- Map marker, server settings line, swagger, engagement manifest, bundle, README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-15 12:26:50 -05:00
parent 140bc67fe1
commit da1d74bdff
28 changed files with 1230 additions and 24 deletions

View File

@@ -163,6 +163,12 @@ test('the classification covers exactly the event kinds the protocol defines, th
'dt.started',
'dt.won',
'dt.ended',
// Protocol 14 (§20.9): Guarded Crate. The landing and the clearing are
// public, read per viewer by `guardedCrate.js`; the timeout is staff's
// (PLAN_REDESIGNS §14.3.5).
'gc.started',
'gc.ended',
'gc.failed',
]
assert.deepEqual([...catalogue.ALL_KINDS].sort(), [...PROTOCOL_4].sort())
@@ -192,6 +198,11 @@ test('the classification covers exactly the event kinds the protocol defines, th
assert.equal(catalogue.isPresence(kind), false)
}
assert.equal(catalogue.isPublic('dt.ended'), false, 'a treasure event ending is a staff line')
for (const kind of ['gc.started', 'gc.ended']) {
assert.equal(catalogue.isPublic(kind), true)
assert.equal(catalogue.isPresence(kind), false)
}
assert.equal(catalogue.isPublic('gc.failed'), false, 'a guarded crate timing out is a staff line')
})
test('every kind that names a player who was on is behind the presence setting', () => {
@@ -217,6 +228,7 @@ test('every kind that names a player who was on is behind the presence setting',
// And what is left names nobody — a world event included, once `worldEvents.shapeFor`
// has taken its names off for a viewer below the presence audience (D376).
// Dynamic PVP's opening names nobody at all, and a Dangerous Treasures win names
// nobody once `dangerousTreasures.shapeFor` has read it for such a viewer.
assert.deepEqual(catalogue.kindsFor({}).sort(), ['dt.started', 'dt.won', 'npc.boss.spawned', 'pvp.zone.created', 'server.initialized', 'server.shutdown', 'server.wipe', 'world.event'])
// nobody once `dangerousTreasures.shapeFor` has read it for such a viewer, nor
// a guarded crate cleared once `guardedCrate.shapeFor` has.
assert.deepEqual(catalogue.kindsFor({}).sort(), ['dt.started', 'dt.won', 'gc.ended', 'gc.started', 'npc.boss.spawned', 'pvp.zone.created', 'server.initialized', 'server.shutdown', 'server.wipe', 'world.event'])
})

View File

@@ -160,6 +160,7 @@ test('the event verbs are registered, and every budget has a verb that spends it
'rust.coins.pay',
'rust.crate.place',
'rust.dt.start',
'rust.gc.place',
'rust.kit.entitle',
'rust.npc.place',
'rust.participation.collect',
@@ -174,6 +175,7 @@ test('the event verbs are registered, and every budget has a verb that spends it
'rust.backpacks',
'rust.coins',
'rust.dt.events',
'rust.gc.crates',
'rust.grants',
'rust.npcs',
'rust.permissions',
@@ -206,8 +208,9 @@ test('the event verbs are registered, and every budget has a verb that spends it
// collect filed, coins paid (D335) and permissions given for good (D346) are
// not undone, and nor is a world event called (D354): a cargo ship or a drop
// may already hold players or their loot; nor a Dangerous Treasures event
// started, which DT despawns itself (§14.3.4).
const once = new Set(['rust.announce', 'rust.participation.collect', 'rust.coins.pay', 'rust.perm.give', 'rust.world.call', 'rust.dt.start'])
// started, which DT despawns itself (§14.3.4); nor a guarded crate placed,
// which Guarded Crate cannot end alone (§14.3.5, D373).
const once = new Set(['rust.announce', 'rust.participation.collect', 'rust.coins.pay', 'rust.perm.give', 'rust.world.call', 'rust.dt.start', 'rust.gc.place'])
for (const a of actions) {
assert.strictEqual(a.reversible, once.has(a.id) ? 'none' : 'ledger', a.id)
// Every param source is one this module registers.

View File

@@ -0,0 +1,368 @@
// ── Guarded Crate, with NpcSpawn (PLAN_REDESIGNS §14.3.5, protocol 14) ──────
//
// Each test is one way the feature could look right and be wrong:
//
// the clearer's name reaches a viewer below the presence audience (D376), or
// a Steam id, a position or a step's key reaches anybody but staff
// an ending with nobody clearing it reads as a win
// the step runs on a server that said it has no Guarded Crate, no NpcSpawn
// (D364), a framework that cannot run its command (D363), or no event of that
// name, or a server that has not said is refused
// the step's location is not a world call's, or the run log is not told the
// crate is kept (D373, D380)
// a refusal Guarded Crate would repeat is retried, or a crowded spot is not
// a retry of a key places a second crate
// the tally's crates cleared is dropped, or Warden's column and sort are missing
// a trigger declares a variable its emitter never fills, names a Steam id, or
// fires for a crate nobody cleared
const test = require('node:test')
const assert = require('node:assert')
const { fakeCtx } = require('./_fakes')
/** Installs a core whose `db.query` records every statement and answers `rows`. */
function withRecorder(rows = []) {
const statements = []
const ctx = fakeCtx({
db: {
query: (sql, params = []) => {
statements.push({ sql, params })
return Promise.resolve(typeof rows === 'function' ? rows(sql, params) : rows)
},
pool: {},
},
})
require('../core')._reset()
require('../core').init(ctx)
return { ctx, statements, touching: (table) => statements.filter((s) => s.sql.includes(table)) }
}
withRecorder()
const gc = require('../guardedCrate')
const { gridOf } = require('../worldEvents')
const { TRIGGERS } = require('../engagement/triggers')
const A = '76561198000000001'
const NOW = Date.now()
const GEOMETRY = { worldSize: 6000, gridCells: 41, gridCellSize: 146.34146341463415 }
const frame = (kind, over = {}) => ({ kind, type: 'event', t: NOW, serverId: 'main', wipeId: 'w1', x: -604, z: -342, ...over })
const cleared = (over = {}) => frame('gc.ended', { steamId: A, name: 'Alice', ...over })
const REPORT = { loaded: true, version: '2.0.17', npcSpawn: { loaded: true, version: '3.4.8' }, events: ['Easy', 'Medium', 'Hard', 'Elite'] }
// ── Who may read what (D353, D376) ─────────────────────────────────────────
test('the public reads a guarded crate’s grid, never its position or the step’s key', () => {
const out = gc.shapeFor(frame('gc.started', { key: '7:1' }), { geometry: GEOMETRY })
assert.strictEqual(out.grid, gridOf(GEOMETRY, -604, -342))
assert.strictEqual(out.x, undefined)
assert.strictEqual(out.z, undefined)
assert.strictEqual(out.key, undefined)
assert.strictEqual(out.kind, 'gc.started')
})
test('a clearing names its clearer only to the presence audience, and the Steam id only to staff', () => {
const signedOut = gc.shapeFor(cleared(), { geometry: GEOMETRY })
assert.strictEqual(signedOut.won, true, 'that somebody cleared it names nobody')
assert.strictEqual(signedOut.name, undefined)
assert.strictEqual(signedOut.steamId, undefined)
const member = gc.shapeFor(cleared(), { presence: true, geometry: GEOMETRY })
assert.strictEqual(member.name, 'Alice')
assert.strictEqual(member.steamId, undefined)
const staff = gc.shapeFor(cleared({ key: '7:1' }), { staff: true, geometry: GEOMETRY })
assert.strictEqual(staff.name, 'Alice')
assert.strictEqual(staff.steamId, A)
assert.strictEqual(staff.x, -604)
assert.strictEqual(staff.key, '7:1')
})
test('an ending with nobody clearing it is not a win, for anybody', () => {
for (const view of [{}, { presence: true }, { staff: true }]) {
const out = gc.shapeFor(frame('gc.ended'), { ...view, geometry: GEOMETRY })
assert.strictEqual(out.won, false)
assert.strictEqual(out.name, undefined)
assert.strictEqual(out.steamId, undefined)
}
// A name without a Steam id is not a clearer either.
assert.strictEqual(gc.shapeFor(frame('gc.ended', { name: 'Alice' }), { staff: true }).name, undefined)
const other = { kind: 'player.death', x: 1 }
assert.strictEqual(gc.shapeFor(other, {}), other)
})
test('the feed reads a stored clearing for its viewer, the row’s own Steam id staff’s', async () => {
withRecorder((sql) => {
if (sql.includes('FROM rust_events')) return [{ id: 9, kind: 'gc.ended', t: NOW, wipeId: 'w1', steamId: A, raw: JSON.stringify(cleared()) }]
if (sql.includes('FROM rust_map_images')) return [{ serverId: 'main', ...GEOMETRY }]
return []
})
const events = require('../model/events/events.model')
const [signedOut] = await events.recent({ serverId: 'main', presence: false })
assert.strictEqual(signedOut.steamId, null)
assert.strictEqual(signedOut.frame.name, undefined)
assert.strictEqual(signedOut.frame.won, true)
assert.ok(signedOut.frame.grid)
const [staff] = await events.recent({ serverId: 'main', admin: true })
assert.strictEqual(staff.steamId, A)
assert.strictEqual(staff.frame.name, 'Alice')
})
test('a server’s report is read leniently: what it never said is null', () => {
assert.deepStrictEqual(gc.reportOf(null), { guardedCrate: null, consolePlugins: null })
assert.deepStrictEqual(gc.reportOf({ guardedCrate: JSON.stringify(REPORT), console: '{"plugins":false}' }), {
guardedCrate: REPORT,
consolePlugins: false,
})
// A plugin that could not read the data file sends no events; NpcSpawn unsaid is null.
assert.deepStrictEqual(gc.reportOf({ guardedCrate: '{"loaded":true,"version":"2.0.17"}', console: null }).guardedCrate, {
loaded: true,
version: '2.0.17',
npcSpawn: null,
events: null,
})
assert.deepStrictEqual(gc.reportOf({ guardedCrate: 'not json', console: '{}' }), { guardedCrate: null, consolePlugins: null })
})
// ── The step ───────────────────────────────────────────────────────────────
const SERVERS = {
main: { id: 'main', name: 'Main', enabled: 1, sidecarBaseUrl: 'http://main:1' },
pve: { id: 'pve', name: 'PvE', enabled: 1, sidecarBaseUrl: 'http://pve:1' },
}
/** The step against servers `main` and `pve`, whose last reports are `reports`, and a sidecar answering `reply`. */
function stubPlace(t, { reports = { main: { guardedCrate: REPORT, console: { plugins: true } } }, reply } = {}) {
const row = (id) => {
const r = reports[id]
return r ? { serverId: id, name: SERVERS[id].name, guardedCrate: JSON.stringify(r.guardedCrate ?? null), console: JSON.stringify(r.console ?? null) } : null
}
withRecorder((sql, params) => {
if (sql.includes('JOIN rust_server_state')) return Object.keys(reports).map(row)
if (sql.includes('FROM rust_server_state')) return row(params[0]) ? [row(params[0])] : []
if (sql.includes('FROM rust_map_images')) return [{ serverId: 'main', ...GEOMETRY }]
return []
})
const client = require('../sidecarClient')
const serversDb = require('../model/servers/servers.db')
const saved = { gcPlace: client.gcPlace, getServer: serversDb.getServer }
const calls = []
serversDb.getServer = async (id) => SERVERS[id] || null
client.gcPlace = async (server, body) => {
calls.push({ server: server.id, body })
return typeof reply === 'function' ? reply(body) : reply
}
t.after(() => {
client.gcPlace = saved.gcPlace
serversDb.getServer = saved.getServer
})
return calls
}
const module_ = () => require('../eventGuardedCrate')
const placeStep = () => module_().ACTIONS[0]
const ok = (data) => ({ ok: true, status: 'ok', data })
const placed = (over = {}) => ok({ kind: 'gc.placed', key: '7:1', event: 'Elite', repeat: false, x: -604, z: -342, ...over })
test('the step is kept, priced one crate each, and has no revert (D373)', () => {
const step = placeStep()
assert.strictEqual(step.id, 'rust.gc.place')
assert.strictEqual(step.reversible, 'none')
assert.strictEqual(step.risk, 'change')
assert.strictEqual(step.revert, undefined)
assert.deepStrictEqual(step.cost({ event: 'Elite', monument: 'main/powerplant_1' }), { 'rust.gc.crates': 1 })
assert.match(step.description, /kept when the run ends/)
})
test('the event list is every server’s own, once each, and Guarded Crate’s defaults where none has said', async (t) => {
const [events] = module_().OPTION_SOURCES
stubPlace(t, {
reports: {
main: { guardedCrate: REPORT, console: { plugins: true } },
pve: { guardedCrate: { ...REPORT, events: ['easy', 'Boss Drop'] }, console: { plugins: true } },
},
})
assert.deepStrictEqual(await events.resolve(), [
{ value: 'Easy', label: 'Easy — Main, PvE' },
{ value: 'Medium', label: 'Medium — Main' },
{ value: 'Hard', label: 'Hard — Main' },
{ value: 'Elite', label: 'Elite — Main' },
{ value: 'Boss Drop', label: 'Boss Drop — PvE' },
])
stubPlace(t, { reports: { main: { guardedCrate: { loaded: false }, console: null } } })
assert.deepStrictEqual((await events.resolve()).map((e) => e.value), ['Easy', 'Medium', 'Hard', 'Elite'])
})
test('the dry run refuses what the server said it cannot do, and nothing else (D364)', async (t) => {
const run = (params) => placeStep().perform({ runId: 7, idempotencyKey: '7:1', params, verify: true })
const at = { event: 'Elite', monument: 'main/powerplant_1' }
stubPlace(t, { reports: { main: { guardedCrate: { loaded: false }, console: { plugins: true } } } })
const absent = await run(at)
assert.strictEqual(absent.retry, false)
assert.match(absent.error, /Guarded Crate is not installed on Main/)
stubPlace(t, { reports: { main: { guardedCrate: { ...REPORT, npcSpawn: { loaded: false } }, console: { plugins: true } } } })
const noGuards = await run(at)
assert.strictEqual(noGuards.retry, false)
assert.strictEqual(noGuards.error, 'Guarded Crate is loaded on Main but NpcSpawn is missing: its events cannot start')
stubPlace(t, { reports: { main: { guardedCrate: REPORT, console: { plugins: false } } } })
const carbon = await run(at)
assert.match(carbon.error, /cannot run Guarded Crate's console command/)
assert.match(carbon.error, /Rust-Plugins#34/)
const calls = stubPlace(t)
const unknown = await run({ ...at, event: 'Legendary' })
assert.strictEqual(unknown.retry, false)
assert.match(unknown.error, /"Legendary" is not one of Guarded Crate's events on Main \(Easy, Medium, Hard, Elite\)/)
assert.ok((await run({ ...at, event: 'elite' })).ok, 'a name is found ignoring case, as Guarded Crate finds it')
assert.match((await run({ monument: 'main/powerplant_1' })).error, /which of Guarded Crate's events/)
assert.match((await run({ event: 'Elite', x: 1, z: 2 })).error, /which server/)
assert.match((await run({ event: 'Elite', monument: 'main/powerplant_1', x: 1, z: 2 })).error, /exactly one of them/)
assert.strictEqual(calls.length, 0, 'the dry run places nothing')
// A plugin before protocol 14, or a server never reached, has not said: the plugin answers.
stubPlace(t, { reports: {} })
assert.ok((await run(at)).ok)
// Nor does a report that could not read the event names refuse a name.
stubPlace(t, { reports: { main: { guardedCrate: { ...REPORT, events: undefined }, console: { plugins: true } } } })
assert.ok((await run({ ...at, event: 'Boss Drop' })).ok)
})
test('a placement sends the key, the event and a world call’s location, and tells the run log the crate is kept', async (t) => {
const calls = stubPlace(t, { reply: placed() })
const result = await placeStep().perform({
runId: 7,
idempotencyKey: '7:1',
params: { event: 'Elite', monument: 'main/powerplant_1', offsetX: 10, offsetZ: -5, y: 40 },
})
assert.ok(result.ok)
assert.deepStrictEqual(calls, [{ server: 'main', body: { key: '7:1', event: 'Elite', monument: 'powerplant_1', offsetX: 10, offsetZ: -5 } }])
assert.strictEqual(result.detail.event, 'Elite')
assert.strictEqual(result.detail.grid, gridOf(GEOMETRY, -604, -342))
assert.strictEqual(result.detail.note, `The crate is ${module_().KEPT}`)
const byPoint = stubPlace(t, { reply: placed() })
await placeStep().perform({ runId: 7, idempotencyKey: '7:2', params: { event: 'Easy', server: 'main', x: -604, z: -342 } })
assert.deepStrictEqual(byPoint[0].body, { key: '7:2', event: 'Easy', x: -604, z: -342 })
})
test('a crowded spot and a game that is down are retried; a refusal Guarded Crate would repeat is not', async (t) => {
stubPlace(t, {
reply: (body) => ok(body.event === 'Hard'
? { kind: 'gc.error', reason: 'gc-too-close', message: 'another guarded crate is running within 80 m of that spot' }
: { kind: 'gc.error', reason: 'npcspawn-missing', message: 'Guarded Crate is loaded but NpcSpawn is missing: its events cannot start' }),
})
const step = placeStep()
const params = (event) => ({ event, monument: 'main/powerplant_1' })
const crowded = await step.perform({ runId: 7, idempotencyKey: '7:3', params: params('Hard') })
assert.strictEqual(crowded.ok, false)
assert.strictEqual(crowded.retry, undefined)
assert.match(crowded.error, /within 80 m/)
const noGuards = await step.perform({ runId: 7, idempotencyKey: '7:4', params: params('Easy') })
assert.strictEqual(noGuards.retry, false)
assert.match(noGuards.error, /NpcSpawn is missing/)
for (const reason of ['gc-missing', 'npcspawn-missing', 'console-unavailable', 'bad-event', 'bad-position', 'refused', 'no-monument', 'off-map', 'malformed', 'events-disabled']) {
assert.ok(module_().PERMANENT.has(reason), reason)
}
assert.ok(!module_().PERMANENT.has('gc-too-close'))
stubPlace(t, { reply: { ok: false, status: 'http-503' } })
const down = await step.perform({ runId: 7, idempotencyKey: '7:5', params: params('Elite') })
assert.strictEqual(down.retry, undefined)
assert.match(down.error, /has no game connected/)
})
test('a repeat of a key places nothing new, and says so beside the crate being kept', async (t) => {
stubPlace(t, { reply: placed({ repeat: true }) })
const again = await placeStep().perform({ runId: 7, idempotencyKey: '7:1', params: { event: 'Elite', monument: 'main/powerplant_1' } })
assert.ok(again.ok)
assert.strictEqual(again.detail.repeat, true)
assert.match(again.detail.note, /^answered from the first attempt; nothing new was placed\. The crate is kept/)
})
// ── Warden (D375) ──────────────────────────────────────────────────────────
test('the tally’s crates cleared is added up', async () => {
const rec = withRecorder()
const { apply } = require('../ingest')
await apply('main', { id: 1, kind: 'player.tally', frame: { kind: 'player.tally', type: 'event', t: NOW, serverId: 'main', wipeId: 'w1', steamId: A, name: 'Alice', guardedWon: 2 } })
const titleAdd = rec.touching('rust_player_wipe_stats').find((s) => s.sql.includes('guarded_won'))
assert.ok(titleAdd, 'the tally reached the title columns')
const cols = titleAdd.sql.match(/\(server_id, wipe_id, steam_id, ([^)]+)\)/)[1].split(', ')
assert.strictEqual(titleAdd.params[3 + cols.indexOf('guarded_won')], 2)
})
test('the leaderboard sorts by crates cleared and carries the count on every row', async () => {
const rec = withRecorder((sql) => (sql.includes('FROM rust_player_wipe_stats') ? [{ steamId: A, name: 'Alice', kills: 1, guardedWon: '3' }] : []))
const events = require('../model/events/events.model')
const [row] = await events.leaderboard({ serverId: 'main', wipeId: 'w1', sort: 'guardedWon' })
assert.strictEqual(row.guardedWon, 3)
const board = rec.touching('rust_player_wipe_stats').at(-1)
assert.match(board.sql, /SUM\(s\.guarded_won\) AS guardedWon/)
assert.match(board.sql, /ORDER BY[^]*guarded_won/)
})
// ── Triggers (D355, §14.3.9) ───────────────────────────────────────────────
test('a landing and a clearing are public triggers, neither declaring a Steam id', () => {
const found = TRIGGERS.filter((t) => t.id.startsWith('rust.gc.'))
assert.deepStrictEqual(found.map((t) => t.id).sort(), ['rust.gc.ended', 'rust.gc.started'])
for (const t of found) {
assert.strictEqual(t.ceiling, 'everyone', t.id)
assert.strictEqual(t.subjectKey, 'subject', t.id)
assert.ok(!t.variables.some((v) => /steam/i.test(v.name)), `${t.id} declares a Steam id`)
}
})
test('a landing and a clearing fire with what they declare; nobody clearing it and a timeout fire nothing', async () => {
const { ctx } = withRecorder((sql) => (sql.includes('FROM rust_map_images') ? [{ serverId: 'main', ...GEOMETRY }] : []))
const emit = require('../engagement/emit')
emit.reset()
const server = { id: 'main', name: 'Main' }
await emit.onEvent(server, { id: 1, kind: 'gc.started', frame: frame('gc.started', { key: '7:1' }) })
await emit.onEvent(server, { id: 2, kind: 'gc.ended', frame: cleared() })
await emit.onEvent(server, { id: 3, kind: 'gc.ended', frame: frame('gc.ended') })
await emit.onEvent(server, { id: 4, kind: 'gc.failed', frame: frame('gc.failed') })
const calls = ctx.events.emit.calls
assert.strictEqual(calls.length, 2)
const [startedId, started] = calls[0]
assert.strictEqual(startedId, 'rust.gc.started')
assert.strictEqual(started.data.byEvent, true)
assert.strictEqual(started.data.subject, 'main:gc.started')
assert.match(started.data.intro, /A guarded crate landed at [A-Z]+\d+ on Main\./)
const [endedId, ended] = calls[1]
assert.strictEqual(endedId, 'rust.gc.ended')
assert.strictEqual(ended.data.player, 'Alice')
assert.match(ended.data.intro, /Alice cleared the guarded crate at [A-Z]+\d+ on Main\./)
for (const [id, envelope] of calls) {
const declared = new Set(TRIGGERS.find((x) => x.id === id).variables.map((v) => v.name))
for (const name of Object.keys(envelope.data)) assert.ok(declared.has(name), `${id} fills ${name}, which it does not declare`)
}
})
test('an old crate tells nobody (D63)', async () => {
const { ctx } = withRecorder()
const emit = require('../engagement/emit')
emit.reset()
await emit.onEvent({ id: 'main', name: 'Main' }, { id: 1, kind: 'gc.ended', frame: cleared({ t: NOW - 3 * 3600 * 1000 }) })
assert.strictEqual(ctx.events.emit.calls.length, 0)
})

View File

@@ -47,8 +47,9 @@ const item = (kind, over = {}) => ({ id: 1, t: 1, kind, frame: frame({ kind, ...
// ── The categories ────────────────────────────────────────────────────────
test('twenty-seven categories carry a default title, and playtime carries none (D172, §11.2.2, D356, D375)', () => {
assert.strictEqual(titles.CATEGORY_IDS.length, 27)
test('twenty-eight categories carry a default title, and playtime carries none (D172, §11.2.2, D356, D375)', () => {
assert.strictEqual(titles.CATEGORY_IDS.length, 28)
assert.strictEqual(titles.STATS.guardedwon.title, 'Warden')
assert.strictEqual(titles.STATS.treasureswon.title, 'Treasure Hunter')
assert.strictEqual(titles.STATS.richest.title, 'Magnate')
assert.strictEqual(titles.STATS.crateshacked.title, 'Safecracker')