/** * Test fixture: a hook that exercises every shape a capability call can take * across the process boundary. * * One test per SHAPE rather than per method: the broker is generic, so a * per-method suite would prove the same thing thirty-seven times and drift the * moment a capability gained a method. * * Reports through a JSON file in its state directory: hook return values are * no longer carried anywhere (hook-owned-state D5). */ import { writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { defineHook, isMissingProviderInputError } from '@celilo/capabilities'; interface DemoCapability { providerModuleId?: string; version?: string; echo(request: Record): Promise; boom(request: Record): Promise; missingInput(request: Record): Promise; returnsNothing(request: Record): Promise; /** Never implemented by this provider — the absent-optional-method case. */ sometimesAbsent?(request: Record): Promise; } export default defineHook({ hook: 'container_created', requires: [], handler: async (ctx) => { // `demo` is a fixture capability, not a registry entry, so the typed // capability map does not know it. The broker is generic and does not care. const demo = (ctx.capabilities as unknown as Record).demo; const report: Record = {}; // Non-function properties are copied verbatim, which is what keeps // `providerModuleId` readable — a hook names the provider in its errors. report.providerModuleId = demo.providerModuleId; report.version = demo.version; // An optional method the provider did not implement must be ABSENT, not a // proxy that throws, or `if (cap.registerTrustedSource)` answers wrongly. report.optionalMethodAbsent = demo.sometimesAbsent === undefined; report.returned = await demo.echo({ x: 1, nested: { y: [2, 3] } }); report.undefinedBecomesNull = await demo.returnsNothing({}); try { await demo.boom({}); report.plainThrow = 'did not throw'; } catch (error) { report.plainThrow = { isError: error instanceof Error, name: (error as Error).name, message: (error as Error).message, hasStack: typeof (error as Error).stack === 'string', }; } try { await demo.missingInput({}); report.missingProviderInput = 'did not throw'; } catch (error) { const e = error as Record; report.missingProviderInput = { recognised: isMissingProviderInputError(error), providerModuleId: e.providerModuleId, ensureId: e.ensureId, value: e.value, humanContext: e.humanContext, }; } try { await (demo as unknown as { nope(): Promise }).nope(); report.unknownMethod = 'did not throw'; } catch (error) { report.unknownMethod = (error as Error).message; } // Hand the report to the caller through the state directory, the one // channel a hook still has. writeFileSync(join(ctx.stateDir as string, 'report.json'), JSON.stringify(report)); }, });