/** * Property-Based Test: Non-metric category derivation is the set difference with accurate counts * * Feature: compliance-nonmetric-filters, Property 1: Non-metric category derivation * **Validates: Requirements 1.1, 1.2, 2.2, 2.4** * * For any set of devices and any set of summary entries for a team, * deriveNonMetricCategories returns exactly the metric_ids present in at least * one device's failing_metrics array that do not appear in any summary entry's * metric_id — deduplicated, sorted alphabetically by metricId, and each with a * count equal to the number of devices whose failing_metrics contains that metric_id. */ import fc from 'fast-check'; import { deriveNonMetricCategories } from '../components/pages/CompliancePage'; // Generators const metricIdArb = fc.stringMatching(/^[A-Za-z0-9_.]{1,20}$/); const failingMetricArb = fc.record({ metric_id: metricIdArb, metric_desc: fc.constant(''), category: fc.constant(''), }); const deviceArb = fc.record({ hostname: fc.string({ minLength: 1, maxLength: 30 }), failing_metrics: fc.array(failingMetricArb, { minLength: 0, maxLength: 8 }), }); const summaryEntryArb = fc.record({ metric_id: metricIdArb, team: fc.constant('STEAM'), compliance_pct: fc.double({ min: 0, max: 1 }), status: fc.constantFrom('Meets/Exceeds Target', 'Within 15% of Target', 'Below 15% of Target'), }); const categoriesConfigArb = fc.dictionary(metricIdArb, fc.constantFrom( 'Vulnerability Management', 'Access & MFA', 'Logging & Monitoring', 'Asset Data Quality', 'Endpoint Protection', 'Application Security', 'Disaster Recovery', 'Decommissioned Assets' )); describe('Compliance Non-Metric Derivation — Property 1: Set difference with accurate counts', () => { it('returned metric_ids are exactly the set difference of device metric_ids minus summary metric_ids', () => { fc.assert( fc.property( fc.array(deviceArb, { minLength: 0, maxLength: 20 }), fc.array(summaryEntryArb, { minLength: 0, maxLength: 10 }), categoriesConfigArb, (devices, summaryEntries, config) => { const result = deriveNonMetricCategories(devices, summaryEntries, config); const summaryIds = new Set(summaryEntries.map(e => e.metric_id)); const deviceMetricIds = new Set(); for (const d of devices) { for (const m of (d.failing_metrics || [])) { if (m.metric_id) deviceMetricIds.add(m.metric_id); } } const expectedIds = new Set([...deviceMetricIds].filter(id => !summaryIds.has(id))); const resultIds = new Set(result.map(r => r.metricId)); expect(resultIds).toEqual(expectedIds); } ), { numRuns: 100 } ); }); it('returned list is deduplicated and sorted alphabetically by metricId', () => { fc.assert( fc.property( fc.array(deviceArb, { minLength: 0, maxLength: 20 }), fc.array(summaryEntryArb, { minLength: 0, maxLength: 10 }), categoriesConfigArb, (devices, summaryEntries, config) => { const result = deriveNonMetricCategories(devices, summaryEntries, config); // Deduplicated const ids = result.map(r => r.metricId); expect(ids.length).toBe(new Set(ids).size); // Sorted alphabetically const sorted = [...ids].sort((a, b) => a.localeCompare(b)); expect(ids).toEqual(sorted); } ), { numRuns: 100 } ); }); it('each count equals the number of devices whose failing_metrics contains that metric_id', () => { fc.assert( fc.property( fc.array(deviceArb, { minLength: 0, maxLength: 20 }), fc.array(summaryEntryArb, { minLength: 0, maxLength: 10 }), categoriesConfigArb, (devices, summaryEntries, config) => { const result = deriveNonMetricCategories(devices, summaryEntries, config); for (const { metricId, count } of result) { const expectedCount = devices.filter(d => (d.failing_metrics || []).some(m => m.metric_id === metricId) ).length; expect(count).toBe(expectedCount); } } ), { numRuns: 100 } ); }); });