import test from 'brittle'
import Corestore from 'corestore'
import path from 'bare-path'
import fs from 'bare-fs'
import os from 'bare-os'
import { MediaStore } from '../../lib/media-store.js'
function tmpdir () {
const dir = path.join(os.tmpdir(), 'hn-vgc-test-' + Date.now() + '-' + Math.random().toString(36).slice(2))
fs.mkdirSync(dir, { recursive: true })
return dir
}
function rmrf (dir) {
try { fs.rmSync(dir, { recursive: true, force: true }) } catch {}
}
test('Video GC - cascade clears poster, manifest, segments', async (t) => {
const dir = tmpdir()
try {
const store = new Corestore(dir)
const media = new MediaStore(store, {
graceMs: 50,
isReferencedAnywhere: async () => false
})
await media.ready()
// Simulate a processed video: original + poster + manifest + segments
const originalHash = await media.put(Buffer.from('original video data'), 'video/mp4')
const posterHash = await media.put(Buffer.from('jpeg poster'), 'image/jpeg')
const manifestHash = await media.put(Buffer.from('#EXTM3U\nmaster'), 'application/vnd.apple.mpegurl')
const m3u8Hash = await media.put(Buffer.from('#EXTINF:6\nseg'), 'application/vnd.apple.mpegurl')
const seg1Hash = await media.put(Buffer.from('segment 1 ts data'), 'video/mp2t')
const seg2Hash = await media.put(Buffer.from('segment 2 ts data'), 'video/mp2t')
// Write video index
await media._index.put('video:' + originalHash, {
status: 'ready',
poster: posterHash,
manifest: manifestHash,
levels: [{
name: '480p',
m3u8: m3u8Hash,
segments: [seg1Hash, seg2Hash]
}]
})
// All blobs exist
t.is(await media.has(originalHash), true)
t.is(await media.has(posterHash), true)
t.is(await media.has(manifestHash), true)
t.is(await media.has(seg1Hash), true)
t.is(await media.has(seg2Hash), true)
// Schedule orphan - should cascade clear everything
media.scheduleOrphan(originalHash)
await new Promise(r => setTimeout(r, 200))
// All blobs should be cleared
t.is(await media.has(originalHash), false)
t.is(await media.has(posterHash), false)
t.is(await media.has(manifestHash), false)
t.is(await media.has(m3u8Hash), false)
t.is(await media.has(seg1Hash), false)
t.is(await media.has(seg2Hash), false)
// Video index should be deleted
const videoEntry = await media._index.get('video:' + originalHash)
t.is(videoEntry, null)
await store.close()
} finally {
rmrf(dir)
}
})
test('Video GC - non-video blob clears normally without cascade', async (t) => {
const dir = tmpdir()
try {
const store = new Corestore(dir)
const media = new MediaStore(store, {
graceMs: 50,
isReferencedAnywhere: async () => false
})
await media.ready()
const hash = await media.put(Buffer.from('just an image'), 'image/jpeg')
t.is(await media.has(hash), true)
media.scheduleOrphan(hash)
await new Promise(r => setTimeout(r, 200))
t.is(await media.has(hash), false)
await store.close()
} finally {
rmrf(dir)
}
})