import { describe, expect, test } from 'vitest'; import { calculateSnatchShortfall, isPositionDelinquent } from '../staking.js'; describe('calculateSnatchShortfall', () => { test('returns zero when within cap', () => { const outstanding = 100n; const desired = 50n; const total = 1000n; const result = calculateSnatchShortfall(outstanding, desired, total, 2n, 10n); expect(result).toBe(0n); }); test('returns positive remainder when exceeding cap', () => { const outstanding = 200n; const desired = 200n; const total = 1000n; const result = calculateSnatchShortfall(outstanding, desired, total, 2n, 10n); expect(result).toBe(200n); }); test('returns exact overage when required equals cap + 1', () => { // cap = (1000 * 2) / 10 = 200; required = 100 + 101 = 201; delta = 1 expect(calculateSnatchShortfall(100n, 101n, 1000n, 2n, 10n)).toBe(1n); }); test('returns zero when required equals cap exactly', () => { // cap = (1000 * 2) / 10 = 200; required = 100 + 100 = 200; delta = 0 expect(calculateSnatchShortfall(100n, 100n, 1000n, 2n, 10n)).toBe(0n); }); test('uses default cap numerator/denominator when not provided', () => { // defaults: capNumerator=2n, capDenominator=10n // cap = (1000 * 2) / 10 = 200; outstanding=100, desired=50 -> required=150 <= 200 -> 0 expect(calculateSnatchShortfall(100n, 50n, 1000n)).toBe(0n); }); test('throws when capDenominator is zero', () => { expect(() => calculateSnatchShortfall(100n, 50n, 1000n, 2n, 0n)).toThrow('capDenominator must be greater than zero'); }); }); describe('isPositionDelinquent', () => { test('respects tax rate windows', () => { const now = 1_000_000; const taxRate = 0.5; // 50% const windowSeconds = (365 * 24 * 60 * 60) / taxRate; expect(isPositionDelinquent(now - windowSeconds + 1, taxRate, now)).toBe(false); expect(isPositionDelinquent(now - windowSeconds - 10, taxRate, now)).toBe(true); expect(isPositionDelinquent(now, 0, now)).toBe(false); }); test('returns false for zero tax rate', () => { expect(isPositionDelinquent(0, 0, 1_000_000)).toBe(false); }); test('returns false for negative tax rate', () => { expect(isPositionDelinquent(0, -0.1, 1_000_000)).toBe(false); }); test('returns true when far past the allowance window', () => { // taxRate=1.0 -> allowance = 31_536_000s (1 year); use now > 31_536_000 const now = 100_000_000; const lastTax = 0; const taxRate = 1.0; // 100% per year = 1 year window expect(isPositionDelinquent(lastTax, taxRate, now)).toBe(true); }); test('uses current time as default when referenceTimestamp not provided', () => { // taxRate=1.0 -> allowance = 1 year; 2 years ago is definitely delinquent const twoYearsAgoSeconds = Math.floor(Date.now() / 1000) - 2 * 365 * 24 * 60 * 60; expect(isPositionDelinquent(twoYearsAgoSeconds, 1.0)).toBe(true); }); });