import { expect, test } from '@rstest/core'; import { trackIdFromUrl, cacheKeyFor, parseRangeHeader, selectEvictions, } from '../public/sw-core.js'; test('trackIdFromUrl extracts the content id from a stream URL', () => { expect( trackIdFromUrl('https://host/api/v1/streaming/tracks/abc123?token=xyz'), ).toBe('abc123'); expect(trackIdFromUrl('https://host/api/v1/library/albums')).toBeNull(); }); test('cacheKeyFor strips the token so the key is token-stable', () => { const a = cacheKeyFor('https://host/api/v1/streaming/tracks/t1?token=AAA'); const b = cacheKeyFor('https://host/api/v1/streaming/tracks/t1?token=BBB'); expect(a).toBe(b); expect(a).toBe('https://host/api/v1/streaming/tracks/t1'); }); test('cacheKeyFor keeps different origins distinct', () => { expect(cacheKeyFor('https://a/streaming/tracks/t1?token=x')).not.toBe( cacheKeyFor('https://b/streaming/tracks/t1?token=x'), ); }); test('parseRangeHeader: closed range', () => { expect(parseRangeHeader('bytes=0-99', 1000)).toEqual({ start: 0, end: 99 }); }); test('parseRangeHeader: open-ended range clamps to size', () => { expect(parseRangeHeader('bytes=500-', 1000)).toEqual({ start: 500, end: 999, }); }); test('parseRangeHeader: suffix range (last N bytes)', () => { expect(parseRangeHeader('bytes=-200', 1000)).toEqual({ start: 800, end: 999, }); }); test('parseRangeHeader: end past size is clamped', () => { expect(parseRangeHeader('bytes=900-5000', 1000)).toEqual({ start: 900, end: 999, }); }); test('parseRangeHeader: invalid / no range returns null', () => { expect(parseRangeHeader('', 1000)).toBeNull(); expect(parseRangeHeader('items=0-1', 1000)).toBeNull(); expect(parseRangeHeader('bytes=500-100', 1000)).toBeNull(); }); test('selectEvictions: nothing evicted when under cap', () => { const index = { a: { size: 100, lastAccess: 1 }, b: { size: 100, lastAccess: 2 }, }; expect(selectEvictions(index, 100, 1000)).toEqual([]); }); test('selectEvictions: evicts least-recently-used first until it fits', () => { const index = { a: { size: 400, lastAccess: 10 }, // oldest b: { size: 400, lastAccess: 30 }, c: { size: 400, lastAccess: 20 }, }; // total 1200 + incoming 400 = 1600, cap 1000 → must free >=600. // LRU order: a (10), c (20). Evict a (1200→800... wait incl incoming) const evicted = selectEvictions(index, 400, 1000); // total with incoming = 1600; evict a → 1200; evict c → 800 <= 1000. expect(evicted).toEqual(['a', 'c']); });