fix(alerts): отклонять запись устаревшего снимка правил
This commit is contained in:
1 parent
c5c1c2812d
commit
382e318b97
23 files changed
+126
-43
No files matched your search
@@ -1,9 +1,9 @@
|
||||
import {test,expect,type Page,type Route} from "@playwright/test"
|
||||
import {alertsWithRule} from "./fixtures/alerts"
|
||||
import {alertsWithRule,alertsRule} from "./fixtures/alerts"
|
||||
async function setup(page:Page,write:(route:Route)=>Promise<unknown>,required=false){
|
||||
await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}));await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;if(path==="/api/auth/config")return r.fulfill({json:{required}});if(path==="/api/alerts")return r.fulfill({json:alertsWithRule});if(path==="/api/alerts/rules")return write(r);return r.fulfill({json:path==="/api/filters/gre-tunnels"?{tunnels:[],failures:[]}:[]})});await page.goto("/alerts");await expect(page.getByText("Всего: 1",{exact:true})).toBeVisible()
|
||||
}
|
||||
function receipt(r:Route){const body=r.request().postDataJSON();return {ok:true,groups:body.groups,rules:body.rules.map((rule:object)=>({...rule,lastFiredAt:null}))}}
|
||||
function receipt(r:Route){const body=r.request().postDataJSON();return {ok:true,revision:"2".repeat(64),groups:body.groups,rules:body.rules.map((rule:object)=>({...rule,lastFiredAt:null}))}}
|
||||
const toggle=(page:Page)=>page.getByRole("switch",{name:"Включить правило Fixture rule",exact:true})
|
||||
test("Rules: pending toggle keeps confirmed state, excludes writes and commits only strict receipt",async({page})=>{
|
||||
let calls=0,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
|
||||
@@ -25,3 +25,10 @@ test("Rules: read permission keeps all mutation entry points disabled",async({pa
|
||||
test("Rules: unknown edit keeps draft through explicit review and confirmed retry",async({page})=>{let calls=0;await setup(page,r=>{calls++;return r.fulfill({json:calls===1?{ok:false}:receipt(r)})});await page.getByRole("button",{name:"Изменить правило Fixture rule",exact:true}).click();const dialog=page.getByRole("dialog"),input=dialog.getByRole("textbox",{name:"Название правила",exact:true});await input.fill("Changed unknown draft");await dialog.getByRole("button",{name:"Сохранить изменения",exact:true}).click();await expect(dialog.getByRole("alert")).toContainText("не подтверждено");await expect(input).toHaveValue("Changed unknown draft");await expect(dialog.getByRole("button",{name:"Сохранить изменения",exact:true})).toBeDisabled();await dialog.getByRole("button",{name:"Я проверил правила — разрешить новый запрос",exact:true}).click();await dialog.getByRole("button",{name:"Сохранить изменения",exact:true}).click();await expect(dialog).not.toBeVisible();await expect(page.getByText("Changed unknown draft",{exact:true})).toBeVisible();expect(calls).toBe(2)})
|
||||
test("Rules presets: no confirmed RTT targets cannot clear existing rules",async({page})=>{let calls=0;await setup(page,r=>{calls++;return r.fulfill({json:receipt(r)})});await page.getByRole("button",{name:/Пресеты и группы/}).click();await page.getByRole("button",{name:"Заменить все",exact:true}).nth(1).click();await page.getByRole("alertdialog").getByRole("button",{name:"Заменить",exact:true}).click();await expect(page.locator("#main-content").getByRole("alert")).toHaveText("Для пресета нет подтверждённых объектов. Список правил сохранён.");await expect(page.getByText("Fixture rule",{exact:true})).toBeVisible();expect(calls).toBe(0)})
|
||||
test("Groups: confirmed delete removes group while retaining its rules without membership",async({page})=>{let payload:{groups:unknown[];rules:{groupId:string|null}[]}|undefined;await setup(page,r=>{payload=r.request().postDataJSON();return r.fulfill({json:receipt(r)})});await page.getByRole("button",{name:/Пресеты и группы/}).click();await page.getByRole("region",{name:"Группа Fixture group",exact:true}).getByRole("button",{name:"Удалить группу",exact:true}).click();await page.getByRole("alertdialog").getByRole("button",{name:"Удалить",exact:true}).click();await expect(page.getByRole("region",{name:"Группа Fixture group",exact:true})).toHaveCount(0);await expect(page.getByText("Fixture rule",{exact:true})).toBeVisible();expect(payload?.groups).toEqual([]);expect(payload?.rules).toHaveLength(1);expect(payload?.rules[0]?.groupId).toBeNull()})
|
||||
|
||||
test("Rules revision: conflict refresh preserves draft and sends new revision with concurrent rule",async({page})=>{
|
||||
const latest="3".repeat(64),headers:(string|undefined)[]=[];let calls=0,payload:{rules:{id:string}[]}|undefined
|
||||
await setup(page,r=>{calls++;headers.push(r.request().headers()["if-match"]);payload=r.request().postDataJSON();return r.fulfill(calls===1?{status:412,json:{error:"Правила изменились на сервере"}}:{json:receipt(r)})})
|
||||
await page.getByRole("button",{name:"Изменить правило Fixture rule",exact:true}).click();const dialog=page.getByRole("dialog"),input=dialog.getByRole("textbox",{name:"Название правила",exact:true});await input.fill("Conflict draft");await dialog.getByRole("button",{name:"Сохранить изменения",exact:true}).click();await expect(dialog.getByRole("alert")).toHaveText("Правила изменились на сервере")
|
||||
await page.route("**/api/alerts",r=>r.fulfill({json:{...alertsWithRule,revision:latest,rules:[alertsRule,{...alertsRule,id:"concurrent-rule",name:"Concurrent rule"}]}}));await dialog.getByRole("button",{name:"Обновить снимок — сохранить черновик",exact:true}).click();await expect(page.getByText("Всего: 2",{exact:true})).toBeVisible();await expect(input).toHaveValue("Conflict draft");await dialog.getByRole("button",{name:"Сохранить изменения",exact:true}).click();await expect(dialog).not.toBeVisible();await expect(page.getByText("Concurrent rule",{exact:true})).toBeVisible();expect(headers).toEqual(['"'+"1".repeat(64)+'"','"'+latest+'"']);expect(payload?.rules.map(rule=>rule.id)).toEqual(["rule-fixture","concurrent-rule"]);expect(calls).toBe(2)
|
||||
})
|
||||
Reference in new issue
Block a user