Files
MikrotikManager/tests/e2e/bgp-data.spec.ts
T

120 lines
8.7 KiB
TypeScript

import { test, expect } from "@playwright/test"
const server = (id: number) => ({ id, name: "Роутер " + id, host: "192.0.2." + id, enabled: true, country: "RU", status: null })
const session = (serverId: number, name = "Сессия " + serverId) => ({ id: "*1", serverId, serverName: "Роутер " + serverId, serverSite: "", serverCountry: "RU", name, peerIp: "192.0.2." + serverId, remoteAs: 64513, localAs: 64512, localId: "192.0.2.100", remoteId: "192.0.2." + serverId, state: "Established", type: "eBGP", uptime: "1h", holdTime: 90, keepalive: 30, prefixesRx: 12, prefixesTx: 2, inputMessages: 20, outputMessages: 10, capabilities: [], lastError: null })
const full = { sessions: [session(1), session(2)], failures: [] }
for (const broken of ["http", "payload", "duplicate", "conflict", "duplicate-failure", "foreign", "inventory"]) {
test('BGP: неизвестное начало, кэш и восстановление ' + broken, async ({ page }) => {
let phase = 0
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/servers") return r.fulfill({ json: phase === 2 && broken === "inventory" ? [server(1), server(1)] : [server(1), server(2)] })
if (path === "/api/bgp/catalog") {
if (phase === 0 || phase === 2 && broken === "http") return r.fulfill({ status: 503, json: { error: "BGP недоступен тест" } })
const failure = { serverId: 2, serverName: "Роутер 2", error: "Таймаут тест" }
const json = phase !== 2 ? full : broken === "payload" ? { sessions: [{}], failures: [] }
: broken === "duplicate" ? { sessions: [session(1), session(1)], failures: [] }
: broken === "conflict" ? { ...full, failures: [failure] }
: broken === "duplicate-failure" ? { sessions: [], failures: [failure, failure] }
: broken === "foreign" ? { sessions: [session(3)], failures: [] } : full
return r.fulfill({ json })
}
return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/bgp")
const alert = page.getByRole("alert").filter({ hasText: "Не удалось загрузить BGP" })
await expect(alert).toContainText("Данные пока неизвестны")
await expect(page.getByText("Нет BGP-сессий", { exact: true })).toHaveCount(0)
await expect(page.getByLabel("Сводка BGP", { exact: true })).toContainText("неизвестно")
phase = 1
await alert.getByRole("button", { name: "Повторить загрузку BGP" }).click()
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(2)
await expect(page.getByRole("option").filter({ hasText: "Роутер 1" })).toContainText("Статус неизвестен")
phase = 2
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(alert).toContainText("Показаны последние полученные данные")
await expect(rows).toHaveCount(2)
await expect(rows.filter({ hasText: "Неизвестно" })).toHaveCount(2)
await page.getByRole("button", { name: "Аналитика", exact: true }).click()
await expect(page.getByRole("status").filter({ hasText: "Аналитика неизвестна" })).toBeVisible()
phase = 3
await alert.getByRole("button", { name: "Повторить загрузку BGP" }).click()
await expect(alert).toHaveCount(0)
await page.getByRole("button", { name: "Сессии", exact: true }).click()
await expect(rows.filter({ hasText: "Неизвестно" })).toHaveCount(0)
})
}
test("BGP: частичный отказ сохраняет устройство, пустой успешный опрос очищает", async ({ page }) => {
let phase = 0
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const path = new URL(r.request().url()).pathname
return r.fulfill({ json: path === "/api/servers" ? [server(1), server(2)] : path === "/api/bgp/catalog" ? phase === 0 ? full : { sessions: [session(1, "Обновлённая сессия")], failures: phase === 1 ? [{ serverId: 2, serverName: "Роутер 2", error: "Таймаут тест" }] : [] } : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/bgp")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(2)
phase = 1
await page.getByRole("button", { name: "Обновить", exact: true }).click()
const alert = page.getByRole("alert").filter({ hasText: "Не удалось опросить часть роутеров BGP" })
await expect(alert).toContainText("Роутер 2: Таймаут тест")
await expect(rows.filter({ hasText: "Сессия 2" })).toContainText("Неизвестно")
await expect(rows.filter({ hasText: "Обновлённая сессия" })).toContainText("Established")
await page.getByRole("option").filter({ hasText: "Роутер 1" }).click()
await expect(page.getByLabel("Сводка BGP", { exact: true })).not.toContainText("неизвестно")
await page.getByRole("option").filter({ hasText: "Роутер 2" }).click()
await expect(page.getByLabel("Сводка BGP", { exact: true })).toContainText("неизвестно")
phase = 2
await alert.getByRole("button", { name: "Повторить загрузку BGP" }).click()
await expect(rows).toHaveCount(0)
await expect(page.getByText("Нет BGP-сессий", { exact: true })).toBeVisible()
})
test("BGP: неизвестные поля и наблюдаемый ноль не подменяются", async ({ page }) => {
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const path = new URL(r.request().url()).pathname
const unknown = { ...session(1), state: "Неизвестное состояние RouterOS", remoteAs: null, localAs: null, type: "Unknown", holdTime: null, keepalive: null, prefixesRx: null, prefixesTx: null, inputMessages: null, outputMessages: null, afi: "ip,ipv6" }
return r.fulfill({ json: path === "/api/servers" ? [server(1), server(2)] : path === "/api/bgp/catalog" ? { sessions: [unknown, { ...session(2), prefixesRx: 0, prefixesTx: 0 }], failures: [] } : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/bgp")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(2)
await expect(rows.first()).toContainText("Неизвестно")
await expect(rows.first()).toContainText("AS неизвестна")
await expect(rows.first()).toContainText("Тип неизвестен")
await expect(rows.last().locator("td").nth(7)).toHaveText("0")
await rows.first().click()
await expect(page.getByText("ip,ipv6", { exact: true })).toBeVisible()
await expect(page.getByText("Нет данных API", { exact: true })).toBeVisible()
await expect(page.getByText("RouterOS Export", { exact: true })).toHaveCount(0)
await page.getByRole("button", { name: "Аналитика", exact: true }).click()
await expect(page.getByLabel("Сводка префиксов BGP")).toContainText("нет данных API")
await expect(page.getByText("Недостаточно данных о префиксах для сравнения", { exact: true })).toBeVisible()
})
for (const field of ["localAs", "remoteAs"]) for (const invalid of [0, 4294967296]) {
test("BGP: невалидная AS сохраняет снимок " + field + "=" + invalid, async ({ page }) => {
let broken = false
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const path = new URL(r.request().url()).pathname
return r.fulfill({ json: path === "/api/servers" ? [server(1)] : path === "/api/bgp/catalog" ? { sessions: [{ ...session(1), ...(broken ? { [field]: invalid } : {}) }], failures: [] } : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/bgp")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(1)
broken = true
await page.getByRole("button", { name: "Обновить", exact: true }).click()
const alert = page.getByRole("alert").filter({ hasText: "Не удалось загрузить BGP" })
await expect(alert).toContainText("Показаны последние полученные данные")
await expect(rows).toHaveCount(1)
await expect(rows).toContainText("Неизвестно")
broken = false
await alert.getByRole("button", { name: "Повторить загрузку BGP" }).click()
await expect(alert).toHaveCount(0)
await expect(rows).toContainText("Established")
})
}