Weekly prizes: top three of the Monday-to-Sunday Central week with 10+ finds get 3/2/1 POL as due prize drips (never finds, never against the pool), awarded once on the first tick after Sunday; Top Hunter badge; /api/prizes + winners on /leaders; admin award-by-hand

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-19 19:53:03 -05:00
parent 5025056446
commit 7b2f71f2f7
5 changed files with 80 additions and 7 deletions
+16 -1
View File
@@ -80,7 +80,22 @@ const sleep = ms => new Promise(r => setTimeout(r, ms));
// social: the find is on the leaderboard with a badge, the board carries rank and a share link,
// and a share link visit sets the referral cookie that turns sign-ups into that member's IAP join link
const lb = await call('/api/leaders?period=all'); eq([lb.status, lb.body.rows.length >= 1, lb.body.rows[0].memberId, lb.body.rows[0].finds >= 1, lb.body.rows[0].badges.length >= 1], [200, true, 42, true, true], 'leaderboard: hunter 42 leads all time with a badge');
const bd = await call('/api/badges'); eq(bd.body.badges.length, 6, 'six badges are defined');
const bd = await call('/api/badges'); eq(bd.body.badges.length, 7, 'seven badges are defined');
// weekly prizes: below the minimum nobody wins; with the minimum at 1 the leader gets 3 POL as a due prize drip
// that does not count as a find or against the pool, and the week cannot be awarded twice
const social = require('../lib/social'); const thisMonday = social.weekOf(new Date().toLocaleDateString('en-CA', { timeZone: 'America/Chicago' }));
const pz0 = await (await fetch(B + '/api/prizes')).json(); eq([pz0.rules.pol, pz0.rules.minFinds, pz0.week], [[3, 2, 1], 10, thisMonday], 'prize rules default to 3/2/1 POL, 10 finds, this Monday');
const aw0 = await admin('/api/admin/prizes/award', { method: 'POST', body: JSON.stringify({ week: thisMonday }) }); eq([aw0.status, aw0.body.winners.length], [200, 0], 'below the minimum finds nobody is awarded');
await admin('/api/admin/settings', { method: 'POST', body: JSON.stringify({ weeklyMinFinds: 1 }) });
// the week above is now recorded as awarded with no winners; award a fresh key by hand is refused for non-Mondays
const bad2 = await admin('/api/admin/prizes/award', { method: 'POST', body: JSON.stringify({ week: '2026-01-01' }) }); eq(bad2.status, 400, 'a non-Monday week key is refused');
const twiceP = await admin('/api/admin/prizes/award', { method: 'POST', body: JSON.stringify({ week: thisMonday }) }); eq(twiceP.body.skipped, 'already awarded', 'a week is never awarded twice');
{ const fs2 = require('fs'); const pf = path.join(DIR, 'prizes.json'); fs2.writeFileSync(pf, '[]'); }
const aw1 = await admin('/api/admin/prizes/award', { method: 'POST', body: JSON.stringify({ week: thisMonday }) });
eq([aw1.status, aw1.body.winners.length, aw1.body.winners[0].memberId, aw1.body.winners[0].prizePol], [200, 1, 42, 3], 'with the minimum at 1, hunter 42 wins 3 POL');
const b4 = await call('/api/my/board'); const pr = b4.body.drips.find(d => d.site === 'Weekly prize #1');
eq([!!pr, pr && pr.status, pr && pr.pol, b4.body.badges.some(x => x.id === 'top'), b4.body.rank.all.finds], [true, 'due', 3, true, 1], 'the prize is a due drip on the board, Top Hunter badge on, finds unchanged');
const lb2 = await call('/api/leaders?period=all'); eq(lb2.body.rows[0].finds, 1, 'the prize drip is not a find on the leaderboard');
const b3 = await call('/api/my/board'); eq([b3.body.badges.some(b => b.id === 'first'), b3.body.rank.all.rank, b3.body.share.link], [true, 1, B + '/?r=hunter42'], 'board: First Find badge, rank #1, share link carries the username');
const rv = await fetch(B + '/?r=hunter42', { redirect: 'manual' }); eq([rv.status, /ph\.ref=hunter42/.test(rv.headers.get('set-cookie') || ''), rv.headers.get('location')], [302, true, '/'], 'a share-link visit sets the referral cookie and lands on the landing');
const cf = await (await fetch(B + '/api/config', { headers: { Cookie: 'ph.ref=hunter42' } })).json(); eq([cf.ref, cf.joinUrl], ['hunter42', 'https://instantadpay.com/join/hunter42?from=polhunter'], 'with the cookie, sign-ups go to that member\u2019s IAP join link');