717ae1031e
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.
251 lines
9.6 KiB
JavaScript
251 lines
9.6 KiB
JavaScript
/**
|
|
* Simple Test Suite for Qualitative Capacity System
|
|
*
|
|
* Tests core functionality using Node.js built-in test framework
|
|
*/
|
|
|
|
import { test, describe } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
QualitativeScale,
|
|
QualitativeCapacity,
|
|
QualitativeFusion,
|
|
OWAQualitativeFusion,
|
|
getOWAQualitativeWeights,
|
|
DEFAULT_QUALITATIVE_SCALE
|
|
} from '../../src/qualitative/index.js';
|
|
|
|
describe('QualitativeScale', () => {
|
|
test('should create scale with correct properties', () => {
|
|
const scale = new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'test');
|
|
assert.deepStrictEqual(scale.values, [0, 0.25, 0.5, 0.75, 1]);
|
|
assert.strictEqual(scale.bottom, 0);
|
|
assert.strictEqual(scale.top, 1);
|
|
assert.strictEqual(scale.size, 5);
|
|
});
|
|
|
|
test('should create correct negation map', () => {
|
|
const scale = new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'test');
|
|
assert.strictEqual(scale.negate(0), 1);
|
|
assert.strictEqual(scale.negate(1), 0);
|
|
assert.strictEqual(scale.negate(0.25), 0.75);
|
|
assert.strictEqual(scale.negate(0.5), 0.5);
|
|
assert.strictEqual(scale.negate(0.75), 0.25);
|
|
});
|
|
|
|
test('should perform min/max operations correctly', () => {
|
|
const scale = new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'test');
|
|
assert.strictEqual(scale.min(0.25, 0.75), 0.25);
|
|
assert.strictEqual(scale.max(0.25, 0.75), 0.75);
|
|
assert.strictEqual(scale.minAll([0.25, 0.5, 0.75]), 0.25);
|
|
assert.strictEqual(scale.maxAll([0.25, 0.5, 0.75]), 0.75);
|
|
});
|
|
|
|
test('should compare values correctly', () => {
|
|
const scale = new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'test');
|
|
assert.strictEqual(scale.compare(0.25, 0.75), -1);
|
|
assert.strictEqual(scale.compare(0.75, 0.25), 1);
|
|
assert.strictEqual(scale.compare(0.5, 0.5), 0);
|
|
});
|
|
|
|
test('should validate values are in scale', () => {
|
|
const scale = new QualitativeScale([0, 0.25, 0.5, 0.75, 1], 'test');
|
|
assert.strictEqual(scale.contains(0.5), true);
|
|
assert.strictEqual(scale.contains(0.3), false);
|
|
});
|
|
|
|
test('should create common scales correctly', () => {
|
|
const binary = QualitativeScale.binary();
|
|
assert.deepStrictEqual(binary.values, [0, 1]);
|
|
|
|
const ternary = QualitativeScale.ternary();
|
|
assert.deepStrictEqual(ternary.values, [0, 0.5, 1]);
|
|
|
|
const fivePoint = QualitativeScale.fivePoint();
|
|
assert.deepStrictEqual(fivePoint.values, [0, 0.25, 0.5, 0.75, 1]);
|
|
});
|
|
});
|
|
|
|
describe('QualitativeCapacity', () => {
|
|
test('should create capacity with correct properties', () => {
|
|
const stateSpace = ['s1', 's2', 's3'];
|
|
const scale = new QualitativeScale([0, 0.5, 1], 'test');
|
|
|
|
// Create a QMT with some focal sets
|
|
const qmt = new Map();
|
|
qmt.set(new Set(['s1']), 0.5);
|
|
qmt.set(new Set(['s1', 's2']), 1);
|
|
qmt.set(new Set(['s1', 's2', 's3']), 1);
|
|
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
|
|
|
assert.deepStrictEqual(capacity.stateSpace, ['s1', 's2', 's3']);
|
|
assert.strictEqual(capacity.scale, scale);
|
|
assert.strictEqual(capacity.getFocalSets().length, 3);
|
|
});
|
|
|
|
test('should compute capacity values correctly', () => {
|
|
const stateSpace = ['s1', 's2', 's3'];
|
|
const scale = new QualitativeScale([0, 0.5, 1], 'test');
|
|
|
|
const qmt = new Map();
|
|
qmt.set(new Set(['s1']), 0.5);
|
|
qmt.set(new Set(['s1', 's2']), 1);
|
|
qmt.set(new Set(['s1', 's2', 's3']), 1);
|
|
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
|
|
|
assert.strictEqual(capacity.getCapacity(['s1']), 0.5);
|
|
assert.strictEqual(capacity.getCapacity(['s1', 's2']), 1);
|
|
assert.strictEqual(capacity.getCapacity(['s2']), 0); // Not a focal set
|
|
assert.strictEqual(capacity.getCapacity(['s1', 's2', 's3']), 1);
|
|
});
|
|
|
|
test('should identify special capacity types', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
// Test possibility measure (all focal sets are singletons)
|
|
const possibilityQMT = new Map();
|
|
possibilityQMT.set(new Set(['s1']), 1);
|
|
possibilityQMT.set(new Set(['s2']), 0.5);
|
|
|
|
const possibilityCapacity = new QualitativeCapacity(stateSpace, scale, possibilityQMT);
|
|
assert.strictEqual(possibilityCapacity.isPossibilityMeasure(), true);
|
|
assert.strictEqual(possibilityCapacity.isNecessityMeasure(), false);
|
|
|
|
// Test necessity measure (focal sets form a nested chain)
|
|
const necessityQMT = new Map();
|
|
necessityQMT.set(new Set(['s1']), 0.5);
|
|
necessityQMT.set(new Set(['s1', 's2']), 1);
|
|
|
|
const necessityCapacity = new QualitativeCapacity(stateSpace, scale, necessityQMT);
|
|
assert.strictEqual(necessityCapacity.isPossibilityMeasure(), false);
|
|
assert.strictEqual(necessityCapacity.isNecessityMeasure(), true);
|
|
});
|
|
|
|
test('should create simple support capacities', () => {
|
|
const stateSpace = ['s1', 's2', 's3'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
const ssc = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
|
|
assert.strictEqual(ssc.getCapacity(['s1']), 1);
|
|
assert.strictEqual(ssc.getCapacity(['s1', 's2']), 1);
|
|
assert.strictEqual(ssc.getCapacity(['s2']), 0);
|
|
assert.strictEqual(ssc.isNecessityMeasure(), true);
|
|
});
|
|
});
|
|
|
|
describe('QualitativeFusion', () => {
|
|
test('should perform normalized conjunctive fusion', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
const capacity1 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
const capacity2 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s2'], 1, scale);
|
|
|
|
const fused = QualitativeFusion.normalizedConjunctive([capacity1, capacity2]);
|
|
|
|
assert.deepStrictEqual(fused.stateSpace, stateSpace);
|
|
assert.strictEqual(fused.scale, scale);
|
|
assert.strictEqual(fused.getCapacity(['s1', 's2']), 1); // Full set should have top value
|
|
});
|
|
|
|
test('should perform disjunctive fusion', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
const capacity1 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
const capacity2 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s2'], 1, scale);
|
|
|
|
const result = QualitativeFusion.disjunctive(capacity1, capacity2);
|
|
|
|
assert.deepStrictEqual(result.stateSpace, stateSpace);
|
|
assert.strictEqual(result.scale, scale);
|
|
assert.strictEqual(result.getCapacity(['s1', 's2']), 1); // min(1, 1)
|
|
});
|
|
|
|
test('should compute Sugeno integral', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const scale = QualitativeScale.ternary();
|
|
|
|
const capacity = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
const decisionFunction = { 's1': 0.5, 's2': 1 };
|
|
|
|
const sugenoValue = QualitativeFusion.sugenoIntegral(capacity, decisionFunction);
|
|
|
|
// Should be a value in the scale
|
|
assert.strictEqual(scale.contains(sugenoValue), true);
|
|
assert.ok(sugenoValue >= 0);
|
|
assert.ok(sugenoValue <= 1);
|
|
});
|
|
});
|
|
|
|
describe('OWAQualitativeFusion', () => {
|
|
test('should perform qualitative OWA fusion', () => {
|
|
const values = [0, 0.5, 1]; // Use values that are in the ternary scale
|
|
const weights = [0, 0.5, 1]; // Use weights that are in the ternary scale
|
|
const scale = QualitativeScale.ternary();
|
|
|
|
const result = OWAQualitativeFusion.fuseWithMeta(values, weights, weights, 'max', scale);
|
|
|
|
// The result should be a valid value in the scale
|
|
assert.ok(scale.contains(result.value));
|
|
assert.ok(result.meta);
|
|
});
|
|
|
|
test('should generate OWA weights correctly', () => {
|
|
const scale = QualitativeScale.ternary();
|
|
const maxWeights = getOWAQualitativeWeights('max', 3, null, scale);
|
|
const minWeights = getOWAQualitativeWeights('min', 3, null, scale);
|
|
|
|
assert.strictEqual(maxWeights[0], 1); // Top weight on first position
|
|
assert.strictEqual(maxWeights[1], 0); // Bottom weight on others
|
|
assert.strictEqual(maxWeights[2], 0);
|
|
|
|
assert.strictEqual(minWeights[0], 0); // Bottom weight on first positions
|
|
assert.strictEqual(minWeights[1], 0);
|
|
assert.strictEqual(minWeights[2], 1); // Top weight on last position
|
|
});
|
|
});
|
|
|
|
describe('Integration Tests', () => {
|
|
test('should work with default qualitative scale', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const capacity = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, DEFAULT_QUALITATIVE_SCALE);
|
|
|
|
assert.strictEqual(capacity.scale, DEFAULT_QUALITATIVE_SCALE);
|
|
assert.strictEqual(capacity.getCapacity(['s1']), 1);
|
|
});
|
|
|
|
test('should perform end-to-end fusion workflow', () => {
|
|
const stateSpace = ['s1', 's2'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
// Create two simple support capacities
|
|
const capacity1 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
const capacity2 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s2'], 1, scale);
|
|
|
|
// Fuse them using normalized conjunctive rule
|
|
const fused = QualitativeFusion.normalizedConjunctive([capacity1, capacity2]);
|
|
|
|
// Verify the result
|
|
assert.deepStrictEqual(fused.stateSpace, stateSpace);
|
|
assert.strictEqual(fused.scale, scale);
|
|
assert.strictEqual(fused.getCapacity(['s1', 's2']), 1); // Full set should have top value
|
|
});
|
|
|
|
test('should handle edge cases gracefully', () => {
|
|
const stateSpace = ['s1'];
|
|
const scale = QualitativeScale.binary();
|
|
|
|
// Test with single state
|
|
const capacity = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 1, scale);
|
|
assert.strictEqual(capacity.getCapacity(['s1']), 1);
|
|
|
|
// Test with empty subset
|
|
assert.strictEqual(capacity.getCapacity([]), 0);
|
|
});
|
|
});
|