fix(ui): проверять контекст расчёта OSPF оптимизатора
This commit is contained in:
1 parent
f93dfe34c2
commit
bf2815b5a4
9 files changed
+131
-108
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import {test,expect,type Page} from "@playwright/test"
|
||||
const hosts=(name="Роутер")=>(["home-router","jump-host","exit-node"] as const).map((type,index)=>({id:index+1,name:name+" "+(index+1),host:"192.0.2."+(index+1),type,site:"TEST",country:"RU",enabled:true,status:"online",latency:10,model:"CHR",...(index===0?{wanUplinks:[{id:"wan",name:"WAN",isp:"ISP",iface:"ether1",ip:"198.51.100.1",maxDl:100,maxUl:100}]}:{})}))
|
||||
const paths={servers:"/api/servers",rules:"/api/filters/rules",probes:"/api/uptime/speed-probes"} as const
|
||||
const body=(p:string,name="Роутер")=>p===paths.servers?hosts(name):p===paths.rules?{rulesets:[{serverId:"1",rules:[{id:"r",community:"65001:1",communityName:"Сервис",gateway:"192.0.2.2",gatewayTunnelId:"",description:""}]}]}:p===paths.probes?{probes:[]}:p==="/api/auth/config"?{required:false}:p.endsWith("/preview")?{interfaces:[],changes:[],changedCount:0,interfacesTotal:0}:p.endsWith("/ospf")?{interfaces:[],instances:[],neighbors:[],bfdSessions:[]}:{}
|
||||
const body=(p:string,name="Роутер")=>p===paths.servers?hosts(name):p===paths.rules?{rulesets:[{serverId:"1",rules:[{id:"r",community:"65001:1",communityName:"Сервис",gateway:"192.0.2.2",gatewayTunnelId:"",description:""}]}]}:p===paths.probes?{probes:[]}:p==="/api/auth/config"?{required:false}:p.endsWith("/preview")?{serverId:1,serverName:"Роутер 1",pingWeight:60,unchangedCount:0,interfaces:[],changes:[],changedCount:0,interfacesTotal:0}:p==="/api/ospf/catalog"?{interfaces:[],instances:[],neighbors:[],bfdSessions:[],routes:[],failures:[]}:{}
|
||||
async function health(page:Page){await page.route("**/health",r=>r.fulfill({json:{ok:true}}))}
|
||||
for(const part of ["servers","rules","probes"] as const)for(const malformed of [false,true])test("Оптимизатор: первоначальная ошибка и повтор "+part+"/"+malformed,async({page})=>{
|
||||
let failing=true;await health(page)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import {test,expect,type Page} from "@playwright/test"
|
||||
const iface=(name="ether1")=>({id:"i",interface:name,currentCost:10,optimalCost:20,score:50,pingMs:10,dlMbps:100,ulMbps:50})
|
||||
const preview=(id=1,weight=60,name="ether1")=>({serverId:id,serverName:"Роутер "+id,pingWeight:weight,interfacesTotal:1,changedCount:1,unchangedCount:0,interfaces:[iface(name)],changes:[iface(name)]})
|
||||
async function base(page:Page){
|
||||
await page.route("**/health",r=>r.fulfill({json:{ok:true}}))
|
||||
await page.route("**/api/**",r=>{const p=new URL(r.request().url()).pathname;return r.fulfill({json:p==="/api/servers"?[1,2].map(id=>({id,name:"Роутер "+id,host:"192.0.2."+id,type:"home-router",country:"RU",enabled:true,status:null})):p==="/api/auth/config"?{required:false}:p==="/api/filters/rules"?{rulesets:[]}:p==="/api/uptime/speed-probes"?{probes:[]}:p==="/api/ospf/catalog"?{interfaces:[],instances:[],neighbors:[],bfdSessions:[],routes:[],failures:[]}:preview(Number(p.split("/")[3]),r.request().postDataJSON()?.pingWeight??60)})})
|
||||
}
|
||||
for(const failure of ["http","foreign","weight","count","missing","duplicate"] as const)test("Оптимизатор: OSPF preview блокирует "+failure,async({page})=>{
|
||||
await base(page);let failing=true
|
||||
await page.route("**/ospf/optimize/preview",r=>{
|
||||
const good=preview(),bad=failure==="foreign"?{...good,serverId:2}:failure==="weight"?{...good,pingWeight:20}:failure==="count"?{...good,changedCount:0}:failure==="duplicate"?{...good,interfaces:[iface(),iface()],interfacesTotal:2}:{}
|
||||
return r.fulfill(failing?(failure==="http"?{status:503,json:{error:"Preview недоступен"}}:{json:bad}):{json:good})
|
||||
})
|
||||
await page.goto("/route-optimizer")
|
||||
await expect(page.getByRole("alert").filter({hasText:"Ошибка preview"})).toBeVisible()
|
||||
const apply=page.getByRole("button",{name:"Оптимизировать OSPF",exact:true});await expect(apply).toBeDisabled()
|
||||
await expect(page.getByRole("table").filter({hasText:"Speed (dl/ul)"})).toHaveCount(0)
|
||||
failing=false;await page.getByRole("button",{name:"Обновить preview OSPF",exact:true}).click()
|
||||
await expect(page.getByRole("table").filter({hasText:"Speed (dl/ul)"}).locator("tr[data-row-id]")).toHaveCount(1)
|
||||
await expect(apply).toBeEnabled()
|
||||
})
|
||||
test("Оптимизатор: preview сохраняется при фоновом отказе",async({page})=>{
|
||||
await base(page);let failing=false
|
||||
await page.route("**/ospf/optimize/preview",r=>r.fulfill(failing?{status:503,json:{error:"Фоновый отказ"}}:{json:preview()}))
|
||||
await page.goto("/route-optimizer");const grid=page.getByRole("table").filter({hasText:"Speed (dl/ul)"});await expect(grid.locator("tr[data-row-id]")).toHaveCount(1)
|
||||
failing=true;await page.getByRole("button",{name:"Обновить preview OSPF",exact:true}).click()
|
||||
await expect(page.getByRole("alert").filter({hasText:"Последний снимок"})).toBeVisible();await expect(grid.locator("tr[data-row-id]")).toHaveCount(1)
|
||||
await expect(page.getByRole("button",{name:"Оптимизировать OSPF",exact:true})).toBeDisabled()
|
||||
})
|
||||
test("Оптимизатор: поздний preview прежнего устройства игнорируется",async({page})=>{
|
||||
await base(page);let started=false,release!:()=>void;const gate=new Promise<void>(resolve=>{release=resolve})
|
||||
await page.route("**/ospf/optimize/preview",async r=>{const id=Number(new URL(r.request().url()).pathname.split("/")[3]);if(id===1){started=true;await gate}return r.fulfill({json:preview(id,60,"ether"+id)})})
|
||||
await page.goto("/route-optimizer");await expect.poll(()=>started).toBe(true)
|
||||
await page.locator("select").selectOption("2")
|
||||
const grid=page.getByRole("table").filter({hasText:"Speed (dl/ul)"});await expect(grid).toContainText("ether2")
|
||||
const response=page.waitForResponse(r=>new URL(r.url()).pathname==="/api/servers/1/ospf/optimize/preview");release();await(await response).finished()
|
||||
await page.evaluate(()=>new Promise<void>(resolve=>requestAnimationFrame(()=>requestAnimationFrame(()=>resolve()))))
|
||||
await expect(grid).toContainText("ether2");await expect(grid).not.toContainText("ether1")
|
||||
})
|
||||
@@ -14,15 +14,15 @@ async function setup(page: Page, preview: "ok" | "empty" | "error" = "ok") {
|
||||
const path = new URL(route.request().url()).pathname
|
||||
if (path.endsWith("/ospf/optimize/preview")) {
|
||||
if (preview === "error") return route.fulfill({ status: 500, json: { error: "Расчёт недоступен" } })
|
||||
const interfaces = preview === "empty" ? [] : ids.map(i => ({ interface: `ether${i}`, currentCost: i, optimalCost: 20, score: i, pingMs: i, dlMbps: 100, ulMbps: 50 }))
|
||||
return route.fulfill({ json: { interfaces, changes: interfaces, changedCount: interfaces.length, interfacesTotal: interfaces.length } })
|
||||
const interfaces = preview === "empty" ? [] : ids.map(i => ({ id: String(i), interface: `ether${i}`, currentCost: i, optimalCost: 20, score: i, pingMs: i, dlMbps: 100, ulMbps: 50 }))
|
||||
return route.fulfill({ json: {serverId:1,serverName:"Роутер 1",pingWeight:60,unchangedCount:0, interfaces, changes: interfaces, changedCount: interfaces.length, interfacesTotal: interfaces.length } })
|
||||
}
|
||||
return route.fulfill({ json: path === "/api/servers" ? servers
|
||||
: path === "/api/auth/config" ? { required: false }
|
||||
: path === "/api/evobgp/settings" ? { enabled: false, baseUrl: "", secretConfigured: false }
|
||||
: path === "/api/filters/rules" ? { rulesets: [{ serverId: "1", rules: ids.map(i => ({ id: String(i), community: `65001:${i}`, communityName: `Сервис ${i}`, gateway: "192.0.2.2", gatewayTunnelId: "", description: "" })) }] }
|
||||
: path === "/api/uptime/speed-probes" ? { probes: [] }
|
||||
: path.endsWith("/ospf") ? { interfaces: [], instances: [], neighbors: [], bfdSessions: [] } : {} })
|
||||
: path === "/api/ospf/catalog" ? { interfaces: [], instances: [], neighbors: [], bfdSessions: [], routes: [], failures: [] } : {} })
|
||||
})
|
||||
await page.goto("/route-optimizer")
|
||||
await expect(page.getByRole("table").filter({ hasText: "WAN-аплинк" }).locator("tr[data-row-id]")).toHaveCount(12)
|
||||
|
||||
@@ -6,7 +6,7 @@ test("Оптимизатор: live рекомендации не подтвер
|
||||
await page.route("**/api/**",r=>{
|
||||
const p=new URL(r.request().url()).pathname
|
||||
if(r.request().method()!=="GET"&&!p.endsWith("/preview"))writes++
|
||||
return r.fulfill({json:p==="/api/auth/config"?{required:false}:p==="/api/servers"?hosts:p==="/api/filters/rules"?{rulesets:[{serverId:"1",rules:[{id:"r",community:"65001:1",communityName:"Сервис",gateway:"192.0.2.2",gatewayTunnelId:"",description:""}]}]}:p==="/api/uptime/speed-probes"?{probes:[]}:p.endsWith("/preview")?{interfaces:[],changes:[],changedCount:0,interfacesTotal:0}:p.endsWith("/ospf")?{interfaces:[],instances:[],neighbors:[],bfdSessions:[]}:{}})
|
||||
return r.fulfill({json:p==="/api/auth/config"?{required:false}:p==="/api/servers"?hosts:p==="/api/filters/rules"?{rulesets:[{serverId:"1",rules:[{id:"r",community:"65001:1",communityName:"Сервис",gateway:"192.0.2.2",gatewayTunnelId:"",description:""}]}]}:p==="/api/uptime/speed-probes"?{probes:[]}:p.endsWith("/preview")?{serverId:1,serverName:"Роутер 1",pingWeight:60,unchangedCount:0,interfaces:[],changes:[],changedCount:0,interfacesTotal:0}:p==="/api/ospf/catalog"?{interfaces:[],instances:[],neighbors:[],bfdSessions:[],routes:[],failures:[]}:{}})
|
||||
})
|
||||
await page.goto("/route-optimizer")
|
||||
await page.getByRole("button",{name:"BGP community (1)",exact:true}).click()
|
||||
|
||||
Reference in new issue
Block a user