/** * TestWrappingAdapter: Real COW storage adapter for testing * * This adapter ACTUALLY wraps binary data in JSON (like production storage) * to properly test the unwrap logic in BlobStorage. * * Unlike InMemoryCOWAdapter which stores Buffers directly, * this adapter simulates real storage behavior: * 1. Compresses with gzip * 2. Wraps binary as {_binary: true, data: "base64..."} * 3. Parses JSON on read (returns JS object, not Buffer) * * This ensures tests exercise the ACTUAL code path that caused v5.10.0 regression. */ import { COWStorageAdapter } from '../../src/storage/cow/BlobStorage.js' import { gzip, gunzip } from 'zlib' import { promisify } from 'util' const gzipAsync = promisify(gzip) const gunzipAsync = promisify(gunzip) export class TestWrappingAdapter implements COWStorageAdapter { private storage = new Map() async get(key: string): Promise { const compressed = this.storage.get(key) if (!compressed) { return undefined } // Decompress (like real storage) const decompressed = await gunzipAsync(compressed) // Parse JSON (like real storage) // This returns a JS object: {_binary: true, data: "base64..."} // NOT a Buffer! return JSON.parse(decompressed.toString()) } async put(key: string, data: Buffer): Promise { // Determine if data is JSON or binary let obj: any try { // Try to parse as JSON (for metadata) obj = JSON.parse(data.toString()) } catch { // Not JSON - wrap as binary (like production) obj = { _binary: true, data: data.toString('base64') } } // Stringify to JSON const jsonStr = JSON.stringify(obj) // Compress (like real storage) const compressed = await gzipAsync(Buffer.from(jsonStr)) // Store compressed data this.storage.set(key, compressed) } async delete(key: string): Promise { this.storage.delete(key) } async list(prefix: string): Promise { const keys: string[] = [] for (const key of this.storage.keys()) { if (key.startsWith(prefix)) { keys.push(key) } } return keys } /** * Clear all storage (for test cleanup) */ clear(): void { this.storage.clear() } /** * Get storage size (for testing) */ size(): number { return this.storage.size } }