Files
MikrotikManager/backend/src/services/traffic-flow-dest.test.ts
T
DenozordecandCursor ee9804f1bb
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m29s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / updater-image (push) Successful in 51s
Docker images / backend-image (push) Successful in 2m40s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 13s
feat(traffic-flow): enhance flow analytics and IP handling
- Introduced `canonicalIp` function to standardize IP address formats across the application, improving consistency in flow processing.
- Updated traffic flow analytics to utilize new endpoint resolution logic, enhancing accuracy in traffic classification.
- Enhanced tests for traffic flow analytics and IP handling, ensuring comprehensive coverage for new functionalities.
- Improved traffic flow destination resolution with additional test cases for various internet services, including Google and Fastly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-12 01:51:23 +07:00

257 lines
6.4 KiB
TypeScript

import assert from "node:assert/strict"
import {
ingestParsedFlowsForServerForTests,
resetEngineForTests,
} from "./traffic-flow-engine.js"
import { factsSnapshotForTests } from "./traffic-flow-facts.js"
import { classifyInternetBrand, mapInternetBrand, resolveInternetDest } from "./traffic-flow-dest.js"
import { disableCatalogFetchForTests, resetFlowCatalogForTests } from "./traffic-flow-classify.js"
import {
disableRipeEnqueueForTests,
disableRipePersistForTests,
resetRipeCacheForTests,
seedRipeCacheForTests,
} from "./traffic-flow-ripe.js"
import { seedFlowTopologyForTests, type FlowTopology } from "./traffic-flow-topology.js"
import { rememberServerIfaces, resetIfaceCacheForTests } from "./traffic-flow-ifindex.js"
disableCatalogFetchForTests()
resetFlowCatalogForTests()
disableRipePersistForTests()
disableRipeEnqueueForTests()
resetRipeCacheForTests()
resetEngineForTests()
resetIfaceCacheForTests()
seedRipeCacheForTests({
prefix: "8.8.8.0/24",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
})
seedRipeCacheForTests({
prefix: "95.167.0.0/16",
asn: 12389,
country: "RU",
lat: null,
lng: null,
holder: "ROSTELECOM-AS",
ok: true,
fetchedAt: Date.now(),
})
const topo: FlowTopology = {
clientIfaces: new Map([[1, new Set(["gre-client"])]]),
clientByIface: new Map([["1|gre-client", {
userId: "u-rost",
login: "alice",
name: "Alice",
serverId: 1,
interfaceName: "gre-client",
}]]),
enNodes: [{ id: 2, name: "en", hosts: ["198.51.100.1"] }],
enHosts: new Set(["198.51.100.1"]),
jhHosts: new Set(["203.0.113.10"]),
wanIfaces: new Map([[1, new Set(["ether1"])]]),
plane: {
clientIfaceNames: new Set(["gre-client"]),
enHosts: new Set(["198.51.100.1"]),
jhHosts: new Set(["203.0.113.10"]),
},
}
seedFlowTopologyForTests(topo)
rememberServerIfaces(1, [{ name: "gre-client", ifindex: "2" }])
ingestParsedFlowsForServerForTests(1, [
{
src: "95.167.1.10",
dst: "10.200.100.53",
proto: 6,
srcPort: 51234,
dstPort: 443,
bytes: 100,
packets: 2,
inIface: "gre-client",
outIface: "ether1",
},
{
src: "95.167.1.10",
dst: "8.8.8.8",
proto: 6,
srcPort: 51234,
dstPort: 443,
bytes: 50,
packets: 1,
inIface: "gre-client",
outIface: "ether1",
},
{
src: "203.0.113.10",
dst: "198.51.100.1",
proto: 47,
srcPort: 0,
dstPort: 0,
bytes: 9_000,
packets: 90,
inIface: "NSK-SERVHOST-RTK",
outIface: "NSK-SERVHOST-RTK",
},
{
src: "10.200.100.53",
dst: "10.200.100.1",
proto: 6,
srcPort: 53880,
dstPort: 443,
bytes: 70,
packets: 1,
inIface: "gre-client",
outIface: "ether1",
natDst: "8.8.8.8",
natDstPort: 443,
},
])
const facts = factsSnapshotForTests()
const total = facts.reduce((s, r) => s + r.bytes, 0)
assert.equal(total, 120, "unique = Google payload + NAT, без overlay/пустого dest")
assert.equal(facts.some((r) => r.asn === 12389), false, "ASN клиента не в кубе")
assert.equal(facts.some((r) => r.service === "GRE"), false, "GRE не сервис unique")
const google = facts.find((r) => r.asn === 15169)
assert.ok(google)
assert.equal(google.bytes, 120)
assert.equal(facts.filter((r) => r.asn === 0).reduce((s, r) => s + r.bytes, 0), 0)
assert.equal(classifyInternetBrand("8.8.8.8", 47, 0, 0, null), null, "GRE не бренд")
assert.equal(classifyInternetBrand("8.8.8.8", 6, 443, 51234, {
prefix: "8.8.8.0/24",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
})?.service, "Google")
assert.equal(mapInternetBrand("203.0.113.50", 6, 443, 51234, null).service, "Прочее")
assert.equal(mapInternetBrand("8.8.8.8", 47, 0, 0, null).service, "Прочее")
assert.equal(mapInternetBrand("8.8.8.8", 6, 443, 51234, {
prefix: "8.8.8.0/24",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
}).service, "Google")
assert.equal(classifyInternetBrand("8.8.8.8", 17, 53, 53000, {
prefix: "8.8.8.0/24",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
})?.service, "Google")
assert.equal(mapInternetBrand("8.8.8.8", 17, 53, 53000, {
prefix: "8.8.8.0/24",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
}).service, "Google")
assert.equal(mapInternetBrand("2001:4860:4860::8888", 17, 443, 50000, {
prefix: "2001:4860:4860::8888/128",
asn: 15169,
country: "US",
lat: null,
lng: null,
holder: "GOOGLE",
ok: true,
fetchedAt: Date.now(),
}).service, "YouTube")
assert.equal(mapInternetBrand("64.233.161.1", 17, 443, 50000, null).service, "YouTube")
assert.equal(mapInternetBrand("142.250.1.10", 6, 443, 1, null).service, "YouTube")
{
const meta = resolveInternetDest({
src: "74.125.104.196/32",
dst: "10.200.100.53/32",
proto: 17,
srcPort: 443,
dstPort: 62598,
serverId: 1,
inIface: "gre-client",
topo,
})
assert.equal(meta.dest, "74.125.104.196")
assert.equal(meta.classified.service, "YouTube")
assert.notEqual(meta.classified.service, "Прочее")
assert.equal(meta.endpoints.direction, "to_client")
assert.equal(meta.endpoints.clientIp, "10.200.100.53")
assert.equal(meta.asn, 0)
}
{
seedRipeCacheForTests({
prefix: "146.75.0.0/16",
asn: 54113,
country: "US",
lat: null,
lng: null,
holder: "FASTLY",
ok: true,
fetchedAt: Date.now(),
})
seedRipeCacheForTests({
prefix: "3.174.0.0/16",
asn: 16509,
country: "US",
lat: null,
lng: null,
holder: "AMAZON-AES",
ok: true,
fetchedAt: Date.now(),
})
const fastly = resolveInternetDest({
src: "146.75.118.132/32",
dst: "10.200.100.53/32",
proto: 6,
srcPort: 80,
dstPort: 35026,
serverId: 1,
inIface: "gre-client",
topo,
})
assert.equal(fastly.classified.service, "Fastly")
assert.equal(fastly.asn, 54113)
assert.equal(fastly.country, "US")
const aws = resolveInternetDest({
src: "3.174.2.35/32",
dst: "10.200.100.53/32",
proto: 6,
srcPort: 443,
dstPort: 43726,
serverId: 1,
inIface: "gre-client",
topo,
})
assert.equal(aws.classified.service, "AWS")
assert.equal(aws.asn, 16509)
}
resetEngineForTests()
seedFlowTopologyForTests(null)
resetRipeCacheForTests()
resetIfaceCacheForTests()
console.log("traffic-flow-dest.test.ts: ok")