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

170 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from "@playwright/test"
const summary = (id: string) => ({ serverId: id, serverName: "Роутер " + id, initialized: false, ike2Ready: false, natRuleManaged: false, clientsTotal: 1, clientsOnline: 0, peers: [] })
const client = (id: string, name = "Клиент " + id) => ({ id: id + "-*1", rosId: "*1", serverId: id, serverName: "Роутер " + id, name, authMethod: "certificate", online: false, disabled: false, managed: true })
const servers = [1, 2].map(id => ({ id, name: "Роутер " + id, host: "192.0.2." + id, enabled: true, country: "RU", type: "exit-node", status: "online" }))
const full = { servers: [summary("1"), summary("2")], clients: [client("1"), client("2")] }
for (const width of [1280, 320]) {
test(`IPsec: один сертификат сохраняет разные строки identity ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 1000 })
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/ipsec" ? {
servers: [{ ...summary("1"), clientsTotal: 2, clientsSessionAmbiguous: 2 }],
clients: [0,1].map(n => ({ ...client("1", "Привязка " + n), id: "1:identity:*" + n, rosId: "identity:*" + n, certName: "shared-certificate", staticIp: "198.18.114." + (250+n), sessionAmbiguous: true })),
} : path === "/api/servers" ? servers : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec?server=1")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(2)
for (const n of [0,1]) {
const row = rows.filter({ has: page.getByRole("button", { name: "Привязка " + n, exact: true }) })
await expect(row).toContainText("198.18.114." + (250+n))
await expect(row.getByText("Не определено", { exact: true })).toHaveCount(1)
}
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
})
test(`IPsec: несколько сессий одного клиента без произвольного адреса ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 1000 })
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/ipsec" ? {
servers: [{ ...summary("1"), clientsOnline: phase === 2 ? 0 : 1 }],
clients: [{ ...client("1"), online: phase !== 2, ...(phase === 1 ? { activeAddress: "198.18.113.250", activeSince: "1m" } : {}) }],
} : path === "/api/servers" ? servers : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec?server=1")
const rows = page.locator("tr[data-row-id]")
await expect(rows.getByText("онлайн", { exact: true })).toHaveCount(1)
await expect(rows.getByText("офлайн", { exact: true })).toHaveCount(0)
await expect(rows.getByText("Не определено", { exact: true })).toHaveCount(0)
await expect(page.getByLabel("Сводка IPsec")).not.toContainText("—")
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
phase = 1
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(rows.getByText("198.18.113.250", { exact: true })).toHaveCount(1)
phase = 2
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(rows.getByText("офлайн", { exact: true })).toHaveCount(1)
await expect(rows.getByText("198.18.113.250", { exact: true })).toHaveCount(0)
})
test(`IPsec: неоднозначная сессия и восстановление статуса ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 1000 })
let phase = 0
const rows = [client("1", "Сертификат"), { ...client("1", "PSK"), id: "1-*2", rosId: "*2", kind: "psk", authMethod: "pre-shared-key" }]
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const path = new URL(r.request().url()).pathname
const values = phase === 0 ? rows.map(c => ({ ...c, sessionAmbiguous: true })) : [
{ ...rows[0], online: true, activeAddress: "198.18.112.250", ...(phase === 2 ? { sessionAmbiguous: true } : {}) }, rows[1],
]
return r.fulfill({ json: path === "/api/ipsec" ? { servers: [{ ...summary("1"), clientsTotal: 2, clientsOnline: phase === 0 ? 0 : 1, ...(phase === 0 ? { clientsSessionAmbiguous: 2 } : {}) }], clients: values }
: path === "/api/servers" ? servers : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec?server=1")
const gridRows = page.locator("tr[data-row-id]")
const ambiguity = page.getByRole("alert").filter({ hasText: "Статус части клиентов не определён" })
await expect(ambiguity).toContainText("У 2 клиентов")
await expect(gridRows).toHaveCount(2)
await expect(gridRows.getByText("Не определено", { exact: true })).toHaveCount(2)
await expect(gridRows.getByText("офлайн", { exact: true })).toHaveCount(0)
await expect(page.getByLabel("Сводка IPsec")).toContainText("—")
await page.getByRole("tab", { name: "Сервер", exact: true }).click()
await expect(page.getByText("0 подтверждено онлайн / 2; статус 2 не определён", { exact: true })).toBeVisible()
await page.getByRole("tab", { name: "Клиенты", exact: true }).click()
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
phase = 1
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(ambiguity).toHaveCount(0)
await expect(gridRows.getByText("офлайн", { exact: true })).toHaveCount(1)
await expect(gridRows.getByText("198.18.112.250", { exact: true })).toHaveCount(1)
phase = 2
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(page.getByRole("alert").filter({ hasText: "Не удалось загрузить IPsec" })).toBeVisible()
await expect(gridRows.getByText("198.18.112.250", { exact: true })).toHaveCount(1)
})
}
for (const broken of ["http", "payload", "duplicate", "conflict", "peer-context", "pool-context", "blank-failure"]) {
test(`IPsec: неизвестное начало и сохранение данных при ${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/ipsec") {
if (phase === 0) return r.fulfill({ status: 503, json: { error: "Начальный отказ IPsec" } })
if (phase === 1 || phase === 3) return r.fulfill({ json: full })
return broken === "http" ? r.fulfill({ status: 503, json: { error: "Повторный отказ IPsec" } })
: r.fulfill({ json: broken === "payload" ? { servers: [], clients: [{}] }
: broken === "duplicate" ? { ...full, clients: [client("1"), client("1")] }
: broken === "peer-context" ? { ...full, servers: [{ ...summary("1"), peers: [{ id: "1-*p1", rosId: "*p1", serverId: "2", serverName: "Чужой роутер", name: "peer", managed: true, disabled: false }] }, summary("2")] }
: broken === "pool-context" ? { ...full, servers: [{ ...summary("1"), pool: { id: "1-*p1", rosId: "*p1", serverId: "2", name: "pool", ranges: "10.0.0.1-10.0.0.20", managed: true } }, summary("2")] }
: broken === "blank-failure" ? { servers: [], clients: [], failures: [{ serverId: "", error: "Неизвестный отказ" }] }
: { ...full, failures: [{ serverId: "1", error: "Сбой устройства" }] } })
}
return r.fulfill({ json: path === "/api/servers" ? servers : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec")
const alert = page.getByRole("alert").filter({ hasText: "Не удалось загрузить IPsec" })
await expect(alert).toContainText("Данные пока неизвестны")
await expect(page.getByText("Нет клиентов IKEv2", { exact: true })).toHaveCount(0)
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
await expect(page.getByLabel("Сводка IPsec")).toContainText("неизвестно")
phase = 1
await alert.getByRole("button", { name: "Повторить загрузку IPsec" }).click()
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
phase = 2
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(alert).toContainText("Показаны последние полученные данные")
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
await expect(page.getByLabel("Сводка IPsec")).toContainText("неизвестно")
phase = 3
await alert.getByRole("button", { name: "Повторить загрузку IPsec" }).click()
await expect(alert).toHaveCount(0)
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
})
}
test("IPsec: частичный отказ сохраняет строки роутера, восстановление удаляет устаревшие", 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/ipsec" ? phase === 0 ? full : phase === 1 ? {
servers: [summary("1")], clients: [client("1", "Обновлённый клиент")], failures: [{ serverId: "2", serverName: "Роутер 2", error: "Таймаут тест" }],
} : { servers: [summary("1"), { ...summary("2"), clientsTotal: 0 }], clients: [client("1", "Обновлённый клиент")] }
: path === "/api/servers" ? servers : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(2)
phase = 1
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(page.getByRole("alert").filter({ hasText: "Не удалось опросить часть роутеров" })).toContainText("Роутер 2: Таймаут тест")
await expect(rows).toHaveCount(2)
await expect(rows.filter({ hasText: "Обновлённый клиент" })).toHaveCount(1)
await expect(rows.filter({ hasText: "Клиент 2" })).toHaveCount(1)
await expect(page.getByLabel("Сводка IPsec")).toContainText("неизвестно")
phase = 2
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(page.getByRole("alert").filter({ hasText: "Не удалось опросить часть роутеров" })).toHaveCount(0)
await expect(rows).toHaveCount(1)
await expect(rows.filter({ hasText: "Клиент 2" })).toHaveCount(0)
})
test("IPsec: ошибка списка устройств не стирает успешный опрос", 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
if (path === "/api/servers") return r.fulfill({ status: 503, json: { error: "Устройства недоступны тест" } })
return r.fulfill({ json: path === "/api/ipsec" ? full : path === "/api/auth/config" ? { required: false } : {} })
})
await page.goto("/ipsec")
await expect(page.getByRole("alert").filter({ hasText: "Не удалось загрузить устройства" })).toBeVisible()
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
})