fix(netflow): проверить смену источника и адаптивность формы
This commit is contained in:
1 parent
fcd8de2a96
commit
a575795ff5
13 files changed
+83
-2
No files matched your search
@@ -260,3 +260,5 @@ REST запрос → ожидание → подтверждённый output /
|
|||||||
Завершён 11Б.3.4.4.4: 6 таблиц/counters purge в общей транзакции, отдельные partition/listener/event warnings и nullable размер файлов. DI/HTTP/API/typecheck/build/lint прошли; реальный PG purge не запускался. Следующий шаг 11Б.3.4.4.5 — форма NetFlow и подтверждённые/частичные/неизвестные операции.
|
Завершён 11Б.3.4.4.4: 6 таблиц/counters purge в общей транзакции, отдельные partition/listener/event warnings и nullable размер файлов. DI/HTTP/API/typecheck/build/lint прошли; реальный PG purge не запускался. Следующий шаг 11Б.3.4.4.5 — форма NetFlow и подтверждённые/частичные/неизвестные операции.
|
||||||
|
|
||||||
Завершён 11Б.3.4.4.5: форма NetFlow known/dirty/rights/lock, selective PUT, partial outcomes и review после unknown POST; 12 E2E/form unit/typecheck/lint/production build прошли. Далее 11Б.3.4.4.6 — source/adaptive приёмка, затем общий контракт IP/CIDR/DNS.
|
Завершён 11Б.3.4.4.5: форма NetFlow known/dirty/rights/lock, selective PUT, partial outcomes и review после unknown POST; 12 E2E/form unit/typecheck/lint/production build прошли. Далее 11Б.3.4.4.6 — source/adaptive приёмка, затем общий контракт IP/CIDR/DNS.
|
||||||
|
|
||||||
|
Завершён 11Б.3.4.4.6: 10 NetFlow backend source E2E, 3 responsive, 6 снимков (320 и ПК просмотрены), регрессия GeoIP/collector прошли. Далее 11Б.3.4.4.7: общий IP/CIDR/DNS ввод/фактический порт и отказ конкурентному purge вместо очереди. Затем приёмка NetFlow и Evolution.
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
||||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.3.4.4.5; status/history/KPI, strict settings API основа и защита каталога Evolution проверены; далее readers/dirty формы |
|
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | В работе: 11Б.1–11Б.3.4.4.6; scheduler и GeoIP приняты локально; NetFlow API/backend/форма/source/adaptive проверены, далее общий сетевой ввод и Evolution |
|
||||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +36,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Блок 11Б.3.4.4.6 — контекст и адаптивность NetFlow (2026-10-03)
|
||||||
|
|
||||||
|
- 10 source E2E проверили поздние GET/PUT/keys/purge/export, success/error старого backend не меняют настройки/ошибки/receipt/Sheet нового backend. Конфигурация :3101/local отдельна от основного :3100/same-origin.
|
||||||
|
- 3 responsive tests на 1440/390/320 прошли; сохранены 6 снимков верхней части и действий docs/ux-ui/screens/netflow*.png. Просмотрены 320 верх/действия и 1440 верх: подписи читаемы, кнопки переносятся, destructive секция на телефоне вертикальная, body не переполняется. Допуск 2px относится только к границе Frame.
|
||||||
|
- Повторная группа 16 E2E (3 responsive + 10 GeoIP + 3 collector/probes) прошла. Source lint прошёл; production/typecheck приняты в 11Б.3.4.4.5. Никаких реальных private keys, PG purge, UDP/Portal/RouterOS в снимках и fixture checks нет.
|
||||||
|
- Далее 11Б.3.4.4.7: общий IP/CIDR/DNS ввод backend/frontend и инструкции с фактическим портом; также выявлено, что текущий advisory lock purge ставит второе удаление в очередь вместо 409 — требуется отказ конкурентному purge. После этих блоков локальная приёмка NetFlow, затем Evolution.
|
||||||
|
|
||||||
## Блок 11Б.3.4.4.5 — достоверная форма NetFlow (2026-10-03)
|
## Блок 11Б.3.4.4.5 — достоверная форма NetFlow (2026-10-03)
|
||||||
|
|
||||||
- Форма использует известные DTO, пустые unknown значения, время получения, последние данные и раздельные read/operation outcomes. Dirty хранится по полям, GET обновляет untouched и не стирает правки, PUT отправляет только изменённое без parseInt/default/clamp. Нулевой порог и false сохранены; включение порога требует явного допустимого значения.
|
- Форма использует известные DTO, пустые unknown значения, время получения, последние данные и раздельные read/operation outcomes. Dirty хранится по полям, GET обновляет untouched и не стирает правки, PUT отправляет только изменённое без parseInt/default/clamp. Нулевой порог и false сохранены; включение порога требует явного допустимого значения.
|
||||||
|
|||||||
@@ -188,3 +188,5 @@ ReUI PRO/base-nova; существующие Frame, Data Grid v9, KpiStatGrid, S
|
|||||||
Завершён 11Б.3.4.4.4: 6 таблиц/counters purge в общей транзакции, отдельные partition/listener/event warnings и nullable размер файлов. DI/HTTP/API/typecheck/build/lint прошли; реальный PG purge не запускался. Следующий шаг 11Б.3.4.4.5 — форма NetFlow и подтверждённые/частичные/неизвестные операции.
|
Завершён 11Б.3.4.4.4: 6 таблиц/counters purge в общей транзакции, отдельные partition/listener/event warnings и nullable размер файлов. DI/HTTP/API/typecheck/build/lint прошли; реальный PG purge не запускался. Следующий шаг 11Б.3.4.4.5 — форма NetFlow и подтверждённые/частичные/неизвестные операции.
|
||||||
|
|
||||||
Завершён 11Б.3.4.4.5: форма NetFlow known/dirty/rights/lock, selective PUT, partial outcomes и review после unknown POST; 12 E2E/form unit/typecheck/lint/production build прошли. Далее 11Б.3.4.4.6 — source/adaptive приёмка, затем общий контракт IP/CIDR/DNS.
|
Завершён 11Б.3.4.4.5: форма NetFlow known/dirty/rights/lock, selective PUT, partial outcomes и review после unknown POST; 12 E2E/form unit/typecheck/lint/production build прошли. Далее 11Б.3.4.4.6 — source/adaptive приёмка, затем общий контракт IP/CIDR/DNS.
|
||||||
|
|
||||||
|
Завершён 11Б.3.4.4.6: 10 NetFlow backend source E2E, 3 responsive, 6 снимков (320 и ПК просмотрены), регрессия GeoIP/collector прошли. Далее 11Б.3.4.4.7: общий IP/CIDR/DNS ввод/фактический порт и отказ конкурентному purge вместо очереди. Затем приёмка NetFlow и Evolution.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -2,7 +2,7 @@ import { defineConfig } from "@playwright/test"
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: "./tests/e2e",
|
testDir: "./tests/e2e",
|
||||||
testIgnore: ["**/geoip-context.spec.ts", "**/data-collection-context.spec.ts", "**/backups-context.spec.ts", "**/certificates-context.spec.ts", "**/containers-context.spec.ts", "**/users-context.spec.ts", "**/probes-context.spec.ts", "**/terminal-context.spec.ts", "**/network-map-context.spec.ts", "**/route-optimizer-context.spec.ts", "**/recursive-routes-context.spec.ts", "**/ospf-context.spec.ts", "**/bgp-context.spec.ts", "**/firewall-context.spec.ts", "**/filters-context.spec.ts", "**/ipsec-context.spec.ts", "**/gre-context.spec.ts", "**/vxlan-context.spec.ts"],
|
testIgnore: ["**/netflow-context.spec.ts", "**/geoip-context.spec.ts", "**/data-collection-context.spec.ts", "**/backups-context.spec.ts", "**/certificates-context.spec.ts", "**/containers-context.spec.ts", "**/users-context.spec.ts", "**/probes-context.spec.ts", "**/terminal-context.spec.ts", "**/network-map-context.spec.ts", "**/route-optimizer-context.spec.ts", "**/recursive-routes-context.spec.ts", "**/ospf-context.spec.ts", "**/bgp-context.spec.ts", "**/firewall-context.spec.ts", "**/filters-context.spec.ts", "**/ipsec-context.spec.ts", "**/gre-context.spec.ts", "**/vxlan-context.spec.ts"],
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
retries: 0,
|
retries: 0,
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { defineConfig } from "@playwright/test"
|
||||||
|
import base from "./playwright.config"
|
||||||
|
const server = base.webServer
|
||||||
|
if (!server || Array.isArray(server)) throw new Error("Ожидается один локальный сервер")
|
||||||
|
export default defineConfig({ ...base, use: { ...base.use, baseURL: "http://127.0.0.1:3101" }, testIgnore: [], testMatch: "netflow-context.spec.ts", webServer: { ...server, command: "node node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3101", url: "http://127.0.0.1:3101", env: { ...server.env, NEXT_PUBLIC_BACKEND_URL: "local" } } })
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import {test,expect,type Page} from "@playwright/test"
|
||||||
|
import {keyedNetflowSettings as settings} from "./fixtures/netflow-settings"
|
||||||
|
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());return r.fulfill(url.pathname==="/api/auth/config"?{headers,json:{required:false}}:url.pathname==="/api/traffic/flow/settings"?{headers,json:{...settings,flowListenPort:url.port==="8101"?4801:4739}}:{headers,status:503,json:{error:"outside NetFlow fixture"}})})
|
||||||
|
}
|
||||||
|
for(const action of ["read","put","keys","purge","export"] as const)for(const fails of [false,true])test("NetFlow: поздний ответ старого backend/"+action+"/"+fails,async({page})=>{
|
||||||
|
await setup(page);let started=false,completed=false,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
|
||||||
|
const path=action==="keys"?"/api/traffic/flow/settings/generate-keys":action==="purge"?"/api/traffic/flow/purge":action==="export"?"/api/traffic/flow/host-files":"/api/traffic/flow/settings"
|
||||||
|
await page.route("**"+path,async r=>{
|
||||||
|
if(new URL(r.request().url()).port!=="8000"||(action==="put"?r.request().method()!=="PUT":action==="read"?r.request().method()!=="GET":false))return r.fallback()
|
||||||
|
started=true;await gate
|
||||||
|
const old={...settings,flowListenPort:4740}
|
||||||
|
const json=action==="read"?old:action==="put"?{ok:true,settings:old}:action==="keys"?{ok:true,created:true,publicKey:old.hostPublicKey,settings:old}:action==="export"?{files:[{id:"wg",label:"WG",filename:"wg.conf",code:"fixture-old"}]}:{ok:true,deleted:{buckets:21,minuteStats:0,minuteDims:0,dailyDims:0},fileBytesBefore:null,fileBytesAfter:null,vacuumed:false}
|
||||||
|
try{await r.fulfill(fails?{headers,status:503,json:{error:"СТАРАЯ ОШИБКА"}}:{headers,json})}catch{}finally{completed=true}
|
||||||
|
})
|
||||||
|
try{
|
||||||
|
await page.goto("/data-collection");const field=page.getByRole("textbox",{name:"UDP IPFIX",exact:true})
|
||||||
|
if(action!=="read")await expect(field).toHaveValue("4739")
|
||||||
|
if(action==="put"){await field.fill("4740");await page.getByRole("button",{name:"Сохранить NetFlow",exact:true}).click()}
|
||||||
|
if(action==="keys")await page.getByRole("button",{name:"Ключи хоста",exact:true}).click()
|
||||||
|
if(action==="export")await page.getByRole("button",{name:"Скачать файлы хоста NetFlow",exact:true}).click()
|
||||||
|
if(action==="purge"){await page.getByRole("button",{name:"Сбросить",exact:true}).click();await page.getByRole("alertdialog").getByRole("button",{name:"Сбросить",exact:true}).click()}
|
||||||
|
await expect.poll(()=>started).toBe(true);await page.goto("/settings");const url=page.getByPlaceholder("http://localhost:8000");await url.fill("http://localhost:8101");await url.press("Enter");await page.goto("/data-collection");await expect(field).toHaveValue("4801");release();await expect.poll(()=>completed).toBe(true)
|
||||||
|
await expect(field).toHaveValue("4801");await expect(page.getByText(/СТАРАЯ ОШИБКА/)).toHaveCount(0);await expect(page.getByRole("status").filter({hasText:/подтверждено|подтверждён/})).toHaveCount(0);await expect(page.getByRole("dialog",{name:"Файлы для хоста Docker MM",exact:true})).toHaveCount(0);await expect(page.getByRole("button",{name:"Ключи хоста",exact:true})).toBeEnabled()
|
||||||
|
}finally{release()}
|
||||||
|
})
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { test, expect, type Page } from "@playwright/test"
|
||||||
|
import { geoip } from "./fixtures/geoip"
|
||||||
|
import { keyedNetflowSettings } from "./fixtures/netflow-settings"
|
||||||
|
const common = { enabled: true, intervalSec: 300, retentionDays: 14, lastCollectedAt: null, lastDurationMs: 0, lastError: null }
|
||||||
|
const values: Record<string, object> = {
|
||||||
|
"/api/traffic/settings": { ...common, intervalSec: 45 },
|
||||||
|
"/api/servers-api-ping/settings": { ...common, enabled: false, intervalSec: 120 },
|
||||||
|
"/api/uptime/settings": { ...common, resourcesEnabled: true, pingEnabled: false, speedEnabled: false, probeIntervalSec: 15, speedIntervalSec: 60 },
|
||||||
|
"/api/internet-path/settings": common,
|
||||||
|
"/api/certificates/renew-settings": { enabled: false, intervalSec: 21600, renewBeforeDays: 30 },
|
||||||
|
}
|
||||||
|
async function setup(page: Page) {
|
||||||
|
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
|
||||||
|
await page.route("**/api/**", r => {
|
||||||
|
const path = new URL(r.request().url()).pathname
|
||||||
|
if (path === "/api/auth/config") return r.fulfill({ json: { required: false } })
|
||||||
|
if (path === "/api/geoip") return r.fulfill({ json: geoip })
|
||||||
|
if (path === "/api/scheduler/status") return r.fulfill({ json: { jobs: [] } })
|
||||||
|
if (path === "/api/scheduler/runs") return r.fulfill({ json: { runs: [] } })
|
||||||
|
if (path === "/api/traffic/flow/settings") return r.fulfill({ json: {...keyedNetflowSettings,publicEndpoint:"collector.example.test"} })
|
||||||
|
if (values[path]) return r.fulfill({ json: values[path] })
|
||||||
|
return r.fulfill({ status: 503, json: { error: "outside settings fixture" } })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (const width of [1440, 390, 320]) test("NetFlow: адаптивность/" + width, async ({ page }) => {
|
||||||
|
await page.setViewportSize({ width, height: 1000 }); await setup(page); await page.goto("/data-collection")
|
||||||
|
const section = page.getByText("Traffic Flow / NetFlow (IPFIX)", { exact: true }).locator("xpath=ancestor::*[@data-slot='frame'][1]")
|
||||||
|
await expect(page.getByRole("textbox", { name: "UDP IPFIX", exact: true })).toHaveValue("4739")
|
||||||
|
await page.getByText("Traffic Flow / NetFlow (IPFIX)", {exact:true}).scrollIntoViewIfNeeded()
|
||||||
|
expect(await section.evaluate(element => element.scrollWidth <= element.clientWidth + 2)).toBe(true)
|
||||||
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true)
|
||||||
|
await page.screenshot({ path: "docs/ux-ui/screens/netflow-" + width + ".png", fullPage: true })
|
||||||
|
await page.getByRole("button",{name:"Сохранить NetFlow",exact:true}).scrollIntoViewIfNeeded()
|
||||||
|
await page.screenshot({path:"docs/ux-ui/screens/netflow-actions-"+width+".png",fullPage:true})
|
||||||
|
})
|
||||||
Reference in new issue
Block a user