mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-17 22:31:26 +02:00
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.
103 lines
4.2 KiB
JavaScript
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);
|
|
});
|
|
});
|