69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { GuideModelPersistentCache } from '@/services/model/GuideModelPersistentCache'
|
|
|
|
describe('GuideModelPersistentCache', () => {
|
|
const records = new Map<string, Response>()
|
|
const cache = {
|
|
match: vi.fn(async (key: string) => records.get(key)?.clone()),
|
|
put: vi.fn(async (key: string, response: Response) => {
|
|
records.set(key, response.clone())
|
|
}),
|
|
delete: vi.fn(async (key: string) => records.delete(key))
|
|
}
|
|
|
|
beforeEach(() => {
|
|
records.clear()
|
|
cache.match.mockClear()
|
|
cache.put.mockClear()
|
|
cache.delete.mockClear()
|
|
localStorage.clear()
|
|
vi.stubGlobal('caches', { open: vi.fn(async () => cache) })
|
|
vi.stubGlobal('fetch', vi.fn(async () => new Response(new Blob(['model']), { status: 200 })))
|
|
vi.stubGlobal('URL', {
|
|
createObjectURL: vi.fn(() => 'blob:cached-model'),
|
|
revokeObjectURL: vi.fn()
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
it('stores a versioned asset and reuses it from persistent storage', async () => {
|
|
const modelCache = new GuideModelPersistentCache()
|
|
await modelCache.store('https://assets.example.com/exterior.glb', 'release-1')
|
|
|
|
const cached = await modelCache.get('https://assets.example.com/exterior.glb', 'release-1')
|
|
|
|
expect(fetch).toHaveBeenCalledWith('https://assets.example.com/exterior.glb', { cache: 'force-cache' })
|
|
expect(cache.put).toHaveBeenCalledTimes(1)
|
|
expect(cached?.url).toBe('blob:cached-model')
|
|
cached?.release()
|
|
expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:cached-model')
|
|
})
|
|
|
|
it('does not reuse a cached asset from another model version', async () => {
|
|
const modelCache = new GuideModelPersistentCache()
|
|
await modelCache.store('https://assets.example.com/exterior.glb', 'release-1')
|
|
|
|
const cached = await modelCache.get('https://assets.example.com/exterior.glb', 'release-2')
|
|
|
|
expect(cached).toBeNull()
|
|
})
|
|
|
|
it('deletes empty cached payloads before falling back to network', async () => {
|
|
const modelCache = new GuideModelPersistentCache()
|
|
await modelCache.store('https://assets.example.com/exterior.glb', 'release-1')
|
|
records.set(
|
|
'https://assets.example.com/exterior.glb?__sgs_model_version=release-1',
|
|
new Response(new Blob([]), { status: 200 })
|
|
)
|
|
|
|
const cached = await modelCache.get('https://assets.example.com/exterior.glb', 'release-1')
|
|
|
|
expect(cached).toBeNull()
|
|
expect(cache.delete).toHaveBeenCalled()
|
|
})
|
|
})
|