67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
import {
|
|
listStorageFiles,
|
|
removeStorageObjects,
|
|
STORAGE_BATCH_SIZE,
|
|
type StorageBucketPort,
|
|
type StorageEntry,
|
|
} from './storage-objects.ts'
|
|
|
|
function assertEquals(actual: unknown, expected: unknown, message: string): void {
|
|
const a = JSON.stringify(actual)
|
|
const e = JSON.stringify(expected)
|
|
if (a !== e) throw new Error(`${message}: expected ${e}, got ${a}`)
|
|
}
|
|
|
|
class FakeBucket implements StorageBucketPort {
|
|
readonly bucket = 'audio'
|
|
listCalls: Array<{ prefix: string; offset: number }> = []
|
|
removed: string[][] = []
|
|
constructor(private tree: Record<string, StorageEntry[]>, private failRemoveAt = -1) {}
|
|
list(prefix: string, offset: number, limit: number): Promise<StorageEntry[]> {
|
|
this.listCalls.push({ prefix, offset })
|
|
return Promise.resolve((this.tree[prefix] ?? []).slice(offset, offset + limit))
|
|
}
|
|
remove(paths: string[]): Promise<void> {
|
|
if (this.removed.length === this.failRemoveAt) return Promise.reject(new Error('storage_delete_failed:audio'))
|
|
this.removed.push(paths)
|
|
return Promise.resolve()
|
|
}
|
|
}
|
|
|
|
Deno.test('storage-objects: listStorageFiles walks folders and pages', async () => {
|
|
const many = Array.from({ length: STORAGE_BATCH_SIZE + 3 }, (_, i) => ({ name: `f${i}.wav`, id: `id${i}` }))
|
|
const bucket = new FakeBucket({
|
|
u: [{ name: 'history', id: null }, { name: 'top.wav', id: 'top' }],
|
|
'u/history': many,
|
|
})
|
|
const files = await listStorageFiles(bucket, 'u')
|
|
|
|
assertEquals(files.length, STORAGE_BATCH_SIZE + 4, 'all files found')
|
|
assertEquals(files[files.length - 1], 'u/top.wav', 'depth-first order')
|
|
assertEquals(
|
|
bucket.listCalls,
|
|
[{ prefix: 'u', offset: 0 }, { prefix: 'u/history', offset: 0 }, { prefix: 'u/history', offset: STORAGE_BATCH_SIZE }],
|
|
'paged listing',
|
|
)
|
|
})
|
|
|
|
Deno.test('storage-objects: removeStorageObjects batches and stops on the first failure', async () => {
|
|
const paths = Array.from({ length: STORAGE_BATCH_SIZE * 2 + 1 }, (_, i) => `u/${i}`)
|
|
const ok = new FakeBucket({})
|
|
await removeStorageObjects(ok, paths)
|
|
assertEquals(ok.removed.map((b) => b.length), [STORAGE_BATCH_SIZE, STORAGE_BATCH_SIZE, 1], 'batch sizes')
|
|
|
|
const failing = new FakeBucket({}, 1)
|
|
let message = ''
|
|
try {
|
|
await removeStorageObjects(failing, paths)
|
|
} catch (error) {
|
|
message = error instanceof Error ? error.message : ''
|
|
}
|
|
assertEquals(message, 'storage_delete_failed:audio', 'error propagates')
|
|
assertEquals(failing.removed.length, 1, 'later batches are not attempted')
|
|
|
|
const empty = new FakeBucket({})
|
|
await removeStorageObjects(empty, [])
|
|
assertEquals(empty.removed.length, 0, 'nothing to remove')
|
|
})
|