- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim) - AdonisJS v7 app with Inertia.js + Vue 3 - MongoDB integration - CWMP/TR-069 state machine engine - Configuration engine with compliance tracking - Branding: ADAM ACS (favicon, navbar, locales) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import * as fc from 'fast-check'
|
|
import {
|
|
isParameterCoveredByInventory,
|
|
suggestInventoryPath,
|
|
} from '../../app/utils/inventory_coverage.js'
|
|
|
|
/**
|
|
* **Validates: Requirements 5.5**
|
|
*
|
|
* Property 9: Desired parameters require inventory coverage
|
|
* Verifies subtree prefix matching logic using random TR-069-like paths.
|
|
*/
|
|
|
|
// Arbitrary: generates a single TR-069 path segment (alphanumeric, 1-10 chars)
|
|
const segmentArb = fc.array(
|
|
fc.constantFrom(...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split('')),
|
|
{ minLength: 1, maxLength: 10 }
|
|
).map((chars) => chars.join(''))
|
|
|
|
// Arbitrary: generates a TR-069-like dot-separated path with 2-5 segments (e.g. "Device.Info.Param")
|
|
const tr069PathArb = fc.array(segmentArb, { minLength: 2, maxLength: 5 }).map((segments) => segments.join('.'))
|
|
|
|
// Arbitrary: generates a subtree path (ends with dot) from a TR-069 path by removing the last segment
|
|
const subtreePathFromParamArb = tr069PathArb.map((paramPath) => {
|
|
const parts = paramPath.split('.')
|
|
parts.pop()
|
|
return { paramPath, subtreePath: parts.join('.') + '.' }
|
|
})
|
|
|
|
describe('Property 9: Inventory coverage validation', () => {
|
|
it('a parameter is always covered by its own subtree path', () => {
|
|
fc.assert(
|
|
fc.property(subtreePathFromParamArb, ({ paramPath, subtreePath }) => {
|
|
const result = isParameterCoveredByInventory(paramPath, [{ path: subtreePath }])
|
|
expect(result).toBe(true)
|
|
}),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
|
|
it('suggestInventoryPath always produces a path that covers the original parameter', () => {
|
|
fc.assert(
|
|
fc.property(tr069PathArb, (paramPath) => {
|
|
const suggested = suggestInventoryPath(paramPath)
|
|
const result = isParameterCoveredByInventory(paramPath, [{ path: suggested }])
|
|
expect(result).toBe(true)
|
|
}),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
|
|
it('a subtree path covers all parameters that start with it', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.array(segmentArb, { minLength: 1, maxLength: 3 }),
|
|
fc.array(segmentArb, { minLength: 1, maxLength: 3 }),
|
|
(prefixSegments, suffixSegments) => {
|
|
const subtreePath = prefixSegments.join('.') + '.'
|
|
const paramPath = prefixSegments.join('.') + '.' + suffixSegments.join('.')
|
|
const result = isParameterCoveredByInventory(paramPath, [{ path: subtreePath }])
|
|
expect(result).toBe(true)
|
|
}
|
|
),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
|
|
it('a subtree path does NOT cover parameters that do not start with it', () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.array(segmentArb, { minLength: 1, maxLength: 3 }),
|
|
fc.array(segmentArb, { minLength: 1, maxLength: 3 }),
|
|
segmentArb,
|
|
(prefixSegments, suffixSegments, differentFirst) => {
|
|
const subtreePath = prefixSegments.join('.') + '.'
|
|
// Build a param that does NOT start with the subtree path
|
|
// by using a different first segment
|
|
const altFirst = differentFirst + 'X' // ensure it differs
|
|
const paramPath = altFirst + '.' + suffixSegments.join('.')
|
|
|
|
// Only assert when the param truly doesn't start with the subtree
|
|
fc.pre(!paramPath.startsWith(subtreePath))
|
|
|
|
const result = isParameterCoveredByInventory(paramPath, [{ path: subtreePath }])
|
|
expect(result).toBe(false)
|
|
}
|
|
),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
|
|
it('an exact path (no trailing dot) only matches the exact same string', () => {
|
|
fc.assert(
|
|
fc.property(tr069PathArb, tr069PathArb, (exactPath, otherPath) => {
|
|
// exactPath has no trailing dot, so it's an exact match inventory path
|
|
fc.pre(!exactPath.endsWith('.'))
|
|
const result = isParameterCoveredByInventory(otherPath, [{ path: exactPath }])
|
|
if (otherPath === exactPath) {
|
|
expect(result).toBe(true)
|
|
} else {
|
|
expect(result).toBe(false)
|
|
}
|
|
}),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
|
|
it('empty inventory paths array means never covered', () => {
|
|
fc.assert(
|
|
fc.property(tr069PathArb, (paramPath) => {
|
|
const result = isParameterCoveredByInventory(paramPath, [])
|
|
expect(result).toBe(false)
|
|
}),
|
|
{ numRuns: 200 }
|
|
)
|
|
})
|
|
})
|