diff --git a/plugins/reporting-governance/test/profile-generator.test.mjs b/plugins/reporting-governance/test/profile-generator.test.mjs index fce7e2c..01287a2 100644 --- a/plugins/reporting-governance/test/profile-generator.test.mjs +++ b/plugins/reporting-governance/test/profile-generator.test.mjs @@ -43,6 +43,8 @@ spec: overdueAction: force_checkpoint escalationAfterMisses: 2 adapters: + hook: + enabled: true watchdog: enabled: true scheduleMode: cron @@ -98,7 +100,15 @@ test('deployment profile schema validator rejects malformed profile', () => { metadata: { id: 'x', version: '1.0.0', runtime: 'openclaw' }, spec: { package: { pluginVersion: 7 }, - adapters: {}, + adapters: { + hook: { enabled: true }, + watchdog: { enabled: true }, + queue: { enabled: true }, + dispatcher: { enabled: true }, + bridge: { enabled: true }, + sender: { mode: 'openclaw-cli' }, + orchestrator: { enabled: true }, + }, notifications: { operatorVisibleRecoveryRequired: true, allowedTerminalStates: ['acked'], @@ -111,6 +121,137 @@ test('deployment profile schema validator rejects malformed profile', () => { ); }); +test('deployment profile schema validator rejects tightened semantic violations', () => { + const validBase = { + apiVersion: 'reporting-governance/v1alpha1', + kind: 'DeploymentProfile', + metadata: { id: 'strict', version: '1.0.0', runtime: 'openclaw' }, + spec: { + package: { pluginVersion: '0.1.0-mainline' }, + adapters: { + hook: { enabled: true }, + watchdog: { enabled: true }, + queue: { enabled: true }, + dispatcher: { enabled: true }, + bridge: { enabled: true }, + sender: { mode: 'openclaw-cli' }, + orchestrator: { enabled: true }, + }, + notifications: { + operatorVisibleRecoveryRequired: true, + allowedTerminalStates: ['acked'], + }, + audit: { + portableArtifactsRequired: true, + requiredArtifacts: ['queue_items'], + }, + capability_expectations: { required: [] }, + }, + }; + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + audit: { + portableArtifactsRequired: true, + requiredArtifacts: [], + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/audit\/requiredArtifacts must NOT have fewer than 1 items/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + notifications: { + operatorVisibleRecoveryRequired: 'yes', + allowedTerminalStates: ['acked'], + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/notifications\/operatorVisibleRecoveryRequired must be boolean/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + notifications: { + operatorVisibleRecoveryRequired: true, + allowedTerminalStates: ['done'], + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/notifications\/allowedTerminalStates\/0 must be equal to one of the allowed values/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + audit: { + portableArtifactsRequired: 'true', + requiredArtifacts: ['queue_items'], + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/audit\/portableArtifactsRequired must be boolean/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + adapters: { + ...validBase.spec.adapters, + queue: { enabled: 'true' }, + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/adapters\/queue\/enabled must be boolean/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + adapters: { + ...validBase.spec.adapters, + sender: { mode: 'shim' }, + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/adapters\/sender\/mode must be equal to one of the allowed values/ + ); + + assert.throws( + () => validateDeploymentProfileSchema({ + ...validBase, + spec: { + ...validBase.spec, + adapters: { + watchdog: { enabled: true }, + queue: { enabled: true }, + dispatcher: { enabled: true }, + bridge: { enabled: true }, + sender: { mode: 'openclaw-cli' }, + orchestrator: { enabled: true }, + }, + }, + }), + /deployment profile schema validation failed: .*\/spec\/adapters missing required property hook/ + ); +}); + test('deployment profile yaml parser covers composite nested yaml structure', () => { const profile = parseDeploymentProfileYaml(complexProfileYaml); @@ -159,7 +300,21 @@ metadata: spec: package: pluginVersion: 123 - adapters: {} + adapters: + hook: + enabled: true + watchdog: + enabled: true + queue: + enabled: true + dispatcher: + enabled: true + bridge: + enabled: true + sender: + mode: openclaw-cli + orchestrator: + enabled: true notifications: operatorVisibleRecoveryRequired: true allowedTerminalStates: diff --git a/schemas/reporting-governance/deployment-profile.schema.json b/schemas/reporting-governance/deployment-profile.schema.json index 575a35a..e7597e2 100644 --- a/schemas/reporting-governance/deployment-profile.schema.json +++ b/schemas/reporting-governance/deployment-profile.schema.json @@ -42,17 +42,97 @@ "adapters": { "type": "object", "additionalProperties": true, - "required": ["watchdog", "queue", "dispatcher", "bridge", "sender", "orchestrator"] + "required": ["hook", "watchdog", "queue", "dispatcher", "bridge", "sender", "orchestrator"], + "properties": { + "hook": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + }, + "watchdog": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + }, + "queue": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + }, + "dispatcher": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + }, + "bridge": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + }, + "sender": { + "type": "object", + "additionalProperties": true, + "required": ["mode"], + "properties": { + "mode": { + "type": "string", + "enum": ["openclaw-cli", "shim_or_openclaw_cli", "dry-run_or_external"] + }, + "enabled": { "type": "boolean" } + } + }, + "orchestrator": { + "type": "object", + "additionalProperties": true, + "required": ["enabled"], + "properties": { + "enabled": { "type": "boolean" } + } + } + } }, "notifications": { "type": "object", "additionalProperties": true, - "required": ["operatorVisibleRecoveryRequired", "allowedTerminalStates"] + "required": ["operatorVisibleRecoveryRequired", "allowedTerminalStates"], + "properties": { + "operatorVisibleRecoveryRequired": { "type": "boolean" }, + "allowedTerminalStates": { + "type": "array", + "items": { + "type": "string", + "enum": ["acked", "blocked", "pending_external_send"] + } + } + } }, "audit": { "type": "object", "additionalProperties": true, - "required": ["portableArtifactsRequired", "requiredArtifacts"] + "required": ["portableArtifactsRequired", "requiredArtifacts"], + "properties": { + "portableArtifactsRequired": { "type": "boolean" }, + "requiredArtifacts": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 } + } + } }, "capability_expectations": { "type": "object",