import type { IStorageHooks, IStorageHookContext } from '../../ts/core/interfaces.storage.js'; /** * Create test storage hooks that track all calls. * Useful for verifying hook invocation order and parameters. */ export function createTrackingHooks(options?: { beforePutAllowed?: boolean; beforeDeleteAllowed?: boolean; throwOnAfterPut?: boolean; throwOnAfterGet?: boolean; }): { hooks: IStorageHooks; calls: Array<{ method: string; context: IStorageHookContext; timestamp: number }>; } { const calls: Array<{ method: string; context: IStorageHookContext; timestamp: number }> = []; return { calls, hooks: { beforePut: async (ctx) => { calls.push({ method: 'beforePut', context: ctx, timestamp: Date.now() }); return { allowed: options?.beforePutAllowed !== false, reason: options?.beforePutAllowed === false ? 'Blocked by test' : undefined, }; }, afterPut: async (ctx) => { calls.push({ method: 'afterPut', context: ctx, timestamp: Date.now() }); if (options?.throwOnAfterPut) { throw new Error('Test error in afterPut'); } }, beforeDelete: async (ctx) => { calls.push({ method: 'beforeDelete', context: ctx, timestamp: Date.now() }); return { allowed: options?.beforeDeleteAllowed !== false, reason: options?.beforeDeleteAllowed === false ? 'Blocked by test' : undefined, }; }, afterDelete: async (ctx) => { calls.push({ method: 'afterDelete', context: ctx, timestamp: Date.now() }); }, afterGet: async (ctx) => { calls.push({ method: 'afterGet', context: ctx, timestamp: Date.now() }); if (options?.throwOnAfterGet) { throw new Error('Test error in afterGet'); } }, }, }; } /** * Create a blocking storage hooks implementation for quota testing. */ export function createQuotaHooks(maxSizeBytes: number): { hooks: IStorageHooks; currentUsage: { bytes: number }; } { const currentUsage = { bytes: 0 }; return { currentUsage, hooks: { beforePut: async (ctx) => { const size = ctx.metadata?.size || 0; if (currentUsage.bytes + size > maxSizeBytes) { return { allowed: false, reason: `Quota exceeded: ${currentUsage.bytes + size} > ${maxSizeBytes}` }; } return { allowed: true }; }, afterPut: async (ctx) => { currentUsage.bytes += ctx.metadata?.size || 0; }, afterDelete: async (ctx) => { currentUsage.bytes -= ctx.metadata?.size || 0; if (currentUsage.bytes < 0) currentUsage.bytes = 0; }, }, }; }