import { test } from 'node:test' import assert from 'node:assert/strict' import { createAsinForceThrottle } from '../src/shared/asin-force-throttle.ts' function clock(start = 1000) { let now = start return { now: () => now, tick: (ms: number) => { now += ms }, } } test('test_task_085_asin_polling_normal_default_path', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) assert.equal(throttle.shouldForce(101), true) throttle.markForce(101) assert.equal(throttle.shouldForce(101), false) assert.equal(throttle.isThrottled(101), true) c.tick(29_999) assert.equal(throttle.shouldForce(101), false) c.tick(2) assert.equal(throttle.shouldForce(101), true) assert.equal(throttle.isThrottled(101), false) const stats = throttle.stats() assert.equal(stats.forcedCount, 1) assert.equal(stats.skippedCount, 2) assert.equal(stats.activeCount, 0) }) test('test_task_085_asin_polling_normal_multiple_items', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) const ids = [201, 202, 203, 204, 205] for (const id of ids) throttle.markForce(id) assert.equal(throttle.activeCount, 5) for (const id of ids) assert.equal(throttle.shouldForce(id), false) // 不同 taskId 的 TTL 相互独立 c.tick(31_000) for (const id of ids) assert.equal(throttle.shouldForce(id), true) throttle.markForce(202) assert.equal(throttle.shouldForce(202), false) assert.equal(throttle.shouldForce(201), true) }) test('test_task_085_asin_polling_normal_repeated_operation_is_idempotent', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) throttle.markForce(301) throttle.markForce(301) throttle.markForce(301) assert.equal(throttle.activeCount, 1) assert.equal(throttle.shouldForce(301), false) assert.equal(throttle.stats().forcedCount, 1) // 重复检查不改变状态 assert.equal(throttle.shouldForce(301), false) assert.equal(throttle.isThrottled(301), true) assert.equal(throttle.stats().forcedCount, 1) }) test('test_task_085_asin_polling_boundary_empty_input', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) assert.equal(throttle.activeCount, 0) assert.equal(throttle.shouldForce(401), true) throttle.clear(401) assert.equal(throttle.activeCount, 0) // clear 不存在的 taskId 无副作用 throttle.clearAll() assert.deepEqual(throttle.stats(), { forcedCount: 0, skippedCount: 0, activeCount: 0 }) }) test('test_task_085_asin_polling_boundary_single_item', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 1000, now: c.now }) assert.equal(throttle.shouldForce(501), true) throttle.markForce(501) assert.equal(throttle.isThrottled(501), true) assert.equal(throttle.activeCount, 1) c.tick(1000) assert.equal(throttle.isThrottled(501), false) assert.equal(throttle.activeCount, 0) }) test('test_task_085_asin_polling_boundary_limit_and_overflow', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) // 大量任务:所有任务都被记录,未标记的任务不被节流 for (let i = 1; i <= 200; i++) throttle.markForce(i) assert.equal(throttle.activeCount, 200) assert.equal(throttle.shouldForce(1), false) assert.equal(throttle.shouldForce(200), false) assert.equal(throttle.shouldForce(201), true) // TTL 到期后整批可重新 force(重试窗口),记录不无界增长 c.tick(60_001) assert.equal(throttle.shouldForce(1), true) assert.equal(throttle.activeCount, 0) throttle.markForce(1) assert.equal(throttle.activeCount, 1) }) test('test_task_085_asin_polling_invalid_input_rejected', () => { const c = clock() const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: c.now }) assert.throws(() => throttle.markForce(0), /taskId 必须是正整数/) assert.throws(() => throttle.markForce(-1), /taskId 必须是正整数/) assert.throws(() => throttle.markForce(NaN), /taskId 必须是正整数/) assert.throws(() => throttle.markForce(1.5), /taskId 必须是正整数/) // 读取路径宽容:非法 id 安全返回 assert.equal(throttle.shouldForce(0), false) assert.equal(throttle.isThrottled(-1), false) assert.equal(throttle.clear(0), false) assert.throws(() => createAsinForceThrottle({ ttlMs: 0 }), /ttlMs 必须为正数/) }) test('test_task_085_asin_polling_dependency_failure_releases_resources', () => { let now = 1000 let broken = false const faultyNow = () => { if (broken) throw new Error('clock down') return now } const throttle = createAsinForceThrottle({ ttlMs: 30_000, now: faultyNow }) throttle.markForce(701) assert.equal(throttle.activeCount, 1) // 时钟故障时读取抛错,但记录不丢失 broken = true assert.throws(() => throttle.shouldForce(701), /clock down/) assert.throws(() => throttle.isThrottled(701), /clock down/) assert.throws(() => throttle.stats(), /clock down/) // 恢复后同一实例继续工作,记录仍在 broken = false assert.equal(throttle.isThrottled(701), true) assert.equal(throttle.shouldForce(701), false) // 终态后 clear 释放记录 throttle.clear(701) assert.equal(throttle.activeCount, 0) assert.equal(throttle.shouldForce(701), true) })