import chai, { expect } from 'chai'; import asPromised from 'chai-as-promised'; import { before, describe, it } from 'mocha'; import dayjs from "dayjs"; import withLocalTmpDir from 'with-local-tmp-dir'; import { initFileCache, initMemoryCache, initValkeyCache } from "../../common/Cache.ts"; import { ListenProgressPositional, ListenProgressTS } from "../../sources/PlayerState/ListenProgress.ts"; import { isPortReachableConnect } from "../../utils/NetworkUtils.ts"; import { sleep } from "../../utils.ts"; chai.use(asPromised); describe('#Caching', function () { describe('#MemoryCaching', function () { it('Memory cache preserves dayjs', async function () { const cache = initMemoryCache(); const now = dayjs(); await cache.set('foo', now); const time = await cache.get('foo'); expect(time).to.not.be.undefined; expect(dayjs.isDayjs(time)).is.true; expect(now.toJSON()).eq((time as any).toJSON()); }); it('Memory cache preserves ListenProgress', async function () { const cache = initMemoryCache(); const prog = new ListenProgressPositional({ timestamp: dayjs(), position: 35, positionPercent: 50 }); await cache.set('foo', prog); const cachedProg = await cache.get('foo') as ListenProgressTS; expect(cachedProg).to.not.be.undefined; expect(cachedProg instanceof ListenProgressTS).is.true; expect(cachedProg.timestamp.toJSON()).eq(prog.timestamp.toJSON()); }); }); describe('#FileCaching', function () { it('File cache serializes and deserializes dayjs', async function () { await withLocalTmpDir(async () => { // for some reason this *recreates* an empty cache after the test has finished (when running the full suite) // i think its because of long persist interval compared to test time? // // so: // make intervals very small // call destroy on both cache instances (shouldn't be necessary) // sleep for longer than persist interal so tmp dir callback can (hopefully) properly delete any files const [keyv, flat] = await initFileCache({ cacheDir: process.cwd(), persistInterval: 5, expirationInterval: 4 }); const now = dayjs(); await keyv.set('foo', now); flat.save(true); keyv.disconnect(); const [cleanKeyv, cleanFlat] = await initFileCache({ cacheDir: process.cwd(), persistInterval: 5, expirationInterval: 4 }); const time = await cleanKeyv.get('foo'); expect(time).to.not.be.undefined; expect(time instanceof dayjs).is.true; expect(now.toJSON()).eq((time as any).toJSON()); flat.destroy(); cleanFlat.destroy(); await sleep(10); }, { unsafeCleanup: true, postfix: 'fileCacheDajys' }); }); }); describe('#ValkeyCaching', function () { before(async function () { try { await isPortReachableConnect(6379, { host: 'valkey' }); } catch (e) { // don't run valkey tests if valkey isn't present this.skip(); } }); it('Valkey cache serializes and deserializes dayjs', async function () { const keyv = await initValkeyCache('test', 'redis://valkey:6379'); await keyv.clear(); const now = dayjs(); await keyv.set('foo', now); const time = await keyv.get('foo'); expect(time).to.not.be.undefined; expect(time instanceof dayjs).is.true; expect(now.toJSON()).eq((time as any).toJSON()); }); }); });