// The Account page's "Delete my account" section (client/src/lib/accountDeletion.js) // and how a deleted account's name is marked for staff (lib/deletedAccount.js). // docs/website/ACCOUNT_DELETION.md §4.1, §4.2, AD5, AD6. import { test } from 'node:test' import assert from 'node:assert/strict' import { usernameMatches, isSharedDemoLogin, refusalMessage, isStaffRole, WHAT_GOES, WHAT_STAYS, } from '../src/lib/accountDeletion.js' import { DELETED_PLAYER, markDeleted, rowName } from '../src/lib/deletedAccount.js' import { describeRequest, leadershipOf } from '../src/lib/teamAdmin.js' import { threadSummary } from '../src/lib/teamForum.js' // ── the confirm step ─────────────────────────────────────────────────────── test('Delete is live only once the typed username matches, ignoring case and spaces', () => { assert.equal(usernameMatches('Thorne', 'Thorne'), true) assert.equal(usernameMatches(' thorne ', 'Thorne'), true) assert.equal(usernameMatches('Thorn', 'Thorne'), false) assert.equal(usernameMatches('', 'Thorne'), false) assert.equal(usernameMatches(' ', 'Thorne'), false) assert.equal(usernameMatches(undefined, 'Thorne'), false) }) test('the demo’s shared logins are recognised from the published list, and nothing else is', () => { const settings = { demo: { logins: [{ username: 'demo-player' }, { username: 'play-review' }] } } assert.equal(isSharedDemoLogin(settings, 'Demo-Player'), true) assert.equal(isSharedDemoLogin(settings, 'alice'), false) // Off the demo there is no `demo` block, so nobody is a shared login. assert.equal(isSharedDemoLogin({}, 'demo-player'), false) assert.equal(isSharedDemoLogin(undefined, 'demo-player'), false) }) test('every refusal code the route answers has its own sentence', () => { for (const code of ['username_mismatch', 'wrong_password', 'code_required', 'wrong_code', 'password_required', 'last_admin', 'DEMO_LOCKED']) { const msg = refusalMessage({ status: 400, body: { code }, message: 'server words' }) assert.notEqual(msg, 'server words', code) assert.ok(msg.length > 10, code) } assert.match(refusalMessage({ status: 429, body: null }), /Too many attempts/) assert.equal(refusalMessage({ status: 500, message: 'Internal Server Error' }), 'Internal Server Error') assert.match(refusalMessage(null), /Could not delete/) }) test('the screen says what goes and what stays, and that staff names are kept forever', () => { assert.ok(WHAT_GOES.some((l) => /game-account links/.test(l))) assert.ok(WHAT_STAYS.some((l) => l.includes('[deleted player]'))) assert.equal(isStaffRole('moderator'), true) assert.equal(isStaffRole('player'), false) }) // ── staff see a deleted account's name, marked ───────────────────────────── test('a deleted account’s stored name is marked; the placeholder is left alone', () => { assert.equal(markDeleted('Ravenna', true), 'Ravenna (deleted account)') assert.equal(markDeleted('Ravenna', false), 'Ravenna') assert.equal(markDeleted(DELETED_PLAYER, true), DELETED_PLAYER) assert.equal(markDeleted(null, true), null) }) test('a raw ledger row is a deleted account only when its id is an explicit null', () => { assert.equal(rowName(null, 'OldMod'), 'OldMod (deleted account)') assert.equal(rowName(4, 'OldMod'), 'OldMod') assert.equal(rowName(undefined, 'OldMod'), 'OldMod') assert.equal(rowName(null, null), '—') }) test('Admin → Teams marks a deleted requester and a deleted override author', () => { assert.match( describeRequest({ action: 'unhide', requested_by: null, requested_username: 'mod1', team_name: 'X' }), /^mod1 \(deleted account\) asks/, ) const l = leadershipOf({ isLeaderSynced: true, leaderOverride: { effect: 'deny', by: 'mod1', byDeleted: true } }) assert.match(l.note, /Denied by mod1 \(deleted account\)/) }) test('a forum thread by a deleted account is marked in the list for staff', () => { assert.match(threadSummary({ type: 'discussion', author: 'Ravenna', authorDeleted: true, postCount: 1 }), /^Ravenna \(deleted account\)/) assert.equal(threadSummary({ type: 'discussion', author: DELETED_PLAYER, authorDeleted: true, postCount: 1 }), DELETED_PLAYER) })