070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
// Plan §3.K3 / spec §10.3.5 — od.genui.surfaces[].component manifest field.
|
|
//
|
|
// Two contracts:
|
|
// 1. The Zod schema in @open-design/contracts accepts the new
|
|
// `component: { path, export?, sandbox? }` field on a surface.
|
|
// 2. doctorPlugin() flags a surface that ships a component without
|
|
// the matching `genui:custom-component` capability, and rejects
|
|
// path-traversal segments.
|
|
// 3. validateCapabilityList accepts `genui:custom-component` as a
|
|
// first-class top-level capability.
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import { GenUISurfaceSpecSchema } from '@open-design/contracts';
|
|
import { validateSafe } from '@open-design/plugin-runtime';
|
|
import { doctorPlugin } from '../src/plugins/doctor.js';
|
|
import { validateCapabilityList } from '../src/plugins/trust.js';
|
|
import { FIRST_PARTY_ATOMS, type AtomCatalogEntry } from '../src/plugins/atoms.js';
|
|
import type { InstalledPluginRecord, PluginManifest } from '@open-design/contracts';
|
|
|
|
const REGISTRY = {
|
|
skills: [],
|
|
designSystems: [],
|
|
craft: [],
|
|
atoms: FIRST_PARTY_ATOMS.map((a: AtomCatalogEntry) => ({ id: a.id, label: a.label })),
|
|
};
|
|
|
|
function pluginRecord(manifest: PluginManifest): InstalledPluginRecord {
|
|
return {
|
|
id: manifest.name,
|
|
title: manifest.title ?? manifest.name,
|
|
version: manifest.version,
|
|
sourceKind: 'local',
|
|
source: '/tmp/test',
|
|
pinnedRef: undefined,
|
|
sourceMarketplaceId: undefined,
|
|
trust: 'restricted',
|
|
capabilitiesGranted: ['prompt:inject'],
|
|
manifest,
|
|
fsPath: '/tmp/test',
|
|
installedAt: 0,
|
|
updatedAt: 0,
|
|
};
|
|
}
|
|
|
|
describe('GenUISurfaceSpec.component (manifest schema)', () => {
|
|
it('accepts a component path + export + sandbox triple', () => {
|
|
const result = GenUISurfaceSpecSchema.safeParse({
|
|
id: 'critique-panel',
|
|
kind: 'choice',
|
|
persist: 'run',
|
|
component: { path: './surfaces/critique-panel.tsx', export: 'CritiquePanel', sandbox: 'react' },
|
|
});
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('rejects an empty component.path', () => {
|
|
const result = GenUISurfaceSpecSchema.safeParse({
|
|
id: 'critique-panel',
|
|
kind: 'choice',
|
|
persist: 'run',
|
|
component: { path: '' },
|
|
});
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('validateCapabilityList — genui:custom-component', () => {
|
|
it('treats genui:custom-component as a first-class top-level capability', () => {
|
|
const { accepted, rejected } = validateCapabilityList([
|
|
'prompt:inject',
|
|
'genui:custom-component',
|
|
]);
|
|
expect(accepted.sort()).toEqual(['genui:custom-component', 'prompt:inject']);
|
|
expect(rejected).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('doctorPlugin — component capability gate', () => {
|
|
const baseManifest: PluginManifest = {
|
|
name: 'sample-plugin',
|
|
title: 'Sample',
|
|
version: '1.0.0',
|
|
description: 'fixture',
|
|
od: {
|
|
kind: 'skill',
|
|
genui: {
|
|
surfaces: [
|
|
{
|
|
id: 'critique-panel',
|
|
kind: 'choice',
|
|
persist: 'run',
|
|
component: { path: './surfaces/critique-panel.tsx' },
|
|
},
|
|
],
|
|
},
|
|
capabilities: ['prompt:inject'],
|
|
},
|
|
};
|
|
|
|
it('errors when a surface ships a component without genui:custom-component', () => {
|
|
expect(validateSafe(baseManifest).ok).toBe(true);
|
|
const report = doctorPlugin(pluginRecord(baseManifest), REGISTRY);
|
|
const codes = report.issues.map((d) => d.code);
|
|
expect(codes).toContain('genui.component-capability');
|
|
expect(report.ok).toBe(false);
|
|
});
|
|
|
|
it('passes when the matching capability is declared', () => {
|
|
const m: PluginManifest = {
|
|
...baseManifest,
|
|
od: { ...baseManifest.od, capabilities: ['prompt:inject', 'genui:custom-component'] },
|
|
};
|
|
const report = doctorPlugin(pluginRecord(m), REGISTRY);
|
|
expect(report.issues.find((d) => d.code === 'genui.component-capability')).toBeUndefined();
|
|
});
|
|
|
|
it('errors on path-traversal segments inside the component path', () => {
|
|
const m: PluginManifest = {
|
|
...baseManifest,
|
|
od: {
|
|
...baseManifest.od,
|
|
capabilities: ['prompt:inject', 'genui:custom-component'],
|
|
genui: {
|
|
surfaces: [
|
|
{
|
|
id: 'critique-panel',
|
|
kind: 'choice',
|
|
persist: 'run',
|
|
component: { path: '../escape/panel.tsx' },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
const report = doctorPlugin(pluginRecord(m), REGISTRY);
|
|
expect(report.issues.find((d) => d.code === 'genui.component-traversal')).toBeDefined();
|
|
expect(report.ok).toBe(false);
|
|
});
|
|
});
|