mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-29 11:08:34 +01:00
① assetPacks.js merges the server-authoritative catalog (from the entitlement snapshot) into RK.PACKS — operator-added packs now appear client-side and existing packs pick up server definitions, retiring the client half of the split-brain. ② A confirmed direct/single_game payment now records as 'buy 1 credit + spend it': balance nets to zero but total_credits_purchased advances (recordDirectEntryPurchase), so direct play unlocks the same tier/threshold cosmetics as buying credits, and the client gets a fresh entitlement snapshot. +2 tests. Suite 49/345 green. P1 foundation (branch feat/credits-catalog-foundation). Remaining: the buy modal reflects the unified credits model; a later pass can fully collapse single_game into the credit spend path.
64 lines
2.9 KiB
JavaScript
64 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
|
|
|
|
// The seed premium packs (threshold 1) are now unlocked, exactly like a credit purchase.
|
|
expect(rec.entitlements.packs['iso-dungeon']).toBe(true);
|
|
expect(rec.entitlements.packs['generated-skins']).toBe(true);
|
|
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);
|
|
});
|
|
});
|