78 lines
9.2 KiB
TypeScript
78 lines
9.2 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import Fastify from "fastify"
|
|
import { pool, closePool } from "../db/index.js"
|
|
import { initDatabase } from "../db/bootstrap.js"
|
|
import trafficRoutes from "../routes/traffic.js"
|
|
const database=process.env.MMAPP_OWN_SQL_DB
|
|
if(!database)console.log("traffic volume 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-volume180-"+Date.now()+"-",userId=prefix+"user",at=new Date(Date.now()-5000).toISOString(),updatedAt=new Date(Date.now()-3600_000).toISOString()
|
|
let serverId:number|undefined,cases=0
|
|
const app=Fastify();await app.register(trafficRoutes)
|
|
const get=async(path:string)=>{const r=await app.inject({method:"GET",url:path});assert.equal(r.statusCode,200);return r.json()}
|
|
const put=async(name:string,offset:number,bytes:number)=>pool.query("INSERT INTO traffic_samples(server_id,interface_name,sampled_at,rx_bytes,tx_bytes,flags) VALUES($1,$2,$3,$4,$4,5)",[serverId,name,new Date(Date.parse(at)+offset).toISOString(),bytes])
|
|
try {
|
|
await initDatabase()
|
|
serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password,updated_at) VALUES($1,'127.0.0.1',1,false,true,'fixture','fixture',$2) RETURNING id",[prefix+"server",updatedAt])).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 traffic_collection_state(server_id,server_updated_at,last_attempt_at,last_succeeded_at,status) VALUES($1,$2,$3,$3,'ok')",[serverId,updatedAt,at])
|
|
await pool.query("INSERT INTO app_users(id,login,name) VALUES($1,$2,'Volume fixture')",[userId,prefix+"login"])
|
|
await pool.query("INSERT INTO user_interface_bindings(id,user_id,server_id,interface_name,interface_type) VALUES($1,$2,$3,'ether1','ether')",[prefix+"binding",userId,serverId])
|
|
for(const bytes of[1,1024,1024**2,1024**3]){
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await put("ether1",-30_000,0);await put("ether1",0,bytes)
|
|
const s=(await get(`/traffic/servers/${serverId}?range=1h`)).server
|
|
assert.equal(s.volumeUnit,"GiB");assert.equal(s.rxTotal,bytes/1024**3);assert.equal(s.observations.rxNow,true)
|
|
const user=(await get("/traffic/users?range=1h")).users.find((u:{id:string})=>u.id===userId)
|
|
assert.equal(user.volumeUnit,"GiB");assert.equal(user.rxTotal,s.rxTotal)
|
|
const iface=(await get("/traffic/bound-interfaces?range=1h")).interfaces.find((i:{userId:string})=>i.userId===userId)
|
|
assert.equal(iface.volumeUnit,"GiB");assert.equal(iface.rxTotal,s.rxTotal);cases++
|
|
}
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await pool.query("DELETE FROM user_interface_bindings WHERE user_id=$1",[userId])
|
|
for(let i=0;i<100;i++){const name=`ether${i}`;await put(name,-30_000,0);await put(name,0,1024**2);await pool.query("INSERT INTO user_interface_bindings(id,user_id,server_id,interface_name,interface_type) VALUES($1,$2,$3,$4,'ether')",[prefix+"binding"+i,userId,serverId,name])}
|
|
let s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.rxTotal,100/1024)
|
|
const user=(await get("/traffic/users?range=1h")).users.find((u:{id:string})=>u.id===userId);assert.equal(user.rxTotal,100/1024);cases++
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await put("ether1",-90_000,0);await put("ether1",-60_000,1024**2);await put("ether1",-30_000,512);await put("ether1",0,1024)
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.rxTotal,(1024**2+512)/1024**3);cases++
|
|
for(const flags of[4,7]){
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);
|
|
for(const[name,bytes]of[["ether1",2048],["ether2",0]] as const){await put(name,-60_000,0);await put(name,-30_000,bytes/2);await put(name,0,bytes)}
|
|
await pool.query("UPDATE traffic_samples SET flags=$1 WHERE server_id=$2 AND interface_name='ether1' AND sampled_at=$3",[flags,serverId,at]);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.rxTotal,2048/1024**3);assert(s.observations.rxSeries.some(Boolean));assert(s.rxPeak>0);assert.equal(s.sessions,1);
|
|
const summary=(await get("/traffic/servers?range=1h")).servers.find((r:{id:string})=>r.id===String(serverId));assert.equal(summary.rxTotal,s.rxTotal);cases++
|
|
}
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await put("ether1",-120_000,0);await put("ether1",0,1024**2);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.rxTotal,1/1024);assert.equal(s.observations.rxNow,false);assert.equal(s.observations.measurementIntervalMs,120_000);assert(s.observations.error.includes("Интервал"));assert(s.observations.rxSeries.some(Boolean));cases++;
|
|
await pool.query("UPDATE traffic_samples SET sampled_at=$1 WHERE server_id=$2 AND sampled_at=$3",[new Date(Date.parse(at)-30_000).toISOString(),serverId,at]);await put("ether1",0,2*1024**2);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.observations.rxNow,true);assert.equal(s.observations.measurementIntervalMs,30_000);assert.equal(s.rxTotal,2/1024);cases++;
|
|
for(const changed of["deleted","renamed"]){
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);
|
|
for(const[name,offset,bytes]of[["ether1",-90_000,0],["ether1",-60_000,1024],["ether2",-90_000,0],["ether2",-60_000,1024],[changed==="renamed"?"newName":"ether2",-30_000,2048],[changed==="renamed"?"newName":"ether2",0,3072]] as const)await put(name,offset,bytes);
|
|
await pool.query("UPDATE traffic_samples SET flags=61,native_counter_identity=CASE WHEN interface_name='newName' THEN 'ether2' ELSE interface_name END WHERE server_id=$1",[serverId]);
|
|
if(changed==="renamed")await pool.query("UPDATE traffic_samples SET flags=45 WHERE server_id=$1 AND interface_name='newName' AND sampled_at=$2",[serverId,new Date(Date.parse(at)-30_000).toISOString()]);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.observations.rxNow,true);assert.equal(s.sessions,1);assert.equal(s.rxTotal,(changed==="renamed"?3072:4096)/1024**3);assert(s.observations.rxSeries.some(Boolean));cases++;
|
|
}
|
|
await put("newInterface",0,0);await pool.query("UPDATE traffic_samples SET flags=45 WHERE server_id=$1 AND interface_name='newInterface'",[serverId]);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.observations.rxNow,false);assert.equal(s.sessions,2);cases++;
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);
|
|
for(const[name,offset,bytes]of[["ether1",-90_000,0],["ether1",-60_000,0],["ether1",-30_000,1024**2],["new",-30_000,0],["ether1",0,1024**2],["new",0,0]] as const)await put(name,offset,bytes);
|
|
await pool.query("UPDATE traffic_samples SET flags=61,native_counter_identity=interface_name WHERE server_id=$1",[serverId]);await pool.query("UPDATE traffic_samples SET flags=45 WHERE server_id=$1 AND interface_name='new' AND sampled_at=$2",[serverId,new Date(Date.parse(at)-30_000).toISOString()]);
|
|
s=(await get(`/traffic/servers/${serverId}?range=1h`)).server;assert.equal(s.observations.rxNow,true);assert.equal(s.rxPeak,0);assert.equal(s.txPeak,0);assert.equal(s.rxTotal,1/1024);assert(s.observations.rxSeries.some(Boolean));cases++;
|
|
await pool.query("DELETE FROM user_interface_bindings WHERE user_id=$1",[userId])
|
|
for(const name of ['peakA','peakB'])await pool.query("INSERT INTO user_interface_bindings(id,user_id,server_id,interface_name,interface_type) VALUES($1,$2,$3,$4,'ether')",[prefix+name,userId,serverId,name])
|
|
for(const mode of ['separate','coincident','jitter']){
|
|
await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId])
|
|
for(const [name,offset,bytes] of [['peakA',-60_000,0],['peakA',-30_000,375_000_000],['peakA',0,375_000_000],
|
|
['peakB',-60_000,0],['peakB',-30_000,mode==='coincident'?375_000_000:0],['peakB',0,375_000_000]] as const)
|
|
await put(name,offset+(mode==='jitter'&&name==='peakB'?-1:0),bytes)
|
|
const aggregate=(await get('/traffic/users?range=1h')).users.find((u:{id:string})=>u.id===userId)
|
|
assert.equal(aggregate.rxPeak,mode==='jitter'?0:mode==='coincident'?200:100)
|
|
assert.equal(aggregate.observations.rxPeak,mode!=='jitter');assert.equal(aggregate.observations.txPeak,mode!=='jitter')
|
|
assert.equal(aggregate.rxTotal,750_000_000/1024**3);assert(aggregate.observations.rxSeries.some(Boolean))
|
|
assert.equal(aggregate.interfaces.length,2);assert(aggregate.interfaces.every((i:{rxPeak:number})=>i.rxPeak===100));cases++
|
|
}
|
|
console.log(`traffic volume owned SQL: ${cases} cases; exact bytes/binary unit/server/user/binding/100-part aggregate/reset passed`)
|
|
}finally{await app.close();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()}
|
|
}
|