fix(evolution): разделить состояния настроек и каталога и защитить контекст
This commit is contained in:
1 parent
554dba55ec
commit
9db1471b2c
11 files changed
+158
-202
No files matched your search
@@ -0,0 +1,16 @@
|
||||
import {test,expect,type Page} from "@playwright/test"
|
||||
import {evoSettings as settings,evoCatalog as catalog} from "./fixtures/evobgp"
|
||||
const headers={"access-control-allow-origin":"*","access-control-allow-methods":"GET,POST,PUT,OPTIONS","access-control-allow-headers":"Content-Type,Authorization"}
|
||||
async function setup(page:Page){await page.route("**/health",r=>r.fulfill({headers,json:{ok:true}}));await page.route("**/api/**",r=>{const url=new URL(r.request().url()),fresh=url.port==="8101";return r.fulfill(url.pathname==="/api/auth/config"?{headers,json:{required:false}}:url.pathname==="/api/evobgp/settings"?{headers,json:{...settings,baseUrl:fresh?"https://new.evo.test":settings.baseUrl}}:url.pathname==="/api/evobgp/catalog"?{headers,json:{...catalog,domains:[{...catalog.domains[0],domain:fresh?"fresh.example.test":"stale.example.test"}]}}:{headers,status:503,json:{error:"outside Evolution context"}})})}
|
||||
for(const action of ["settings","catalog","save"] as const)for(const failed of [false,true])test("Evolution provider: старый backend/"+action+"/"+failed,async({page})=>{
|
||||
await setup(page);let started=false,completed=false,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
|
||||
const path=action==="catalog"?"/api/evobgp/catalog":"/api/evobgp/settings"
|
||||
await page.route("**"+path,async r=>{if(new URL(r.request().url()).port!=="8000"||r.request().method()==="OPTIONS"||(action==="save"?r.request().method()!=="PUT":action==="settings"?r.request().method()!=="GET":false))return r.fallback();started=true;await gate;try{await r.fulfill(failed?{headers,status:503,json:{error:"СТАРАЯ ОШИБКА"}}:{headers,json:action==="settings"?settings:action==="catalog"?{...catalog,domains:[{...catalog.domains[0],domain:"stale.example.test"}]}:{...settings,...r.request().postDataJSON(),ok:true,savedFields:Object.keys(r.request().postDataJSON())}})}catch{}finally{completed=true}})
|
||||
try{
|
||||
await page.goto("/settings")
|
||||
if(action==="save"){await page.getByRole("button",{name:"EvoBGP",exact:true}).click();await expect(page.getByPlaceholder("http://localhost:8080",{exact:true})).toHaveValue(settings.baseUrl);await page.getByPlaceholder("http://localhost:8080",{exact:true}).fill("https://old-write.evo.test");await page.getByRole("button",{name:"Сохранить",exact:true}).click()}
|
||||
await expect.poll(()=>started).toBe(true);if(action==="save")await page.getByRole("button",{name:"Общие",exact:true}).click()
|
||||
const url=page.getByPlaceholder("http://localhost:8000",{exact:true});await url.fill("http://localhost:8101");await url.press("Enter");await page.getByRole("button",{name:"EvoBGP",exact:true}).click();await expect(page.getByPlaceholder("http://localhost:8080",{exact:true})).toHaveValue("https://new.evo.test");release();await expect.poll(()=>completed).toBe(true)
|
||||
await expect(page.getByPlaceholder("http://localhost:8080",{exact:true})).toHaveValue("https://new.evo.test");await expect(page.getByText("СТАРАЯ ОШИБКА",{exact:true})).toHaveCount(0);await page.locator('a[href="/domains"]').first().click();await expect(page.getByText("fresh.example.test",{exact:true})).toBeVisible();await expect(page.getByText("stale.example.test",{exact:true})).toHaveCount(0)
|
||||
}finally{release()}
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
import {test,expect,type Page} from "@playwright/test"
|
||||
import {evoSettings as settings,evoCatalog as catalog} from "./fixtures/evobgp"
|
||||
async function setup(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(p==="/api/auth/config"?{json:{required:false}}:p==="/api/evobgp/settings"?{json:settings}:p==="/api/evobgp/catalog"?{json:catalog}:{status:503,json:{error:"outside Evolution fixture"}})})}
|
||||
async function open(page:Page){await page.goto("/settings");await page.getByRole("button",{name:"EvoBGP",exact:true}).click()}
|
||||
const base=(page:Page)=>page.getByPlaceholder("http://localhost:8080",{exact:true})
|
||||
const settingsStamp=(page:Page)=>page.getByRole("status").filter({hasText:/^Настройки EvoBGP получены:/})
|
||||
const catalogStamp=(page:Page)=>page.getByRole("status").filter({hasText:/^Каталог EvoBGP получен:/})
|
||||
async function ready(page:Page){await expect(base(page)).toHaveValue(settings.baseUrl);await expect(catalogStamp(page)).toBeVisible();await expect(page.getByText("Обновление каталога EvoBGP…",{exact:true})).toHaveCount(0)}
|
||||
test("Evolution provider: malformed settings остаются unknown",async({page})=>{await setup(page);await page.route("**/api/evobgp/settings",r=>r.fulfill({json:{}}));await open(page);await expect(page.getByText("Настройки EvoBGP неизвестны",{exact:true})).toBeVisible();await expect(page.getByText("Ключ: —",{exact:true})).toBeVisible();await expect(settingsStamp(page)).toHaveCount(0);await expect(base(page)).toHaveValue("")})
|
||||
test("Evolution provider: каталог не задерживает known настройки",async({page})=>{await setup(page);let started=false,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve);await page.route("**/api/evobgp/catalog",async r=>{started=true;await gate;try{await r.fulfill({json:catalog})}catch{}});try{await open(page);await expect.poll(()=>started).toBe(true);await expect(base(page)).toHaveValue(settings.baseUrl);await expect(settingsStamp(page)).toBeVisible();await expect(page.getByText("Загрузка каталога EvoBGP…",{exact:true})).toBeVisible();release();await expect(catalogStamp(page)).toBeVisible()}finally{release()}})
|
||||
test("Evolution provider: ошибка settings GET сохраняет настройки, каталог и время",async({page})=>{await setup(page);let failed=false;await page.route("**/api/evobgp/settings",r=>r.fulfill(failed?{status:503,json:{error:"settings unavailable"}}:{json:settings}));await open(page);await ready(page);const before=await settingsStamp(page).textContent(),catBefore=await catalogStamp(page).textContent();failed=true;await page.getByRole("button",{name:"Обновить настройки EvoBGP",exact:true}).click();await expect(page.getByText("Настройки EvoBGP не обновлены",{exact:true})).toBeVisible();await expect(settingsStamp(page)).toHaveText(before!);await expect(catalogStamp(page)).toHaveText(catBefore!);await expect(base(page)).toHaveValue(settings.baseUrl)})
|
||||
test("Evolution provider: ошибка каталога сохраняет последний снимок и время",async({page})=>{await setup(page);let failed=false;await page.route("**/api/evobgp/catalog",r=>r.fulfill(failed?{status:503,json:{error:"catalog unavailable"}}:{json:catalog}));await open(page);await ready(page);const before=await catalogStamp(page).textContent();failed=true;await page.getByRole("button",{name:"Обновить каталог",exact:true}).click();await expect(page.getByText("Каталог EvoBGP не обновлён; показаны последние данные",{exact:true})).toBeVisible();await expect(catalogStamp(page)).toHaveText(before!);await page.locator('a[href="/domains"]').first().click();await expect(page.getByText("kept.example.test",{exact:true})).toBeVisible()})
|
||||
test("Evolution provider: отключение подтверждённой интеграции очищает каталог",async({page})=>{await setup(page);let enabled=true,posts=0;await page.route("**/api/evobgp/settings",r=>r.fulfill({json:{...settings,enabled}}));await page.route("**/api/evobgp/catalog",r=>{posts++;return r.fulfill({json:catalog})});await open(page);await ready(page);const before=posts;enabled=false;await page.getByRole("button",{name:"Обновить настройки EvoBGP",exact:true}).click();await expect(page.getByText("Интеграция: выключена",{exact:true})).toBeVisible();await expect(page.getByText("Каталог EvoBGP отключён в сохранённых настройках",{exact:true})).toBeVisible();await expect(catalogStamp(page)).toHaveCount(0);expect(posts).toBe(before)})
|
||||
test("Evolution provider: сохранение не ждёт каталог и не отменяется его отказом",async({page})=>{await setup(page);let failed=false,writes=0;await page.route("**/api/evobgp/catalog",r=>r.fulfill(failed?{status:503,json:{error:"catalog after save unavailable"}}:{json:catalog}));await page.route("**/api/evobgp/settings",r=>{if(r.request().method()!=="PUT")return r.fallback();writes++;failed=true;const patch=r.request().postDataJSON();return r.fulfill({json:{...settings,...patch,ok:true,savedFields:Object.keys(patch)}})});await open(page);await ready(page);await page.getByRole("button",{name:"Сохранить",exact:true}).click();await expect(page.getByText("Настройки EvoBGP сохранены",{exact:true})).toBeVisible();await expect(page.getByText("Каталог EvoBGP не обновлён; показаны последние данные",{exact:true})).toBeVisible();expect(writes).toBe(1)})
|
||||
test("Evolution provider: malformed PUT не подтверждает сохранение",async({page})=>{await setup(page);let writes=0;await page.route("**/api/evobgp/settings",r=>{if(r.request().method()!=="PUT")return r.fallback();writes++;return r.fulfill({json:{}})});await open(page);await ready(page);await base(page).fill("https://new.example.test");await page.getByRole("button",{name:"Сохранить",exact:true}).click();await expect(page.locator("#main-content").getByText("Сохранение EvoBGP не подтверждено сервером",{exact:true})).toBeVisible();await expect(base(page)).toHaveValue("https://new.example.test");expect(writes).toBe(1)})
|
||||
test("Evolution provider: права settings admin проверяются до GET",async({page})=>{const token="header."+Buffer.from(JSON.stringify({sub:"fixture",apps:["mm"],permissions:["mm:dashboard:read"],exp:Math.floor(Date.now()/1000)+3600})).toString("base64url")+".fixture";await page.addInitScript(value=>localStorage.setItem("mmapp_token",value),token);await setup(page);await page.route("**/api/auth/config",r=>r.fulfill({json:{required:true}}));let reads=0;page.on("request",r=>{if(new URL(r.url()).pathname.startsWith("/api/evobgp/"))reads++});await open(page);await expect(page.getByText("Нет доступа к настройкам EvoBGP",{exact:true})).toBeVisible();await expect(page.getByRole("button",{name:"Обновить настройки EvoBGP",exact:true})).toBeDisabled();expect(reads).toBe(0)})
|
||||
@@ -0,0 +1,2 @@
|
||||
export const evoSettings={baseUrl:"https://evo.example.test",enabled:true,secretConfigured:true}
|
||||
export const evoCatalog={fetchedAt:"2026-10-03T00:00:00Z",modules:[],domains:[{id:"fixture-domain",domain:"kept.example.test",resolvedIp:"192.0.2.1",asn:"AS64512",purpose:"fixture",filter:"fixture",updated:"сейчас",enabled:true}],ipRanges:[],asns:[],communities:[]}
|
||||
Reference in new issue
Block a user