step 10.3: done

This commit is contained in:
bepis
2026-10-22 22:44:49 +11:00
parent 37e2219d32
commit 441efb06bd
5 changed files with 124 additions and 27 deletions
+82
View File
@@ -614,4 +614,86 @@ describe('expeditions', () => {
const ch = db.prepare('SELECT energy FROM characters WHERE id = ?').get(characterId);
assert.equal(ch.energy, 45, 'DB energy must be 55 - 10 = 45');
});
// ─── 10.3: injectable rng and time ────────────────────────────────────
it('10.3: fixed rng asserts exact gold reward on claim (service test)', async () => {
const { cookie, characterId } = await setupHero({ energy: 80, level: 1, xp: 0, gold: 100 });
// Start a scout expedition (gold range 5–15).
const startRes = await fetch(`${baseUrl}/api/expeditions/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
body: JSON.stringify({ defId: 1 }),
});
const startBody = await startRes.json();
const expeditionId = startBody.data.expedition.id;
// Fast-forward time past ends_at via DB.
const db = require('../../src/db').getDb();
db.prepare('UPDATE expeditions SET ends_at = ? WHERE id = ?').run(Date.now() - 600000, expeditionId);
// Claim with a constant rng that always returns 0.5.
// gold = 5 + floor(0.5 * (15 - 5 + 1)) = 5 + floor(5.5) = 10
const { claimExpedition } = require('../../src/services/expeditionService');
const result = claimExpedition(characterId, expeditionId, {
rng: () => 0.5,
});
assert.equal(result.goldGained, 10, 'gold should be exactly 10 with rng=0.5');
assert.equal(result.xpGained, 30, 'should grant 30 XP');
// Verify DB state.
const ch = db.prepare('SELECT gold FROM characters WHERE id = ?').get(characterId);
assert.equal(ch.gold, 110, 'gold should be 100 + 10');
});
it('10.3: time.setNow proves claim rejected before ends_at and accepted after', async () => {
const { cookie, characterId } = await setupHero({ energy: 80, level: 1, xp: 0, gold: 50 });
const startRes = await fetch(`${baseUrl}/api/expeditions/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
body: JSON.stringify({ defId: 1 }), // scout, 10 min duration
});
const startBody = await startRes.json();
const expeditionId = startBody.data.expedition.id;
const time = require('../../src/utils/time');
const db = require('../../src/db').getDb();
const realNow = Date.now();
// Set ends_at to 5 min ago so time.setNow controls the claim window.
db.prepare('UPDATE expeditions SET ends_at = ? WHERE id = ?').run(realNow - 300000, expeditionId);
// setNow to 10 min ago — still before ends_at (-5 min), claim should be rejected.
time.setNow(() => realNow - 600000);
const res1 = await fetch(`${baseUrl}/api/expeditions/claim`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
body: JSON.stringify({ expeditionId }),
});
const body1 = await res1.json();
assert.equal(res1.status, 409);
assert.equal(body1.error.code, 'NOT_FINISHED', 'claim should be rejected before ends_at');
// setNow to 2 min ago — past ends_at (-5 min), claim should succeed.
time.setNow(() => realNow - 120000);
const res2 = await fetch(`${baseUrl}/api/expeditions/claim`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders(cookie) },
body: JSON.stringify({ expeditionId }),
});
const body2 = await res2.json();
assert.equal(res2.status, 200);
assert.equal(body2.ok, true);
assert.equal(body2.data.xpGained, 30, 'should grant expedition XP');
assert.ok(body2.data.goldGained >= 5 && body2.data.goldGained <= 15, 'gold in expected range');
time.resetNow();
});
});