import { test } from 'node:test' import assert from 'node:assert/strict' import { createLocalStorageLimiter } from '../src/shared/local-storage-limiter.ts' interface FakeStorage { getItem: (key: string) => string | null setItem: (key: string, value: string) => void removeItem: (key: string) => void } function createFakeStorage(initial: Record = {}): FakeStorage { const map = new Map(Object.entries(initial)) return { getItem: (key) => map.get(key) ?? null, setItem: (key, value) => void map.set(key, value), removeItem: (key) => void map.delete(key), } } const encoder = new TextEncoder() const bytes = (v: unknown) => encoder.encode(JSON.stringify(v)).byteLength test('test_task_087_task_normal_default_path', () => { const storage = createFakeStorage() const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 1024 }) const ok = limiter.write('brand:tasks', { ids: [1, 2, 3] }) assert.equal(ok, true) assert.deepEqual(limiter.read('brand:tasks'), { ids: [1, 2, 3] }) const stats = limiter.stats() assert.equal(stats.keyCount, 1) assert.equal(stats.totalBytes, bytes({ ids: [1, 2, 3] })) assert.equal(stats.evictedCount, 0) }) test('test_task_087_task_normal_multiple_items', () => { const storage = createFakeStorage() const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 1024 }) for (let i = 1; i <= 5; i++) { assert.equal(limiter.write(`k${i}`, { i }), true) } const stats = limiter.stats() assert.equal(stats.keyCount, 5) assert.deepEqual(limiter.read('k1'), { i: 1 }) assert.deepEqual(limiter.read('k5'), { i: 5 }) assert.equal(stats.totalBytes, bytes({ i: 1 }) * 5) assert.equal(stats.evictedCount, 0) }) test('test_task_087_task_normal_repeated_operation_is_idempotent', () => { const storage = createFakeStorage() const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 1024 }) limiter.write('brand:tasks', [1]) limiter.write('brand:tasks', [1]) limiter.write('brand:tasks', [1, 2, 3]) const stats = limiter.stats() assert.equal(stats.keyCount, 1, '同 key 重复写不增加条数') assert.equal(stats.totalBytes, bytes([1, 2, 3])) assert.deepEqual(limiter.read('brand:tasks'), [1, 2, 3]) }) test('test_task_087_task_boundary_empty_input', () => { const storage = createFakeStorage() const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 1024 }) assert.equal(limiter.write('', { a: 1 }), false, '空 key 拒绝') assert.equal(limiter.read('missing'), undefined) assert.equal(limiter.remove('missing'), false) assert.equal(limiter.write('empty', {}), true) assert.deepEqual(limiter.read('empty'), {}) limiter.clear() assert.equal(limiter.stats().keyCount, 0) }) test('test_task_087_task_boundary_single_item', () => { const storage = createFakeStorage() const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 1024 }) assert.equal(limiter.write('k', 42), true) assert.equal(limiter.read('k'), 42) assert.equal(limiter.stats().keyCount, 1) }) test('test_task_087_task_boundary_limit_and_overflow', () => { // 超过 maxKeys:驱逐最旧写入的 key,保持条数有界 const s1 = createFakeStorage() const byKeys = createLocalStorageLimiter({ storage: s1, maxKeys: 3, maxBytes: 10_000 }) byKeys.write('a', 1) byKeys.write('b', 2) byKeys.write('c', 3) byKeys.write('d', 4) const stats1 = byKeys.stats() assert.equal(stats1.keyCount, 3) assert.equal(stats1.evictedCount, 1) assert.equal(byKeys.read('a'), undefined) assert.equal(byKeys.read('d'), 4) // 最近写入的 key 不会被驱逐;更新已存在 key 刷新其新鲜度 byKeys.write('a', 1) byKeys.write('e', 5) assert.equal(byKeys.read('b'), undefined) assert.equal(byKeys.read('a'), 1) assert.equal(byKeys.read('e'), 5) assert.equal(byKeys.stats().keyCount, 3) // 单条超过 maxBytes:拒绝写入,不产生部分数据 const s2 = createFakeStorage() const byBytes = createLocalStorageLimiter({ storage: s2, maxKeys: 10, maxBytes: 100 }) assert.equal(byBytes.write('big', { x: 'y'.repeat(200) }), false) assert.equal(byBytes.read('big'), undefined) assert.equal(byBytes.stats().keyCount, 0) // 批量累积超限:驱逐最旧直到有界 const s3 = createFakeStorage() const limited = createLocalStorageLimiter({ storage: s3, maxKeys: 10, maxBytes: 60 }) for (let i = 1; i <= 5; i++) limited.write(`k${i}`, { v: 'x'.repeat(20) }) const stats3 = limited.stats() assert.equal(stats3.keyCount, 2) assert.equal(stats3.evictedCount, 3) assert.equal(limited.read('k1'), undefined) assert.equal(limited.read('k3'), undefined) assert.ok(limited.read('k4')) assert.ok(limited.read('k5')) }) test('test_task_087_task_invalid_input_rejected', () => { const storage = createFakeStorage() assert.throws(() => createLocalStorageLimiter({ storage, maxKeys: 0, maxBytes: 100 }), /maxKeys 必须为正数/) assert.throws(() => createLocalStorageLimiter({ storage, maxKeys: -1, maxBytes: 100 }), /maxKeys 必须为正数/) assert.throws(() => createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 0 }), /maxBytes 必须为正数/) assert.throws(() => createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: -5 }), /maxBytes 必须为正数/) assert.throws(() => createLocalStorageLimiter({ storage: null as never, maxKeys: 10, maxBytes: 100 }), /storage 必须提供/) const limiter = createLocalStorageLimiter({ storage, maxKeys: 10, maxBytes: 100 }) assert.equal(limiter.write('', 'x'), false) // 不可序列化值:拒绝并保持原状态 const circular: Record = {} circular.self = circular assert.equal(limiter.write('bad', circular), false) assert.equal(limiter.read('bad'), undefined) assert.equal(limiter.stats().keyCount, 0) }) test('test_task_087_task_dependency_failure_releases_resources', () => { const inner = createFakeStorage() const failing: FakeStorage = { getItem: (k) => inner.getItem(k), setItem: (k, v) => { if (k === 'poison') throw new Error('QuotaExceededError') inner.setItem(k, v) }, removeItem: (k) => inner.removeItem(k), } const limiter = createLocalStorageLimiter({ storage: failing, maxKeys: 10, maxBytes: 1024 }) limiter.write('a', 1) // 存储抛错:写入失败但不污染内部状态 assert.equal(limiter.write('poison', { x: 1 }), false) assert.equal(limiter.stats().keyCount, 1, '失败的写入不占条数') assert.equal(limiter.read('a'), 1, '已写入数据不受影响') // 错误可恢复:非故障 key 继续工作 assert.equal(limiter.write('b', 2), true) assert.equal(limiter.read('b'), 2) // remove/clear 释放全部条目 limiter.remove('a') limiter.remove('b') assert.equal(limiter.stats().keyCount, 0) limiter.clear() assert.deepEqual(limiter.stats(), { keyCount: 0, totalBytes: 0, evictedCount: 0 }) })