- Recover the IndexedDB and in-memory document cache behind the storage domain - Add persistent outbox supersession, cache eviction, import validation, and naming helpers - Keep the substrate provider-neutral and cover its pure behavior Co-authored-by: Rob Coenen <753704+rcoenen@users.noreply.github.com>
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { beforeEach, describe, expect, test } from 'bun:test'
|
|
|
|
import { evictLocalFigCache } from '@/app/storage/cache-eviction'
|
|
import {
|
|
createMemoryLocalCanvasStore,
|
|
getLocalCanvasStore,
|
|
resetLocalCanvasStoreForTests
|
|
} from '@/app/storage/local-store'
|
|
import type { LocalSyncStatus } from '@/app/storage/local-store'
|
|
|
|
const MB = 1024 * 1024
|
|
|
|
async function seed(
|
|
id: string,
|
|
sizeMb: number,
|
|
lastOpenedAt: string,
|
|
syncStatus: LocalSyncStatus = 'synced'
|
|
) {
|
|
const local = getLocalCanvasStore()
|
|
await local.writeCanvas({
|
|
id,
|
|
providerId: 's3-compatible',
|
|
name: id,
|
|
figBytes: new Uint8Array(sizeMb * MB),
|
|
syncStatus
|
|
})
|
|
await local.updateMeta(id, { lastOpenedAt })
|
|
}
|
|
|
|
describe('evictLocalFigCache', () => {
|
|
beforeEach(() => {
|
|
resetLocalCanvasStoreForTests(createMemoryLocalCanvasStore())
|
|
})
|
|
|
|
test('does nothing under budget', async () => {
|
|
await seed('a', 2, '2026-01-01')
|
|
const evicted = await evictLocalFigCache(new Set(), 10 * MB)
|
|
expect(evicted).toBe(0)
|
|
})
|
|
|
|
test('evicts least-recently-opened synced figs until under budget', async () => {
|
|
await seed('old', 4, '2026-01-01')
|
|
await seed('mid', 4, '2026-02-01')
|
|
await seed('new', 4, '2026-03-01')
|
|
const evicted = await evictLocalFigCache(new Set(), 8 * MB)
|
|
expect(evicted).toBe(1)
|
|
const local = getLocalCanvasStore()
|
|
const oldMeta = await local.getMeta('old')
|
|
expect(oldMeta?.hasFig).toBe(false)
|
|
expect(await local.readFig('old')).toBeNull()
|
|
// meta row and identity survive — the card stays listed
|
|
expect(oldMeta?.name).toBe('old')
|
|
expect((await local.getMeta('new'))?.hasFig).toBe(true)
|
|
})
|
|
|
|
test('never evicts unsynced or open canvases', async () => {
|
|
await seed('dirty', 4, '2026-01-01', 'pending')
|
|
await seed('open', 4, '2026-01-02')
|
|
await seed('fresh', 4, '2026-03-01')
|
|
const evicted = await evictLocalFigCache(new Set(['open']), 4 * MB)
|
|
const local = getLocalCanvasStore()
|
|
expect((await local.getMeta('dirty'))?.hasFig).toBe(true)
|
|
expect((await local.getMeta('open'))?.hasFig).toBe(true)
|
|
// only 'fresh' was evictable
|
|
expect(evicted).toBe(1)
|
|
expect((await local.getMeta('fresh'))?.hasFig).toBe(false)
|
|
})
|
|
|
|
test('backfills figSize for legacy rows instead of skipping them', async () => {
|
|
await seed('legacy', 6, '2026-01-01')
|
|
const local = getLocalCanvasStore()
|
|
await local.updateMeta('legacy', { figSize: undefined })
|
|
const evicted = await evictLocalFigCache(new Set(), 1 * MB)
|
|
expect(evicted).toBe(1)
|
|
})
|
|
})
|