initial commit: @arbiter/core authorization engine with js-rigor hardening
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/ binary modes, condensed snapshots, value relations) with 39 rigor test campaigns. Includes fixes for snapshot binary writer/reader format mismatch (snapshot-of-snapshot corruption), possibility write-boundary validation, empty-graph snapshot serialization, relation lookup cache direction collision, config-redefinition cache invalidation, binary threshold semantics, defeasible compiled routing, and comparator reason whitelisting.
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* Test Unified Evidence Fusion System
|
||||
*
|
||||
* This test demonstrates the new separation of concerns between aggregation
|
||||
* (OWA) and reconciliation (bilattice/Dempster-Shafer) logic, supporting
|
||||
* both qualitative and quantitative modes with the same lexicon.
|
||||
*/
|
||||
|
||||
import { test, describe } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import {
|
||||
QualitativeScale,
|
||||
UnifiedEvidenceFusion,
|
||||
EvidenceAggregation,
|
||||
EvidenceReconciliation,
|
||||
NumericBilatticeOrderings
|
||||
} from '../../src/qualitative/index.js';
|
||||
|
||||
describe('Unified Evidence Fusion System', () => {
|
||||
|
||||
test('should demonstrate aggregation vs reconciliation separation', () => {
|
||||
// Test data with conflicting evidence
|
||||
const collectedValues = [
|
||||
{
|
||||
value: 0.8,
|
||||
possibility: 0.9,
|
||||
path: ['evidence1'],
|
||||
source: { type: 'direct', confidence: 0.95 },
|
||||
metadata: { timestamp: Date.now(), reliability: 0.9 }
|
||||
},
|
||||
{
|
||||
value: 0.3,
|
||||
possibility: 0.4,
|
||||
path: ['evidence2'],
|
||||
source: { type: 'inferred', confidence: 0.6 },
|
||||
metadata: { timestamp: Date.now(), reliability: 0.7 }
|
||||
},
|
||||
{
|
||||
value: 0.7,
|
||||
possibility: 0.6,
|
||||
path: ['evidence3'],
|
||||
source: { type: 'derived', confidence: 0.8 },
|
||||
metadata: { timestamp: Date.now(), reliability: 0.8 }
|
||||
}
|
||||
];
|
||||
|
||||
// Test pure aggregation (no reconciliation)
|
||||
const aggregationResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
aggregationMethod: 'majority',
|
||||
useReconciliation: false
|
||||
});
|
||||
|
||||
assert.ok(aggregationResult.hasValue);
|
||||
assert.strictEqual(aggregationResult.fusionMethod, 'aggregation');
|
||||
assert.strictEqual(aggregationResult.reconciliationMethod, 'none');
|
||||
assert.ok(aggregationResult.epistemicAnalysis === null);
|
||||
|
||||
// Test reconciliation-based fusion
|
||||
const reconciliationResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
aggregationMethod: 'max',
|
||||
reconciliationMethod: 'dempster_shafer',
|
||||
epistemicMode: 'hybrid',
|
||||
useReconciliation: true
|
||||
});
|
||||
|
||||
assert.ok(reconciliationResult.hasValue);
|
||||
assert.strictEqual(reconciliationResult.fusionMethod, 'reconciliation');
|
||||
assert.strictEqual(reconciliationResult.reconciliationMethod, 'dempster_shafer');
|
||||
assert.ok(reconciliationResult.epistemicAnalysis !== null);
|
||||
});
|
||||
|
||||
test('should support both qualitative and quantitative modes with same lexicon', () => {
|
||||
const collectedValues = [
|
||||
{
|
||||
value: 0.75, // Valid fivePoint scale value
|
||||
possibility: 0.75,
|
||||
path: ['evidence1'],
|
||||
source: { type: 'direct' },
|
||||
metadata: { reliability: 0.9 }
|
||||
},
|
||||
{
|
||||
value: 0.5, // Valid fivePoint scale value
|
||||
possibility: 0.5,
|
||||
path: ['evidence2'],
|
||||
source: { type: 'inferred' },
|
||||
metadata: { reliability: 0.7 }
|
||||
}
|
||||
];
|
||||
|
||||
const scale = QualitativeScale.fivePoint();
|
||||
|
||||
// Test quantitative mode
|
||||
const quantitativeResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
aggregationMethod: 'average',
|
||||
useReconciliation: false
|
||||
});
|
||||
|
||||
// Test qualitative mode
|
||||
const qualitativeResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'qualitative',
|
||||
aggregationMethod: 'average',
|
||||
scale: scale,
|
||||
useReconciliation: false
|
||||
});
|
||||
|
||||
assert.ok(quantitativeResult.hasValue);
|
||||
assert.ok(qualitativeResult.hasValue);
|
||||
assert.strictEqual(quantitativeResult.aggregationMethod, 'average');
|
||||
assert.strictEqual(qualitativeResult.aggregationMethod, 'average');
|
||||
|
||||
// Both should use the same aggregation lexicon
|
||||
assert.ok(quantitativeResult.value > 0);
|
||||
assert.ok(qualitativeResult.value > 0);
|
||||
});
|
||||
|
||||
test('should demonstrate numeric bilattice orderings', () => {
|
||||
// Create a simple numeric capacity
|
||||
const capacity = {
|
||||
stateSpace: ['evidence1', 'evidence2', 'evidence3'],
|
||||
getCapacity: (set) => {
|
||||
// For single elements
|
||||
if (set.size === 1) {
|
||||
if (set.has('evidence1')) return 0.8;
|
||||
if (set.has('evidence2')) return 0.6;
|
||||
if (set.has('evidence3')) return 0.4;
|
||||
}
|
||||
// For complements (multiple elements)
|
||||
if (set.size === 2) {
|
||||
return 0.2; // Some belief in complements
|
||||
}
|
||||
// For empty set
|
||||
if (set.size === 0) {
|
||||
return 0;
|
||||
}
|
||||
// For full set
|
||||
if (set.size === 3) {
|
||||
return 1.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const propositions = [['evidence1'], ['evidence2'], ['evidence3']];
|
||||
|
||||
// Test information ordering
|
||||
const mostInformative = NumericBilatticeOrderings.findMostInformative(propositions, capacity);
|
||||
assert.ok(mostInformative);
|
||||
assert.strictEqual(mostInformative.rank, 1);
|
||||
|
||||
// Test truth ordering
|
||||
const mostTrue = NumericBilatticeOrderings.findMostTrue(propositions, capacity);
|
||||
assert.ok(mostTrue);
|
||||
assert.strictEqual(mostTrue.rank, 1);
|
||||
|
||||
// Test Dempster-Shafer measures
|
||||
const belief = NumericBilatticeOrderings.dempsterShaferBelief(['evidence1'], capacity);
|
||||
const plausibility = NumericBilatticeOrderings.dempsterShaferPlausibility(['evidence1'], capacity);
|
||||
const uncertainty = NumericBilatticeOrderings.dempsterShaferUncertainty(['evidence1'], capacity);
|
||||
|
||||
assert.strictEqual(belief, 0.8);
|
||||
// Plausibility should be 1 - capacity of complement
|
||||
// Complement of ['evidence1'] is ['evidence2', 'evidence3']
|
||||
// Capacity of ['evidence2', 'evidence3'] is 0.2
|
||||
// So plausibility = 1 - 0.2 = 0.8
|
||||
assert.strictEqual(plausibility, 0.8);
|
||||
assert.strictEqual(uncertainty, 0); // 0.8 - 0.8 = 0
|
||||
});
|
||||
|
||||
test('should demonstrate reconciliation methods comparison', () => {
|
||||
const collectedValues = [
|
||||
{
|
||||
value: 0.8,
|
||||
possibility: 0.9,
|
||||
path: ['evidence1'],
|
||||
source: { type: 'direct' },
|
||||
metadata: { reliability: 0.9 }
|
||||
},
|
||||
{
|
||||
value: 0.3,
|
||||
possibility: 0.4,
|
||||
path: ['evidence2'],
|
||||
source: { type: 'inferred' },
|
||||
metadata: { reliability: 0.7 }
|
||||
}
|
||||
];
|
||||
|
||||
// Test different reconciliation methods
|
||||
const bilatticeResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
reconciliationMethod: 'bilattice',
|
||||
epistemicMode: 'hybrid',
|
||||
useReconciliation: true
|
||||
});
|
||||
|
||||
const dempsterShaferResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
reconciliationMethod: 'dempster_shafer',
|
||||
epistemicMode: 'hybrid',
|
||||
useReconciliation: true
|
||||
});
|
||||
|
||||
const subjectiveLogicResult = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
reconciliationMethod: 'subjective_logic',
|
||||
epistemicMode: 'hybrid',
|
||||
useReconciliation: true
|
||||
});
|
||||
|
||||
// All should produce valid results
|
||||
assert.ok(bilatticeResult.hasValue);
|
||||
assert.ok(dempsterShaferResult.hasValue);
|
||||
assert.ok(subjectiveLogicResult.hasValue);
|
||||
|
||||
// All should have epistemic analysis
|
||||
assert.ok(bilatticeResult.epistemicAnalysis);
|
||||
assert.ok(dempsterShaferResult.epistemicAnalysis);
|
||||
assert.ok(subjectiveLogicResult.epistemicAnalysis);
|
||||
|
||||
// Different methods may produce different results
|
||||
console.log('Bilattice result:', bilatticeResult.value);
|
||||
console.log('Dempster-Shafer result:', dempsterShaferResult.value);
|
||||
console.log('Subjective Logic result:', subjectiveLogicResult.value);
|
||||
});
|
||||
|
||||
test('should demonstrate aggregation methods comparison', () => {
|
||||
const collectedValues = [
|
||||
{
|
||||
value: 0.8,
|
||||
possibility: 0.8,
|
||||
path: ['evidence1'],
|
||||
source: { type: 'direct' },
|
||||
metadata: { reliability: 0.9 }
|
||||
},
|
||||
{
|
||||
value: 0.6,
|
||||
possibility: 0.6,
|
||||
path: ['evidence2'],
|
||||
source: { type: 'inferred' },
|
||||
metadata: { reliability: 0.7 }
|
||||
},
|
||||
{
|
||||
value: 0.4,
|
||||
possibility: 0.4,
|
||||
path: ['evidence3'],
|
||||
source: { type: 'derived' },
|
||||
metadata: { reliability: 0.8 }
|
||||
}
|
||||
];
|
||||
|
||||
const aggregationMethods = ['max', 'min', 'average', 'majority', 'median'];
|
||||
|
||||
const results = {};
|
||||
for (const method of aggregationMethods) {
|
||||
results[method] = UnifiedEvidenceFusion.fuse(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
aggregationMethod: method,
|
||||
useReconciliation: false
|
||||
});
|
||||
}
|
||||
|
||||
// All methods should produce valid results
|
||||
for (const [method, result] of Object.entries(results)) {
|
||||
assert.ok(result.hasValue, `Method ${method} should produce valid result`);
|
||||
assert.strictEqual(result.aggregationMethod, method);
|
||||
assert.ok(result.value >= 0 && result.value <= 1);
|
||||
}
|
||||
|
||||
// Different methods should produce different results
|
||||
assert.ok(results.max.value >= results.average.value);
|
||||
assert.ok(results.average.value >= results.min.value);
|
||||
});
|
||||
|
||||
test('should demonstrate method comparison functionality', () => {
|
||||
const collectedValues = [
|
||||
{
|
||||
value: 0.7,
|
||||
possibility: 0.7,
|
||||
path: ['evidence1'],
|
||||
source: { type: 'direct' },
|
||||
metadata: { reliability: 0.9 }
|
||||
},
|
||||
{
|
||||
value: 0.5,
|
||||
possibility: 0.5,
|
||||
path: ['evidence2'],
|
||||
source: { type: 'inferred' },
|
||||
metadata: { reliability: 0.7 }
|
||||
}
|
||||
];
|
||||
|
||||
const comparison = UnifiedEvidenceFusion.compareMethods(collectedValues, {
|
||||
mode: 'quantitative',
|
||||
aggregationMethods: ['max', 'average', 'majority'],
|
||||
reconciliationMethods: ['none', 'dempster_shafer'],
|
||||
epistemicModes: ['hybrid']
|
||||
});
|
||||
|
||||
assert.ok(comparison.results);
|
||||
assert.ok(comparison.summary);
|
||||
assert.ok(comparison.summary.bestMethods.length > 0);
|
||||
assert.ok(comparison.summary.worstMethods.length > 0);
|
||||
assert.ok(comparison.summary.valueRange.min <= comparison.summary.valueRange.max);
|
||||
});
|
||||
|
||||
test('should validate fusion options', () => {
|
||||
const validation = UnifiedEvidenceFusion.validateOptions({
|
||||
mode: 'quantitative',
|
||||
aggregationMethod: 'max',
|
||||
reconciliationMethod: 'bilattice',
|
||||
epistemicMode: 'hybrid',
|
||||
capacityType: 'simple_support'
|
||||
});
|
||||
|
||||
assert.ok(validation.valid);
|
||||
assert.strictEqual(validation.errors.length, 0);
|
||||
|
||||
// Test invalid options
|
||||
const invalidValidation = UnifiedEvidenceFusion.validateOptions({
|
||||
mode: 'invalid',
|
||||
aggregationMethod: 'invalid',
|
||||
reconciliationMethod: 'invalid'
|
||||
});
|
||||
|
||||
assert.ok(!invalidValidation.valid);
|
||||
assert.ok(invalidValidation.errors.length > 0);
|
||||
});
|
||||
|
||||
test('should get available methods and descriptions', () => {
|
||||
const availableMethods = UnifiedEvidenceFusion.getAvailableMethods();
|
||||
assert.ok(availableMethods.aggregation);
|
||||
assert.ok(availableMethods.reconciliation);
|
||||
assert.ok(availableMethods.epistemicModes);
|
||||
assert.ok(availableMethods.capacityTypes);
|
||||
|
||||
const descriptions = UnifiedEvidenceFusion.getMethodDescriptions();
|
||||
assert.ok(descriptions.aggregation);
|
||||
assert.ok(descriptions.reconciliation);
|
||||
assert.ok(descriptions.epistemicModes);
|
||||
assert.ok(descriptions.capacityTypes);
|
||||
|
||||
// Test specific descriptions
|
||||
assert.ok(descriptions.aggregation.max);
|
||||
assert.ok(descriptions.reconciliation.bilattice);
|
||||
assert.ok(descriptions.epistemicModes.hybrid);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user