import { test, expect } from "@playwright/test" const headers={"access-control-allow-origin":"*","access-control-allow-methods":"GET,POST,OPTIONS","access-control-allow-headers":"Content-Type,Authorization"} for(const change of ["backend","mode"])for(const fails of [false,true])test("Диагностика: поздний запрос/"+change+"/"+fails,async({page})=>{ let started=false,completed=false,release!:()=>void;const gate=new Promise(r=>release=r) await page.route("**/health",r=>r.fulfill({headers,json:{ok:true}})) await page.route("**/api/**",r=>{const u=new URL(r.request().url()),p=u.pathname;return r.fulfill({headers,json:p==="/api/auth/config"?{required:false}:p==="/api/servers"?[{id:1,name:u.port==="8101"?"Новый источник":"Прежний источник",host:"192.0.2.1",type:"exit-node",country:"RU",enabled:true,status:"online"}]:p.endsWith("/ros-src-address")?{host:"192.0.2.1",ipv4:"192.0.2.1"}:p.endsWith("/probes/run")?{output:"НОВЫЙ РЕЗУЛЬТАТ"}:{tunnels:[]}})}) await page.goto("/probes?server=1");await expect(page.getByRole("button",{name:"Запустить",exact:true})).toBeEnabled() await page.route("**/api/servers/1/probes/run",async r=>{if(new URL(r.request().url()).port!=="8000")return r.fallback();started=true;await gate;try{await r.fulfill(fails?{headers,status:503,json:{error:"СТАРАЯ ОШИБКА"}}:{headers,json:{output:"СТАРЫЙ РЕЗУЛЬТАТ"}})}catch{}finally{completed=true}}) await page.getByRole("button",{name:"Запустить",exact:true}).click();await expect.poll(()=>started).toBe(true);await page.getByRole("link",{name:/^Настройки/}).click() if(change==="backend"){const input=page.getByPlaceholder("http://localhost:8000");await input.fill("http://localhost:8101");await input.press("Enter")}else await page.getByRole("button",{name:"Моковые",exact:true}).click() await page.goto("/probes?server=1");if(change==="mode")await page.getByRole("option").first().click();await expect(page.getByRole("button",{name:"Запустить",exact:true})).toBeEnabled();release();await expect.poll(()=>completed).toBe(true) await page.getByRole("button",{name:"Запустить",exact:true}).click() if(change==="backend")await expect(page.getByText("НОВЫЙ РЕЗУЛЬТАТ",{exact:true})).toBeVisible() await expect(page.getByText("СТАРЫЙ РЕЗУЛЬТАТ",{exact:true})).toHaveCount(0);await expect(page.getByText("СТАРАЯ ОШИБКА",{exact:true})).toHaveCount(0) }) for(const change of ["backend","mode"])for(const fails of [false,true])test("Probe schedule ignores late old-source snapshot after ABA/"+change+"/"+fails,async({page})=>{ let started=false,completed=false,first=true,release!:()=>void;const gate=new Promise(r=>release=r) const row={id:"new-probe",srcServerId:"1",dstServerId:"2",srcInterface:"NEW_SCHEDULE",dstInterface:"",protocol:"tcp",direction:"both",durationSec:"10",enabled:true,lastRunAt:null,lastTxAvgMbps:0,lastRxAvgMbps:null,lastStatus:"done",lastError:null} await page.route("**/health",r=>r.fulfill({headers,json:{ok:true}}));await page.route("**/api/**",r=>{const p=new URL(r.request().url()).pathname;return r.fulfill({headers,json:p==="/api/auth/config"?{required:false}:p==="/api/servers"?[{id:1,name:"Fixture",host:"192.0.2.1",type:"exit-node",enabled:true,status:"online"}]:p==="/api/uptime/speed-probes"?{probes:[row]}:{tunnels:[]}})}) await page.route("**/api/uptime/speed-probes",async r=>{if(new URL(r.request().url()).port!=="8000"||!first)return r.fallback();first=false;started=true;await gate;try{await r.fulfill(fails?{headers,status:503,json:{error:"OLD_SCHEDULE_ERROR"}}:{headers,json:{probes:[{...row,id:"old-probe",srcInterface:"OLD_SCHEDULE"}]}})}catch{}finally{completed=true}}) await page.goto("/probes?server=1");await page.getByRole("button",{name:"Расписание",exact:true}).click();await expect.poll(()=>started).toBe(true) async function switchSource(back:boolean){await page.getByRole("link",{name:/^Настройки/}).click();if(change==="backend"){const input=page.getByPlaceholder("http://localhost:8000");await input.fill(back?"http://localhost:8000":"http://localhost:8101");await input.press("Enter")}else await page.getByRole("button",{name:back?"Живые":"Моковые",exact:true}).click();await page.goto("/probes?server=1");await page.getByRole("button",{name:"Расписание",exact:true}).click()} await switchSource(false);await switchSource(true);await expect(page.getByText(/NEW_SCHEDULE/)).toBeVisible();release();await expect.poll(()=>completed).toBe(true);await expect(page.getByText(/OLD_SCHEDULE/)).toHaveCount(0) })