feat(reporting-governance): schema-validate profile generation
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import {
|
||||
parseDeploymentProfileYaml,
|
||||
@@ -14,6 +16,66 @@ import { validateDeploymentProfileArtifact } from '../src/storage/profile-artifa
|
||||
const packageRoot = path.resolve(import.meta.dirname, '..');
|
||||
const repoRoot = path.resolve(packageRoot, '..', '..');
|
||||
|
||||
const complexProfileYaml = `apiVersion: reporting-governance/v1alpha1
|
||||
kind: DeploymentProfile
|
||||
metadata:
|
||||
id: demo
|
||||
version: 1.0.0
|
||||
runtime: openclaw
|
||||
title: Demo Profile
|
||||
summary: >-
|
||||
Complex profile for YAML parser coverage with nested objects,
|
||||
arrays, and folded scalars.
|
||||
spec:
|
||||
package:
|
||||
pluginVersion: 0.1.0-mainline
|
||||
compatibility:
|
||||
adapterInterface: reporting-governance-adapter-interface
|
||||
deploymentModel: reporting-governance-deployment-model
|
||||
policies:
|
||||
enabledPacks:
|
||||
- no-silence
|
||||
- verified-completion-only
|
||||
overrides:
|
||||
completion:
|
||||
minQuality: moderate
|
||||
checkpoints:
|
||||
overdueAction: force_checkpoint
|
||||
escalationAfterMisses: 2
|
||||
adapters:
|
||||
watchdog:
|
||||
enabled: true
|
||||
scheduleMode: cron
|
||||
queue:
|
||||
enabled: true
|
||||
dispatcher:
|
||||
enabled: true
|
||||
bridge:
|
||||
enabled: true
|
||||
truthfulDeliveryStates:
|
||||
- dispatched
|
||||
- acked
|
||||
sender:
|
||||
mode: openclaw-cli
|
||||
orchestrator:
|
||||
enabled: true
|
||||
notifications:
|
||||
operatorVisibleRecoveryRequired: true
|
||||
allowedTerminalStates:
|
||||
- acked
|
||||
- blocked
|
||||
audit:
|
||||
portableArtifactsRequired: true
|
||||
requiredArtifacts:
|
||||
- queue_items
|
||||
- bridge_receipts
|
||||
capability_expectations:
|
||||
required:
|
||||
- create_queue_items
|
||||
preferred:
|
||||
- direct_sender_binding
|
||||
`;
|
||||
|
||||
test('deployment profile yaml parser reads strict-manager-mode profile shape', () => {
|
||||
const artifact = generateDeploymentProfileArtifactFromFile(path.join(repoRoot, 'profiles', 'strict-manager-mode.yaml'));
|
||||
|
||||
@@ -26,7 +88,7 @@ test('deployment profile yaml parser reads strict-manager-mode profile shape', (
|
||||
test('deployment profile schema validator rejects malformed profile', () => {
|
||||
assert.throws(
|
||||
() => validateDeploymentProfileSchema({ kind: 'DeploymentProfile' }),
|
||||
/deployment profile apiVersion must be reporting-governance\/v1alpha1/
|
||||
/deployment profile schema validation failed: .*missing required property apiVersion/
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
@@ -35,19 +97,32 @@ test('deployment profile schema validator rejects malformed profile', () => {
|
||||
kind: 'DeploymentProfile',
|
||||
metadata: { id: 'x', version: '1.0.0', runtime: 'openclaw' },
|
||||
spec: {
|
||||
package: { pluginVersion: '0.1.0-mainline' },
|
||||
package: { pluginVersion: 7 },
|
||||
adapters: {},
|
||||
notifications: {},
|
||||
audit: { requiredArtifacts: [] },
|
||||
notifications: {
|
||||
operatorVisibleRecoveryRequired: true,
|
||||
allowedTerminalStates: ['acked'],
|
||||
},
|
||||
audit: { portableArtifactsRequired: true, requiredArtifacts: ['queue_items'] },
|
||||
capability_expectations: { required: [] },
|
||||
},
|
||||
}),
|
||||
/deployment profile spec\.audit\.requiredArtifacts must be a non-empty array/
|
||||
/deployment profile schema validation failed: .*\/spec\/package\/pluginVersion must be string/
|
||||
);
|
||||
});
|
||||
|
||||
test('deployment profile yaml parser covers composite nested yaml structure', () => {
|
||||
const profile = parseDeploymentProfileYaml(complexProfileYaml);
|
||||
|
||||
assert.equal(profile.metadata.summary, 'Complex profile for YAML parser coverage with nested objects, arrays, and folded scalars.');
|
||||
assert.deepEqual(profile.spec.policies.enabledPacks, ['no-silence', 'verified-completion-only']);
|
||||
assert.equal(profile.spec.policies.overrides.checkpoints.overdueAction, 'force_checkpoint');
|
||||
assert.deepEqual(profile.spec.adapters.bridge.truthfulDeliveryStates, ['dispatched', 'acked']);
|
||||
assert.deepEqual(profile.spec.notifications.allowedTerminalStates, ['acked', 'blocked']);
|
||||
});
|
||||
|
||||
test('yaml to artifact generation produces validator-compatible package artifact', () => {
|
||||
const profile = parseDeploymentProfileYaml(`apiVersion: reporting-governance/v1alpha1\nkind: DeploymentProfile\nmetadata:\n id: demo\n version: 1.0.0\n runtime: openclaw\nspec:\n package:\n pluginVersion: 0.1.0-mainline\n adapters:\n watchdog:\n enabled: true\n queue:\n enabled: true\n dispatcher:\n enabled: true\n bridge:\n enabled: true\n sender:\n mode: openclaw-cli\n orchestrator:\n enabled: true\n notifications:\n operatorVisibleRecoveryRequired: true\n allowedTerminalStates:\n - acked\n audit:\n portableArtifactsRequired: true\n requiredArtifacts:\n - queue_items\n capability_expectations:\n required:\n - create_queue_items\n`);
|
||||
const profile = parseDeploymentProfileYaml(complexProfileYaml);
|
||||
const artifact = generateDeploymentProfileArtifact(profile, { sourceProfile: 'profiles/demo.yaml' });
|
||||
|
||||
assert.equal(artifact.kind, 'DeploymentProfileArtifact');
|
||||
@@ -69,3 +144,43 @@ test('strict-manager YAML generation matches checked-in package artifact snapsho
|
||||
|
||||
assert.deepEqual(generated, checkedIn);
|
||||
});
|
||||
|
||||
test('profile artifact generator CLI exits 1 with actionable error on malformed profile', () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'reporting-governance-profile-cli-'));
|
||||
const malformedProfilePath = path.join(tempDir, 'malformed.yaml');
|
||||
const outputArtifactPath = path.join(tempDir, 'artifact.json');
|
||||
|
||||
fs.writeFileSync(malformedProfilePath, `apiVersion: reporting-governance/v1alpha1
|
||||
kind: DeploymentProfile
|
||||
metadata:
|
||||
id: malformed
|
||||
version: 1.0.0
|
||||
runtime: openclaw
|
||||
spec:
|
||||
package:
|
||||
pluginVersion: 123
|
||||
adapters: {}
|
||||
notifications:
|
||||
operatorVisibleRecoveryRequired: true
|
||||
allowedTerminalStates:
|
||||
- acked
|
||||
audit:
|
||||
portableArtifactsRequired: true
|
||||
requiredArtifacts:
|
||||
- queue_items
|
||||
capability_expectations:
|
||||
required: []
|
||||
`, 'utf8');
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[path.join(repoRoot, 'scripts', 'generate_reporting_governance_profile_artifact.mjs'), malformedProfilePath, outputArtifactPath],
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
|
||||
assert.equal(result.status, 1);
|
||||
assert.match(result.stderr, /profile artifact generation failed:/);
|
||||
assert.match(result.stderr, /deployment profile schema validation failed:/);
|
||||
assert.match(result.stderr, /\/spec\/package\/pluginVersion must be string/);
|
||||
assert.equal(fs.existsSync(outputArtifactPath), false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user