Files
wownerogue/test/directEntryCredit_ultra.test.js
T
jwinterm 1b59d2f927 feat(catalog): graduated cosmetic ladder + per-session test-unlock
Step 1 — real tiered unlocks (was flat: any credit unlocked skins+iso+3D):
- entitlements DEFAULT_CATALOG + migration 025 graduate the ladder — unlock by
  lifetime credit spend OR subscription tier, whichever first:
    Fancy (generated-skins)       1 credit  / supporter (tier 1)
    Iso   (iso-dungeon)          10 credits / premium   (tier 2)
    3D    (kenney-3d-characters) 25 credits / operator  (tier 3)
- Migration 025 UPDATEs the already-seeded 024 rows (idempotent).
- Tests updated to assert the ladder (1cr -> Fancy only; 10 -> +Iso; 25 -> all).

Step 2 — test the premium modes without paying:
- renderModes: ?unlock=1 (sticky via localStorage) flips RENDER_MODE_TEST_UNLOCKS
  for THAT browser only, so Fancy/Iso/3D are all clickable; ?unlock=0 turns it off.

Suite 57 suites / 414 tests green.
2026-07-11 07:24:26 -04:00

65 lines
2.9 KiB
JavaScript

/**
* Credits unification, step ②: a direct/single_game entry is recorded as "buy 1 credit and
* spend it" — the balance nets to zero but total_credits_purchased advances, so direct play
* unlocks the same tier/threshold cosmetics as buying credits.
*/
const GameModeManager = require('../src/game/gameModeManager');
const mockPaymentConfig = () => ({
getConfig: () => ({
paymentsEnabled: true,
currency: { symbol: 'WOW', decimals: 11 },
modes: {
direct: { enabled: true, price: 100000000000n },
credits: { enabled: false, creditsPerGame: 1, packages: [] }
},
payouts: { rules: { direct: { enabled: false, multipliers: { escape: 2, escapeWithTreasure: 3 }, minPayout: 0n, maxPayout: 0n } } },
preferences: { preferCreditsFirst: true },
earlyEntry: { enabled: false }
}),
getLegacyGameMode: () => 'PAID_SINGLE',
eventBus: { on: () => {} }
});
function makeGmm(userRow) {
const client = { query: jest.fn().mockResolvedValue({ rows: [userRow] }) };
const db = {
query: jest.fn().mockResolvedValue({ rows: [] }), // user_pack_entitlements lookup
withTransaction: jest.fn().mockImplementation(async (cb) => cb(client)),
_client: client
};
const gmm = new GameModeManager(db, { processPayout: jest.fn() }, { CONSOLE_LOGGING: false, getCurrentBlockHeight: () => 1 }, mockPaymentConfig());
gmm.getOrCreateUser = jest.fn().mockResolvedValue({ id: 7 });
return { gmm, db, client };
}
describe('recordDirectEntryPurchase', () => {
test('advances total_credits_purchased and unlocks threshold-1 packs', async () => {
const { gmm, client } = makeGmm({ total_credits_purchased: 1, credits: 0, premium_level: null });
const rec = await gmm.recordDirectEntryPurchase('sock');
expect(rec).toBeTruthy();
expect(rec.totalCreditsPurchased).toBe(1);
expect(rec.balance).toBe(0); // net zero — bought and spent
const insert = client.query.mock.calls.find(c => /INSERT INTO credit_transactions/i.test(c[0]));
expect(insert).toBeTruthy();
expect(insert[0]).toMatch(/'direct_entry'/); // audits the purchase leg
expect(insert[0]).toMatch(/'game_entry'/); // audits the spend leg
// A direct entry = 1 credit, which unlocks Fancy (generated-skins, threshold 1) but not the
// pricier Iso (10) / 3D (25) tiers.
expect(rec.entitlements.packs['generated-skins']).toBe(true);
expect(rec.entitlements.packs['iso-dungeon']).toBe(false);
expect(rec.entitlements.premium).toBe(true);
// ...but a direct buyer is still NOT a premium tier.
expect(rec.entitlements.level).toBe('free');
});
test('returns null when the session has no user', async () => {
const { gmm } = makeGmm({});
gmm.getOrCreateUser = jest.fn().mockResolvedValue(null);
expect(await gmm.recordDirectEntryPurchase('sock')).toBe(null);
});
});