Files
MikrotikManager/backend/src/routes/exec.test.ts
T

40 lines
5.9 KiB
TypeScript

import assert from "node:assert/strict"
import Fastify from "fastify"
import {validatorCompiler,serializerCompiler} from "@fastify/type-provider-zod"
import {createExecRoutes,type ExecDependencies} from "./exec.js"
import type {servers} from "../db/schema.js"
const outputs:Record<string,unknown>={getIdentity:{name:"test"},getResource:{}}
const calls:unknown[][]=[];let exists=true,lookups=0,enabled=true,clients=0
const names=["getIpAddresses","getInterfaces","getResource","getIdentity","getIpRoutes","getFirewallFilters","getBgpSessions","getOspfNeighbors","getOspfAreas","getOspfInstances","getOspfInterfaceTemplates","getBfdSessions","getLogs","ping"]
const client=Object.fromEntries(names.map(key=>[key,async(...args:unknown[])=>{calls.push([key,...args]);if(outputs[key] instanceof Error)throw outputs[key];return outputs[key]??[]}])) as unknown as ReturnType<ExecDependencies["createClient"]>
const app=Fastify();app.setValidatorCompiler(validatorCompiler);app.setSerializerCompiler(serializerCompiler)
await app.register(createExecRoutes({getServer:async()=>{lookups++;return exists?{id:1,name:"Тест",host:"192.0.2.1",enabled} as typeof servers.$inferSelect:undefined},createClient:()=>{clients++;return client}}),{prefix:"/api"})
const send=(command:string)=>app.inject({method:"POST",url:"/api/servers/1/exec",payload:{command}})
for(const command of ["ip address print","interface print","system resource print","system identity print","ip route print","ip firewall filter print","routing bgp session print","routing ospf neighbor print","routing ospf area print","routing ospf instance print","routing ospf interface print","routing bfd session print","log print","/IP ADDRESS PRINT","help"]){
const response=await send(command);assert.equal(response.statusCode,200,command);assert.equal(typeof response.json().output,"string")
}
outputs.getBfdSessions=new Error("BFD недоступен");const failed=await send("routing bfd session print");assert.equal(failed.statusCode,502);assert.equal(failed.json().error,"BFD недоступен");assert.equal(failed.json().output,undefined)
outputs.getBfdSessions=[];assert.equal((await send("routing bfd session print")).statusCode,200)
outputs.getBfdSessions=[{".id":"*1"}];const bfd=(await send("routing bfd session print")).json().output;assert.ok(bfd.includes("?"));assert.ok(!bfd.includes("down"))
outputs.getBgpSessions=[{".id":"*1"}];assert.ok(!(await send("routing bgp session print")).json().output.includes("active"))
outputs.getOspfInterfaceTemplates=[{".id":"*1"}];const iface=(await send("routing ospf interface print")).json().output;assert.ok(!iface.includes("broadcast"));assert.ok(!iface.includes("no"))
outputs.getOspfAreas=[{name:"test"}];assert.ok(!(await send("routing ospf area print")).json().output.includes("0.0.0.0"))
outputs.getOspfInstances=[{name:"test"}];assert.match((await send("routing ospf instance print")).json().output,/test\s+\?\s+\?/)
outputs.getFirewallFilters=[{".id":"*1",chain:"input",action:"accept",packets:"0"}];assert.ok((await send("ip firewall filter print")).json().output.includes("packets=0 bytes=?"))
outputs.getResource={"free-memory":"junk"};assert.ok(!(await send("system resource print")).json().output.includes("NaN"))
outputs.ping=[{seq:"0",status:"timeout"},{sent:"0",received:"0"}];const ping=(await send("ping 192.0.2.2 count=2")).json().output;assert.ok(ping.includes("seq=0 timeout"));assert.ok(ping.includes("sent=0 received=0 packet-loss=?"));assert.deepEqual(calls.at(-1),["ping","192.0.2.2",2])
outputs.ping=[{seq:"0",time:"1ms"},{seq:"1",time:"9ms",sent:"2",received:"2","avg-rtt":"5ms"}];assert.match((await send("ping 192.0.2.2 count=2")).json().output,/avg-rtt=5ms/)
outputs.ping=[{seq:"0",time:"9ms",sent:"1",received:"1"}];assert.match((await send("ping 192.0.2.2 count=1")).json().output,/avg-rtt=\?/)
for(const average of ["junk","",undefined]){outputs.ping=[{sent:"1",received:"1",time:"9ms","avg-rtt":average}];assert.match((await send("ping 192.0.2.2 count=1")).json().output,/avg-rtt=\?/)}
outputs.ping=[{sent:"1",received:"0","avg-rtt":"0ms"}];assert.match((await send("ping 192.0.2.2 count=1")).json().output,/avg-rtt=\?/)
outputs.ping=[{sent:"1",received:"1","avg-rtt":"0ms"}];assert.match((await send("ping 192.0.2.2 count=1")).json().output,/avg-rtt=0ms/)
assert.equal((await send("ping 192.0.2.2 count=99")).statusCode,200);assert.deepEqual(calls.at(-1),["ping","192.0.2.2",20])
assert.equal((await send("log print limit=500")).statusCode,200);assert.deepEqual(calls.at(-1),["getLogs",200])
outputs.getLogs=new Error("Журнал недоступен");const failedLogs=await send("log print limit=3");assert.equal(failedLogs.statusCode,502);assert.equal(failedLogs.json().error,"Журнал недоступен");assert.equal(failedLogs.json().output,undefined)
outputs.getLogs=[];const emptyLogs=await send("log print limit=3");assert.equal(emptyLogs.statusCode,200);assert.equal(emptyLogs.json().output,"(no log entries)")
for(const command of ["system reboot","pingbad 192.0.2.2","log printable","ping","ping 192.0.2.2 count=0","ping 192.0.2.2 count=1junk","ping 192.0.2.2 count=1 count=2","log print limit=0","log print limit=3junk"]){const before=calls.length;assert.equal((await send(command)).statusCode,400,command);assert.equal(calls.length,before)}
exists=false;assert.equal((await send("interface print")).statusCode,404);exists=true
enabled=false;const beforeDisabled={clients,calls:calls.length};for(const command of["interface print","ping 192.0.2.2 count=1","help"])assert.equal((await send(command)).statusCode,409);assert.deepEqual({clients,calls:calls.length},beforeDisabled);enabled=true
const before=lookups;for(const payload of [{},{command:""},{command:"x".repeat(1025)}])assert.equal((await app.inject({method:"POST",url:"/api/servers/1/exec",payload})).statusCode,400);assert.equal(lookups,before)
await app.close();console.log("Exec HTTP: readonly allowlist, errors/known empty, unknown versus defaults, bounded arguments and missing/bad requests; no DB/RouterOS")