Files
wownerogue/test/identityService.test.js
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

103 lines
4.2 KiB
JavaScript

const IdentityService = require('../src/network/identityService');
function makeSocket(id = 'sockA') {
return { id, emit: jest.fn() };
}
function makeDb({ user, grants = [] } = {}) {
const state = {
user: {
id: 1,
socket_id: 'sockA',
credits: 0,
total_credits_purchased: 0,
premium_level: 'free',
appearance: {
avatar: 'char-ranger',
tint: 'teal',
equipment: { body: 'mail', head: 'hood', shield: 'round', weapon: 'bow' },
colors: { base: 'teal', skin: 'warm', hair: 'silver', body: 'gold', head: 'none', shield: 'rose', weapon: 'none' }
},
...user
},
grants
};
const db = {
state,
query: jest.fn(async (text, params = []) => {
if (/SELECT pack_id/i.test(text)) return { rows: state.grants };
if (/UPDATE users[\s\S]*SET appearance/i.test(text)) {
state.user = { ...state.user, appearance: JSON.parse(params[0]) };
return { rows: [state.user] };
}
return { rows: [] };
})
};
return db;
}
describe('IdentityService', () => {
test('loads stored appearance and entitlements for a socket user', async () => {
const db = makeDb({ user: { total_credits_purchased: 5, credits: 2 } });
const sessionManager = { sessions: new Map([['sockA', db.state.user]]) };
const gameModeManager = { getOrCreateUser: jest.fn(async () => db.state.user), db };
const svc = new IdentityService({ db, gameModeManager, sessionManager });
const snapshot = await svc.identityForSocket(makeSocket());
expect(snapshot.appearance).toEqual(db.state.user.appearance);
expect(snapshot.entitlements.premium).toBe(true);
expect(snapshot.entitlements.packs['generated-skins']).toBe(true); // 5 credits >= threshold 1
expect(snapshot.entitlements.packs['iso-dungeon']).toBe(false); // < threshold 10
});
test('saves normalized appearance and refreshes the session cache', async () => {
const db = makeDb({ user: { total_credits_purchased: 0 } });
const sessionManager = { sessions: new Map([['sockA', db.state.user]]) };
const gameModeManager = { getOrCreateUser: jest.fn(async () => db.state.user), db };
const svc = new IdentityService({ db, gameModeManager, sessionManager });
const snapshot = await svc.saveAppearanceForSocket(makeSocket(), {
avatar: 'char-ranger',
tint: 'bad',
equipment: { body: 'mail', head: 'bad', shield: 'round', weapon: 'laser' },
colors: { base: 'violet', skin: 'bad', hair: 'black', body: 'gold', head: 'bad', shield: 'rose', weapon: 'bad' }
});
expect(snapshot.appearance).toEqual({
avatar: 'char-ranger',
tint: 'violet',
equipment: { body: 'mail', head: 'none', shield: 'round', weapon: 'none' },
colors: { base: 'violet', skin: 'natural', hair: 'black', body: 'gold', head: 'none', shield: 'rose', weapon: 'none' }
});
expect(db.state.user.appearance).toEqual(snapshot.appearance);
expect(sessionManager.sessions.get('sockA').appearance).toEqual(snapshot.appearance);
});
test('falls back unauthorized premium appearance before saving', async () => {
const db = makeDb({ user: { total_credits_purchased: 0 } });
const gameModeManager = { getOrCreateUser: jest.fn(async () => db.state.user), db };
const svc = new IdentityService({ db, gameModeManager });
const snapshot = await svc.saveAppearanceForSocket(makeSocket(), { avatar: 'monero-knight' });
expect(snapshot.appearance.avatar).toBe('default');
expect(db.state.user.appearance.avatar).toBe('default');
});
test('allows an explicitly granted pack appearance', async () => {
const db = makeDb({
user: { total_credits_purchased: 0 },
grants: [{ pack_id: 'generated-skins' }]
});
const gameModeManager = { getOrCreateUser: jest.fn(async () => db.state.user), db };
const svc = new IdentityService({ db, gameModeManager });
const snapshot = await svc.saveAppearanceForSocket(makeSocket(), { avatar: 'monero-knight' });
expect(snapshot.appearance.avatar).toBe('monero-knight');
expect(snapshot.entitlements.packs['generated-skins']).toBe(true);
expect(snapshot.entitlements.packs['kenney-3d-characters']).toBe(false);
});
});