1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// The NWC budget window: a UTC day, week (Monday) or month; what a
// connection spent in the window on now, counting the app's counter and the
// worker's unabsorbed spends; when it renews. Run: bun tools/nwc-budget-test.js
import { budgetWindow, budgetRenewsAt, renewalPeriod, spentInWindow } from '../src/nwc-budget.js';
let fails = 0;
const check = (name, ok, detail = '') => { console.log(` ${ok ? '✓' : '✗'} ${name}${detail ? ' — ' + detail : ''}`); if (!ok) fails++; };
// Wednesday 2026-10-14 23:30 UTC
const T = Date.UTC(2026, 9, 14, 23, 30);
check('day window is the UTC date', budgetWindow('day', T) === '2026-10-14', budgetWindow('day', T));
check('day: half an hour later is the next day', budgetWindow('day', T + 3600_000) === '2026-10-15');
check('week window is keyed by its Monday', budgetWindow('week', T) === 'W2026-10-12', budgetWindow('week', T));
check('week: Sunday night is still that week', budgetWindow('week', Date.UTC(2026, 9, 18, 23, 59)) === 'W2026-10-12');
check('week: Monday 00:00 starts the next', budgetWindow('week', Date.UTC(2026, 9, 19, 0, 0)) === 'W2026-10-19');
check('month window is the UTC month', budgetWindow('month', T) === '2026-10');
check('month: Nov 1 00:00 is the next', budgetWindow('month', Date.UTC(2026, 10, 1)) === '2026-11');
check('unknown period counts as a day', budgetWindow(undefined, T) === '2026-10-14');
check('day renews at next midnight', budgetRenewsAt('day', T) === Date.UTC(2026, 9, 15) / 1000);
check('week renews next Monday', budgetRenewsAt('week', T) === Date.UTC(2026, 9, 19) / 1000);
check('month renews on the 1st', budgetRenewsAt('month', T) === Date.UTC(2026, 10, 1) / 1000);
check('December rolls into January', budgetRenewsAt('month', Date.UTC(2026, 11, 20)) === Date.UTC(2027, 0, 1) / 1000);
check('NIP-47 period words', renewalPeriod('day') === 'daily' && renewalPeriod('week') === 'weekly' && renewalPeriod('month') === 'monthly' && renewalPeriod(undefined) === 'daily');
console.log('\n[spent in the window]');
const conn = { id: 'c1', dailySat: 10000, period: 'month', spentDate: '2026-10', spentToday: 3000 };
{
const r = spentInWindow(conn, [], T);
check('the app counter counts when stamped with this window', r.spent === 3000 && r.left === 7000, JSON.stringify(r));
}
{
const r = spentInWindow({ ...conn, spentDate: '2026-09' }, [], T);
check('a counter from last month does not', r.spent === 0 && r.left === 10000);
}
{
const spends = [
{ connId: 'c1', amountSat: 500, feeSat: 2, ts: Date.UTC(2026, 9, 2) }, // this month, unabsorbed
{ connId: 'c1', amountSat: 400, feeSat: 0, ts: Date.UTC(2026, 9, 3), absorbed: true }, // already in the counter
{ connId: 'c1', amountSat: 900, feeSat: 0, ts: Date.UTC(2026, 8, 30) }, // last month
{ connId: 'c2', amountSat: 800, feeSat: 0, ts: Date.UTC(2026, 9, 5) }, // another connection
];
const r = spentInWindow(conn, spends, T);
check('the worker adds only its own, unabsorbed spends from this window', r.spent === 3502, String(r.spent));
}
{
const r = spentInWindow({ id: 'c1', dailySat: 100, spentDate: '2026-10-14', spentToday: 80 }, [{ connId: 'c1', amountSat: 30, ts: T - 60_000 }], T);
check('a day budget, over: left is 0 not negative', r.spent === 110 && r.left === 0);
}
console.log(fails ? `\n❌ ${fails} failure(s)` : '\n✅ budget windows behave');
process.exit(fails ? 1 : 0);
|