import { describe, expect, it } from 'vitest'; import { applyCentDust, DUST_MAX_STEPS, DUST_STEP } from '../src/dust'; describe('applyCentDust', () => { it('adds a suffix between 0.0001 and 0.0999 in 0.0001 steps', () => { for (let i = 0; i < 50; i++) { const dusted = applyCentDust(100_000000n, () => false); const dust = dusted - 100_000000n; expect(dust >= DUST_STEP).toBe(true); expect(dust <= BigInt(DUST_MAX_STEPS) * DUST_STEP).toBe(true); expect(dust % DUST_STEP).toBe(0n); } }); it('avoids amounts already taken by concurrent invoices', () => { const taken = new Set([100_000100n, 100_000200n]); // rng that picks steps 1, then 2, then 3… const seq = [0, 0.0011, 0.0021]; let call = 0; const rng = () => seq[Math.min(call++, seq.length - 1)]!; const dusted = applyCentDust(100_000000n, (c) => taken.has(c), rng); expect(dusted).toBe(100_000300n); }); it('falls back to a deterministic sweep when random attempts collide', () => { const taken = (c: bigint) => c !== 100_000000n + BigInt(DUST_MAX_STEPS) * DUST_STEP; const dusted = applyCentDust(100_000000n, taken, () => 0); // rng always proposes step 1 (taken) expect(dusted).toBe(100_000000n + BigInt(DUST_MAX_STEPS) * DUST_STEP); }); it('throws when every suffix is exhausted', () => { expect(() => applyCentDust(100_000000n, () => true)).toThrow(/no unique dust suffix/); }); });