// Point the DB at a closed port BEFORE requiring anything that builds the pool, // so any stray query fails fast instead of holding the process open. The session // service, users model, and shardLinks model are all stubbed, so no query runs. 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') const { startApp } = require('./_helper') const playerRouter = require('../src/router/v1/player') const sessionService = require('../src/auth/session.service') const users = require('../src/model/users/users.model') const shardLinks = require('../src/model/shardLinks/shardLinks.model') const db = require('../src/utils/db') after(() => db.close()) const originals = { validateSession: sessionService.validateSession, isSessionRevoked: sessionService.isSessionRevoked, sessionMeta: sessionService.sessionMeta, getById: users.getById, listForUser: shardLinks.listForUser, } afterEach(() => { sessionService.validateSession = originals.validateSession sessionService.isSessionRevoked = originals.isSessionRevoked sessionService.sessionMeta = originals.sessionMeta users.getById = originals.getById shardLinks.listForUser = originals.listForUser }) // Sign every request in as the given DB user (role decides the gate outcome). function signInAs(user) { sessionService.validateSession = () => ({ userId: user.id, sessionId: 's1', createdAt: Date.now(), authMethod: 'jwt' }) sessionService.isSessionRevoked = async () => false sessionService.sessionMeta = () => ({}) users.getById = async () => user } // The player self-service group is intentionally role-agnostic (staff are a // superset of players): any authenticated, active account reaches the self-scoped // handlers. Regression guard for the fix that dropped requireRole('player') so a // staff/admin account is no longer 403'd out of its own characters. for (const role of ['player', 'admin', 'editor', 'moderator']) { test(`GET /player/shard/accounts is reachable by an authenticated ${role}`, async () => { signInAs({ id: 7, username: 'u', role, status: 'active' }) shardLinks.listForUser = async (id) => { assert.equal(id, 7) // self-scoped to the caller regardless of role return [{ account: 'acctA' }] } const app = await startApp((a) => a.use('/api/v1/player', playerRouter)) try { const res = await fetch(app.url + '/api/v1/player/shard/accounts') assert.equal(res.status, 200, `${role} should reach the handler, got ${res.status}`) assert.deepEqual(await res.json(), [{ account: 'acctA' }]) } finally { await app.close() } }) } test('GET /player/shard/accounts still rejects an unauthenticated caller with 401', async () => { sessionService.validateSession = () => null const app = await startApp((a) => a.use('/api/v1/player', playerRouter)) try { const res = await fetch(app.url + '/api/v1/player/shard/accounts') assert.equal(res.status, 401) } finally { await app.close() } }) test('a disabled account is still rejected with 403 (status gate, not role)', async () => { signInAs({ id: 8, username: 'banned', role: 'player', status: 'disabled' }) const app = await startApp((a) => a.use('/api/v1/player', playerRouter)) try { const res = await fetch(app.url + '/api/v1/player/shard/accounts') assert.equal(res.status, 403) } finally { await app.close() } })