/** * `control_plane_api` reaches a consuming module, and only a consuming module. * * The rest of `loadCapabilityFunctions` hands over every capability it can * build, declared or not — its own comment says "not just required ones", and * `capability-loader-bindings.test.ts` asserts that shape deliberately. That is * fine for a capability whose worst outcome is an unused OIDC client. * * This one mints an SSH principal into celilo's own control plane, so it is * gated on the `requires` line instead. These pin both halves, because the * failure that matters is silent in both directions: a console that declared it * and got nothing fails at install with "no provider is loaded" for a capability * no module ever provides, and a module that declared nothing and got it anyway * holds control-plane read that no manifest review would have shown. */ import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; import type { HookLogger } from '@celilo/capabilities'; import type { DbClient } from '../db/client'; import { cleanupTestDatabase, setupTestDatabase } from '../test-utils/database'; import { loadCapabilityFunctions } from './capability-loader'; const noopLogger: HookLogger = { info() {}, warn() {}, error() {}, success() {}, }; function installModule(db: DbClient, moduleId: string, manifest: Record): void { db.$client.run( `INSERT INTO modules (id, name, version, source_path, manifest_data) VALUES (?, ?, '1.0.0', '/tmp/${moduleId}', ?)`, [moduleId, moduleId, JSON.stringify(manifest)], ); } const DECLARES = { requires: { capabilities: [{ name: 'control_plane_api', version: '1.0.0' }] }, }; describe('control_plane_api injection', () => { let db: DbClient; beforeEach(async () => { db = await setupTestDatabase(); }); afterEach(async () => { await cleanupTestDatabase(db); }); test('a module that requires it gets it, with no provider module deployed', async () => { // The point of the whole decision: nothing provides this capability, and it // is still there. A test that first deployed a provider would be measuring // the ordinary path. installModule(db, 'celilo-web-console', DECLARES); const capabilities = await loadCapabilityFunctions('celilo-web-console', db, noopLogger); expect(capabilities.control_plane_api).toBeTruthy(); const api = capabilities.control_plane_api as Record; expect(typeof api.enrol_principal).toBe('function'); expect(typeof api.revoke_principal).toBe('function'); }); test('a module that declares it as optional gets it too', async () => { // `optional` is a declaration a reviewer reads on the same line of the same // file. Treating it as no declaration would fail the module at runtime for a // capability its manifest names. installModule(db, 'someday-console', { optional: { capabilities: [{ name: 'control_plane_api', version: '1.0.0' }] }, }); const capabilities = await loadCapabilityFunctions('someday-console', db, noopLogger); expect(capabilities.control_plane_api).toBeTruthy(); }); test('a module that declares nothing does NOT get it', async () => { installModule(db, 'hello-foo', { requires: { capabilities: [] } }); const capabilities = await loadCapabilityFunctions('hello-foo', db, noopLogger); expect(capabilities.control_plane_api).toBeUndefined(); }); test('a module that requires something ELSE does NOT get it', async () => { // The near miss: a manifest with a populated `requires` block is the shape a // too-loose check ("does this module require anything?") would wave through. installModule(db, 'hello-bar', { requires: { capabilities: [{ name: 'idp', version: '1.0.0' }] }, }); const capabilities = await loadCapabilityFunctions('hello-bar', db, noopLogger); expect(capabilities.control_plane_api).toBeUndefined(); }); test('a module celilo has no record of does NOT get it', async () => { // A hook can run for a module id with no row (a removal path, a typo). The // manifest lookup returns nothing, and "no manifest" must read as "declared // nothing" rather than throwing or defaulting open. const capabilities = await loadCapabilityFunctions('never-imported', db, noopLogger); expect(capabilities.control_plane_api).toBeUndefined(); }); test('the injected object is scoped to the module it was built for', async () => { // Scoping is what stops a module rotating a neighbour's key. It is asserted // through the loader rather than only on `buildControlPlaneApi`, because the // loader is where the wrong argument (a provider id, a hook name) would be // passed. installModule(db, 'hello-foo', DECLARES); const capabilities = await loadCapabilityFunctions('hello-foo', db, noopLogger); const api = capabilities.control_plane_api as { revoke_principal(request: { name: string }): Promise; }; await expect(api.revoke_principal({ name: 'celilo-web-console' })).rejects.toThrow( /only revoke its own principal/, ); }); });