Files
MikrotikManager/backend/src/services/traffic-status-sql.test.ts
T

63 lines
9.7 KiB
TypeScript

import assert from "node:assert/strict"
import {createServer} from "node:http"
import Fastify from "fastify"
import {pool,closePool} from "../db/index.js"
import {initDatabase} from "../db/bootstrap.js"
import {collectTrafficOnce} from "./traffic-collector.js"
import trafficRoutes from "../routes/traffic.js"
const database=process.env.MMAPP_OWN_SQL_DB
if(!database)console.log("traffic status SQL: skipped; explicit owned database required")
else{
assert(/^mm_accept_166_\d+$/.test(database));assert.equal(new URL(process.env.DATABASE_URL!).pathname,"/"+database);assert.equal((await pool.query("SELECT current_database() AS name")).rows[0].name,database)
const prefix="own-status181-"+Date.now()+"-",userId=prefix+"user";let serverId:number|undefined,cases=0,deny=false,flag:unknown=true,peerDisabled=false,peerVariant="valid",denyPeer=false,ifaceId:unknown="*1",counterValue="1000",peerId="*2",peerCounter="1000"
let switchSource="",peerRequests=0;const http=createServer(async(req,res)=>{if(req.url?.includes("/wireguard/"))peerRequests++;
if(switchSource && !req.url?.includes("/wireguard/")){
const action=switchSource;switchSource="";
if(action==="disable")await pool.query("UPDATE servers SET enabled=false WHERE id=$1",[serverId]);
else await pool.query("UPDATE servers SET updated_at=$1 WHERE id=$2",[new Date().toISOString(),serverId]);
}
res.setHeader("Content-Type","application/json");if(deny || denyPeer && req.url?.includes("/wireguard/")){res.statusCode=401;return res.end('{"message":"Unauthorized"}')}res.end(JSON.stringify(req.url?.includes("/wireguard/")?(peerVariant==="shape"?{}:peerVariant==="duplicate"?[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false},{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false}]:[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:peerCounter,tx:peerCounter,disabled:peerDisabled},...(peerVariant==="partial"?[{".id":peerId,interface:"wg1","public-key":"broken",rx:"invalid",tx:"1000",disabled:false}]:[])]):[{".id":ifaceId,name:"ether1",running:flag,disabled:false,"rx-byte":counterValue,"tx-byte":counterValue}]))})
await new Promise<void>(resolve=>http.listen(0,"127.0.0.1",resolve));const address=http.address();assert(address&&typeof address==="object");const app=Fastify();await app.register(trafficRoutes)
const bindings=async()=>{const r=await app.inject({method:"GET",url:"/traffic/bound-interfaces?range=1h"});assert.equal(r.statusCode,200);return r.json().interfaces.filter((i:{userId:string})=>i.userId===userId)}
try{
await initDatabase();const updated=new Date(Date.now()-3600_000).toISOString()
serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password,updated_at) VALUES($1,'127.0.0.1',$2,false,true,'fixture','fixture',$3) RETURNING id",[prefix+"server",address.port,updated])).rows[0].id)
await pool.query("INSERT INTO traffic_settings(id,enabled,interval_sec) VALUES(1,true,30) ON CONFLICT(id) DO UPDATE SET enabled=true,interval_sec=30")
await pool.query("INSERT INTO app_users(id,login,name) VALUES($1,$2,'Status fixture')",[userId,prefix+"login"])
for(const[name,key]of[["ether1",""],["wg1","fixturekey"]])await pool.query("INSERT INTO user_interface_bindings(id,user_id,server_id,interface_name,interface_type,peer_public_key) VALUES($1,$2,$3,$4,$5,$6)",[prefix+name,userId,serverId,name,key?"wg":"ether",key])
await collectTrafficOnce();let rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").status,"online");assert(rows[0].statusObservedAt&&rows[0].statusExpiresAt);cases++
assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1").status,"unknown");cases++
peerDisabled=true;await collectTrafficOnce();rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1").status,"offline");cases++
flag=false;await collectTrafficOnce();rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").status,"offline");cases++
flag="bad";let snap=await collectTrafficOnce();assert.equal(snap.servers[0].ok,false);rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
flag=true;deny=true;await collectTrafficOnce();rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
deny=false;await collectTrafficOnce();await pool.query("UPDATE traffic_samples SET flags=flags & 7 WHERE server_id=$1",[serverId]);rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
await collectTrafficOnce();await pool.query("UPDATE traffic_collection_state SET last_attempt_at=$1 WHERE server_id=$2",[new Date(Date.now()-100_000).toISOString(),serverId]);rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
for(const variant of["partial","duplicate","shape"]){
peerVariant=variant;await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);const result=await collectTrafficOnce();assert.equal(result.servers[0].ok,true);assert(result.servers[0].warning?.includes("WireGuard"));assert((await pool.query("SELECT error FROM traffic_collection_state WHERE server_id=$1",[serverId])).rows[0].error.includes("WireGuard"))
const saved=(await pool.query("SELECT peer_public_key FROM traffic_samples WHERE server_id=$1",[serverId])).rows;assert.equal(saved.length,1);assert.equal(saved[0].peer_public_key,"")
rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1").status,"unknown");assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").status,"online");cases++
}
denyPeer=true;peerVariant="valid";let partial=await collectTrafficOnce();assert.equal(partial.servers[0].ok,true);assert(partial.servers[0].warning?.includes("401"));rows=await bindings();assert(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").observations.error.includes("401"));cases++
const manualPartial=await app.inject({method:"POST",url:"/traffic/collect-now"});assert.equal(manualPartial.statusCode,200);assert.equal(manualPartial.json().ok,true);assert.equal(manualPartial.json().warnings.length,1);assert(manualPartial.json().lastError.includes("401"));
denyPeer=false;partial=await collectTrafficOnce();assert.equal(partial.servers[0].warning,undefined);assert.equal((await pool.query("SELECT error FROM traffic_collection_state WHERE server_id=$1",[serverId])).rows[0].error,null);cases++
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1 AND sampled_at<>(SELECT max(sampled_at) FROM traffic_samples WHERE server_id=$1)",[serverId]);
await pool.query("UPDATE traffic_samples SET sampled_at=$1 WHERE server_id=$2",[new Date(Date.now()-120_000).toISOString(),serverId]);
let gap=await collectTrafficOnce();assert.equal(gap.servers[0].ok,true);assert.equal(gap.servers[0].sumRxMbps,undefined);rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").observations.rxNow,false);
gap=await collectTrafficOnce();assert.equal(gap.servers[0].sumRxMbps,0);rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").observations.rxNow,true);cases++;
for(const change of["native-id","config","missing-id"]){
counterValue=String(Number(counterValue)+1000);
if(change==="native-id")ifaceId="*10";else if(change==="missing-id")ifaceId=undefined;else await pool.query("UPDATE servers SET updated_at=$1 WHERE id=$2",[new Date().toISOString(),serverId]);
const switched=await collectTrafficOnce();assert.equal(switched.servers[0].sumRxMbps,undefined);rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").observations.rxNow,false);assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").rxTotal,0);cases++;
if(change!=="missing-id"){const next=await collectTrafficOnce();assert.equal(next.servers[0].sumRxMbps,0);rows=await bindings();assert.equal(rows.find((r:{interfaceName:string})=>r.interfaceName==="ether1").observations.rxNow,true)}
}
ifaceId="*10";await collectTrafficOnce();await collectTrafficOnce();peerId="*20";peerCounter="2000";await collectTrafficOnce();rows=await bindings();let peer=rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1");assert.equal(peer.observations.rxNow,false);assert.equal(peer.rxTotal,0);
await collectTrafficOnce();rows=await bindings();peer=rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1");assert.equal(peer.observations.rxNow,true);assert.equal(peer.rxNow,0);cases++;
for(const action of["disable","config"]){
const before=peerRequests,count=Number((await pool.query("SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1",[serverId])).rows[0].n);switchSource=action;const switched=await collectTrafficOnce();assert.equal(switched.servers[0].ok,false);assert(switched.servers[0].error?.includes("Сервер"));assert.equal(peerRequests,before);assert.equal(Number((await pool.query("SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1",[serverId])).rows[0].n),count);await pool.query("UPDATE servers SET enabled=true WHERE id=$1",[serverId]);cases++;
}
await pool.query("UPDATE servers SET enabled=false WHERE id=$1",[serverId]);rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
console.log(`traffic status owned SQL: ${cases} cases; first-wave native flags/peer config/invalid/error/legacy/stale/disabled passed`)
}finally{await app.close();await new Promise<void>(resolve=>http.close(()=>resolve()));await pool.query("DELETE FROM app_users WHERE id=$1",[userId]);if(serverId!==undefined){await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await pool.query("DELETE FROM servers WHERE id=$1",[serverId])}await closePool()}
}