80 lines
4.3 KiB
TypeScript
80 lines
4.3 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import { test } from "node:test"
|
|
import { discoverChrIngressEndpoint } from "./traffic-flow-chr-ingress-discovery.js"
|
|
|
|
type Options = Parameters<typeof discoverChrIngressEndpoint>[0]
|
|
function fixture() {
|
|
const reads: string[] = [], toolReads: string[] = []
|
|
const collectors = { enabled: true, collectorIp: "192.0.2.100", flowListenPort: 4740,
|
|
listenerBound: true, listenerAddress: "192.0.2.100:4740" }
|
|
let revoked = false
|
|
const mappings = new Map([["192.0.2.10", 1], ["192.0.2.20", 2]])
|
|
const native = (serverId: number) => {
|
|
const identity = "owned-" + serverId, version = "7.24.5 (stable)", source = "192.0.2." + serverId * 10
|
|
const table: Record<string, unknown> = {
|
|
"/system/identity": [{ name: identity }],
|
|
"/system/resource": { version },
|
|
"/ip/address": [{ address: source + "/32", disabled: "false" }],
|
|
"/ip/traffic-flow": [{ enabled: "true" }],
|
|
"/ip/traffic-flow/target": [{ ".id": "*A", disabled: "false", "dst-address": "192.0.2.100",
|
|
"src-address": serverId === 1 ? source : "0.0.0.0", port: "4740", version: "ipfix" }],
|
|
}
|
|
const device: Options["devices"][0] = {
|
|
identity, version, source, serverId,
|
|
assertPinnedTransport: async () => { if (revoked) throw Error("transport pin lost") },
|
|
client: { async get<T>(path: string, timeout?: number, _signal?: AbortSignal, max?: number, before?: () => Promise<unknown>) {
|
|
assert(timeout! > 0 && timeout! <= 5000)
|
|
assert.equal(max, 65536)
|
|
assert(before)
|
|
await before()
|
|
reads.push(serverId + ":" + path)
|
|
assert(Object.hasOwn(table, path))
|
|
return structuredClone(table[path]) as T
|
|
} },
|
|
}
|
|
return { table, device }
|
|
}
|
|
const a = native(1), b = native(2)
|
|
const options: Options = { destination: collectors.collectorIp, baselinePort: 4740, testPort: 4739,
|
|
deadlineAt: Date.now() + 5000, devices: [a.device, b.device], assertCurrent: async () => {},
|
|
callTool: async (name, input) => { assert.deepEqual(input, {}); toolReads.push(name); return collectors },
|
|
resolveExporter: async address => mappings.get(address) ?? null,
|
|
}
|
|
return { options, reads, toolReads, a, b, mappings, collectors, revoke: () => { revoked = true } }
|
|
}
|
|
|
|
test("discovers exact configured two-CHR baseline endpoint without claiming observed ingress", async () => {
|
|
const f = fixture()
|
|
const result = await discoverChrIngressEndpoint(f.options)
|
|
assert.deepEqual(result.endpoint, { sources: ["192.0.2.10", "192.0.2.20"], destination: "192.0.2.100", port: 4740 })
|
|
assert.deepEqual(result.observations.map(row => row.sourceConfiguredExplicitly), [true, false])
|
|
assert.equal(result.ingressVerified, false)
|
|
assert.equal(f.reads.length, 10)
|
|
assert.deepEqual(f.toolReads, ["mm_netflow_settings_get", "mm_netflow_settings_get"])
|
|
})
|
|
|
|
test("refuses absent exact target, competing test port, source drift and attribution mismatch", async () => {
|
|
for (const [mutate, reason] of [
|
|
[(f: ReturnType<typeof fixture>) => { f.b.table["/ip/traffic-flow/target"] = [] }, /baseline IPFIX target/],
|
|
[(f: ReturnType<typeof fixture>) => { (f.a.table["/ip/traffic-flow/target"] as object[]).push({ ".id": "*B", "dst-address": "192.0.2.100", port: "4739" }) }, /test endpoint/],
|
|
[(f: ReturnType<typeof fixture>) => { f.b.table["/ip/address"] = [{ address: "192.0.2.99/32", disabled: false }] }, /source is not assigned/],
|
|
[(f: ReturnType<typeof fixture>) => { f.mappings.set("192.0.2.20", 1) }, /attribution differs/],
|
|
] as const) {
|
|
const f = fixture()
|
|
mutate(f)
|
|
await assert.rejects(discoverChrIngressEndpoint(f.options), reason)
|
|
}
|
|
})
|
|
|
|
test("stops on wrong collector bind, lost transport pin and changed CHR identity", async () => {
|
|
const wrong = fixture(); wrong.collectors.listenerAddress = "0.0.0.0:4740"
|
|
await assert.rejects(discoverChrIngressEndpoint(wrong.options), /collector endpoint/)
|
|
assert.deepEqual(wrong.reads, [])
|
|
const revoked = fixture(); revoked.revoke()
|
|
await assert.rejects(discoverChrIngressEndpoint(revoked.options), /transport pin lost/)
|
|
assert.deepEqual(revoked.reads, [])
|
|
const changed = fixture(); changed.a.table["/system/identity"] = [{ name: "foreign" }]
|
|
await assert.rejects(discoverChrIngressEndpoint(changed.options), /identity or version/)
|
|
assert.deepEqual(changed.reads, ["1:/system/identity", "1:/system/resource"])
|
|
})
|