test: harden decision record storage slice
This commit is contained in:
@@ -18,10 +18,24 @@ function assertObjectRecord(value, label) {
|
||||
}
|
||||
|
||||
function sanitizeFileSegment(value, fallback) {
|
||||
const normalized = String(value ?? '').trim().replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
||||
const normalized = String(value ?? '')
|
||||
.trim()
|
||||
.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/^(?:\.+-*)+/, '')
|
||||
.replace(/(?:-+\.)+$/g, '')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
return normalized || fallback;
|
||||
}
|
||||
|
||||
function assertIsoTimestamp(value, label) {
|
||||
const normalized = assertNonEmptyString(value, label);
|
||||
if (Number.isNaN(Date.parse(normalized))) {
|
||||
throw new Error(`${label} must be a valid ISO-8601 timestamp`);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function validateDecisionRecordArtifact(artifact) {
|
||||
if (!artifact || typeof artifact !== 'object' || Array.isArray(artifact)) {
|
||||
throw new Error('decision record artifact must be an object');
|
||||
@@ -37,14 +51,43 @@ export function validateDecisionRecordArtifact(artifact) {
|
||||
const spec = assertObjectRecord(artifact.spec, 'decision record artifact spec');
|
||||
const decision = assertObjectRecord(spec.decision, 'decision record artifact spec.decision');
|
||||
const receipt = assertObjectRecord(spec.receipt, 'decision record artifact spec.receipt');
|
||||
const source = spec.source == null ? null : assertObjectRecord(spec.source, 'decision record artifact spec.source');
|
||||
|
||||
assertNonEmptyString(metadata.recorded_at, 'decision record artifact metadata.recorded_at');
|
||||
assertNonEmptyString(metadata.policy_id, 'decision record artifact metadata.policy_id');
|
||||
assertNonEmptyString(metadata.decision, 'decision record artifact metadata.decision');
|
||||
assertNonEmptyString(decision.policy_id, 'decision record artifact spec.decision.policy_id');
|
||||
assertNonEmptyString(decision.decision, 'decision record artifact spec.decision.decision');
|
||||
const recordedAt = assertIsoTimestamp(metadata.recorded_at, 'decision record artifact metadata.recorded_at');
|
||||
const metadataPolicyId = assertNonEmptyString(metadata.policy_id, 'decision record artifact metadata.policy_id');
|
||||
const metadataDecision = assertNonEmptyString(metadata.decision, 'decision record artifact metadata.decision');
|
||||
const decisionPolicyId = assertNonEmptyString(decision.policy_id, 'decision record artifact spec.decision.policy_id');
|
||||
const decisionName = assertNonEmptyString(decision.decision, 'decision record artifact spec.decision.decision');
|
||||
assertNonEmptyString(receipt.delivery_state, 'decision record artifact spec.receipt.delivery_state');
|
||||
|
||||
if (metadataPolicyId !== decisionPolicyId) {
|
||||
throw new Error('decision record artifact policy_id mismatch between metadata and spec.decision');
|
||||
}
|
||||
if (metadataDecision !== decisionName) {
|
||||
throw new Error('decision record artifact decision mismatch between metadata and spec.decision');
|
||||
}
|
||||
if (source?.event_id != null) {
|
||||
assertNonEmptyString(source.event_id, 'decision record artifact spec.source.event_id');
|
||||
}
|
||||
if (source?.task_id != null) {
|
||||
const sourceTaskId = assertNonEmptyString(source.task_id, 'decision record artifact spec.source.task_id');
|
||||
if (metadata.task_id != null && assertNonEmptyString(metadata.task_id, 'decision record artifact metadata.task_id') !== sourceTaskId) {
|
||||
throw new Error('decision record artifact task_id mismatch between metadata and spec.source');
|
||||
}
|
||||
}
|
||||
if (source?.correlation_id != null) {
|
||||
const sourceCorrelationId = assertNonEmptyString(source.correlation_id, 'decision record artifact spec.source.correlation_id');
|
||||
if (metadata.correlation_id != null && assertNonEmptyString(metadata.correlation_id, 'decision record artifact metadata.correlation_id') !== sourceCorrelationId) {
|
||||
throw new Error('decision record artifact correlation_id mismatch between metadata and spec.source');
|
||||
}
|
||||
}
|
||||
|
||||
metadata.recorded_at = recordedAt;
|
||||
metadata.policy_id = metadataPolicyId;
|
||||
metadata.decision = metadataDecision;
|
||||
decision.policy_id = decisionPolicyId;
|
||||
decision.decision = decisionName;
|
||||
|
||||
return artifact;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,5 +12,6 @@ export {
|
||||
createDecisionRecordArtifact,
|
||||
createDecisionRecordFileName,
|
||||
validateDecisionRecordArtifact,
|
||||
__testables,
|
||||
} from './decision-artifact.mjs';
|
||||
export { createFileDecisionStore } from './decision-store.mjs';
|
||||
|
||||
Reference in New Issue
Block a user