Files
MikrotikManager/backend/src/services/recursive-apply.test.ts
T

27 lines
3.1 KiB
TypeScript

import assert from "node:assert/strict"
import type { MikrotikClient } from "./mikrotik.js"
import { MikrotikError } from "./mikrotik.js"
import { executeRecursiveApply, RecursiveApplyError } from "./recursive-apply.js"
const owned = { ".id": "*old", static: "true", "dst-address": "198.18.88.0/24", gateway: "192.0.2.1", distance: "1", scope: "30", "target-scope": "10", "routing-table": "main", "check-gateway": "none", disabled: "false", comment: "MikrotikManager:recursive test" }
let rows: unknown = [owned, { ".id": "*foreign", comment: "Keep default route" }], events: string[] = [], puts = 0
let failAt = 0, rejection: Error = new Error("ACK потерян"), badAck = false, failDelete = false, failPersist = false
const client: Pick<MikrotikClient,"get"|"put"|"delete"> = {
get: async <T>() => rows as T,
put: async <T>() => { puts++; events.push("put:"+puts); if(puts===failAt)throw rejection; return (badAck?{}:{".id":"*new"+puts}) as T },
delete: async path => { events.push("delete:"+path); if(failDelete)throw new MikrotikError(403,path,"rejected") },
}
const persist=async()=>{events.push("persist");if(failPersist)throw Error("История недоступна")}
const reset=()=>{events=[];puts=0;failAt=0;badAck=false;failDelete=false;failPersist=false;rows=[owned,{".id":"*foreign",comment:"Keep default route"}]}
const payloads=[{"dst-address":"198.18.88.1/32"},{"dst-address":"198.18.88.2/32"}]
reset();assert.deepEqual(await executeRecursiveApply(client,payloads,persist),{pushed:2,deleted:1});assert.deepEqual(events,["put:1","put:2","delete:/ip/route/*old","persist"])
for(const invalid of [{},[owned,owned],[{...owned,static:undefined}],[{...owned,blackhole:"true"}]]){reset();rows=invalid;await assert.rejects(executeRecursiveApply(client,payloads,persist));assert.deepEqual(events,[])}
async function expectState(state:string,confirmed:number){await assert.rejects(executeRecursiveApply(client,payloads,persist),(e:unknown)=>{assert(e instanceof RecursiveApplyError);assert.equal(e.receipt.state,state);assert.equal(e.receipt.confirmedWrites,confirmed);assert.equal(e.receipt.metadataRecorded,false);return true})}
reset();failAt=1;rejection=new MikrotikError(401,"/ip/route","denied");await expectState("rejected",0);assert.deepEqual(events,["put:1"])
reset();failAt=2;rejection=new MikrotikError(403,"/ip/route","denied");await expectState("partial",1);assert.deepEqual(events,["put:1","put:2"])
reset();failAt=2;rejection=new Error("ACK второго PUT потерян");await expectState("unknown",1);assert.deepEqual(events,["put:1","put:2"])
reset();badAck=true;await expectState("unknown",0);assert.deepEqual(events,["put:1"])
reset();failDelete=true;await expectState("partial",2);assert.deepEqual(events,["put:1","put:2","delete:/ip/route/*old"])
reset();failPersist=true;await expectState("applied",3)
reset();rows=[];assert.deepEqual(await executeRecursiveApply(client,[],persist),{pushed:0,deleted:0});assert.deepEqual(events,["persist"])
console.log("Recursive apply: strict ownership before PUT, foreign retention, partial/unknown/rejected/applied, no retry/delete after rejection")