fix(ui): сохранять достоверные снимки сертификатов
This commit is contained in:
1 parent
17f03e7af1
commit
b184922f61
7 files changed
+242
-93
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import { test, expect, type Page } from "@playwright/test"
|
||||
import { certificate, certificateState, server } from "./fixtures/certificates"
|
||||
|
||||
const refresh = (page: Page) => page.getByRole("button", { name: "Обновить", exact: true })
|
||||
test.beforeEach(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
|
||||
return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? [server] : path === "/api/certificates/state" ? certificateState() : { directoryUrl: "https://acme.example.test/directory", tokenConfigured: false, enabled: false, intervalSec: 300, daysBeforeExpiry: 30 } })
|
||||
})
|
||||
})
|
||||
for (const kind of ["API", "malformed", "duplicate", "foreign"] as const) test("Сертификаты: первоначальный отказ " + kind + " и восстановление", async ({ page }) => {
|
||||
let fail = true
|
||||
await page.route("**/api/certificates/state", r => r.fulfill(fail ? kind === "API" ? { status: 503, json: { error: "Отказ чтения" } } : { json: kind === "malformed" ? {} : certificateState(kind === "duplicate" ? [certificate, certificate] : [{ ...certificate, serverId: "2" }]) } : { json: certificateState() }))
|
||||
await page.goto("/certificates")
|
||||
await expect(page.getByRole("alert", { name: "Каталог сертификатов", exact: true })).toContainText("Сертификаты пока неизвестны")
|
||||
await expect(page.getByText("Нет сертификатов", { exact: true })).toHaveCount(0)
|
||||
fail = false; await refresh(page).click()
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
})
|
||||
test("Сертификаты: nullable поля и неизвестная классификация", async ({ page }) => {
|
||||
await page.goto("/certificates")
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
await expect(page.getByText("Срок неизвестен", { exact: true })).toBeVisible()
|
||||
await expect(page.getByText("Неизвестен", { exact: true })).toBeVisible()
|
||||
await page.getByText(certificate.name, { exact: true }).click()
|
||||
await expect(page.getByText("Неизвестно", { exact: true })).toBeVisible()
|
||||
await expect(page.getByText("0 бит", { exact: true })).toHaveCount(0)
|
||||
await expect(page.getByRole("status").filter({ hasText: "Сводка неполная" })).toBeVisible()
|
||||
await expect(page.getByRole("button", { name: "Импорт", exact: true })).toBeDisabled()
|
||||
})
|
||||
test("Сертификаты: ошибка сервера не скрывает строки", async ({ page }) => {
|
||||
await page.route("**/api/servers", r => r.fulfill({ status: 503, json: { error: "Серверы отказали" } }))
|
||||
await page.goto("/certificates")
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
await expect(page.getByRole("alert", { name: "Серверы сертификатов" })).toContainText("Каталог серверов пока неизвестен")
|
||||
await expect(page.getByRole("button", { name: "Выпустить сертификат", exact: true })).toBeDisabled()
|
||||
})
|
||||
test("Сертификаты: общий фоновый отказ сохраняет данные", async ({ page }) => {
|
||||
let fail = false
|
||||
await page.route("**/api/certificates/state", r => r.fulfill(fail ? { status: 503, json: { error: "Фоновый отказ" } } : { json: certificateState() }))
|
||||
await page.goto("/certificates"); await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
fail = true; await refresh(page).click()
|
||||
await expect(page.getByRole("alert", { name: "Каталог сертификатов", exact: true })).toContainText("Последние данные сохранены")
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
})
|
||||
test("Сертификаты: частичный отказ и подтверждённый пустой снимок", async ({ page }) => {
|
||||
let phase = 0
|
||||
await page.route("**/api/certificates/state", r => r.fulfill({ json: phase === 0 ? certificateState() : certificateState([], { error: phase === 1 ? "Устройство недоступно" : null }) }))
|
||||
await page.goto("/certificates"); await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
phase = 1; await refresh(page).click()
|
||||
await expect(page.getByRole("alert", { name: "Сертификаты сервера 1", exact: true })).toContainText("Последние сертификаты сохранены")
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
phase = 2; await refresh(page).click()
|
||||
await expect(page.getByText("Нет сертификатов", { exact: true })).toBeVisible()
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toHaveCount(0)
|
||||
})
|
||||
test("Сертификаты: медленные серверы не задерживают строки", async ({ page }) => {
|
||||
let release!: () => void; const gate = new Promise<void>(r => release = r)
|
||||
await page.route("**/api/servers", async r => { await gate; await r.fulfill({ json: [server] }).catch(() => {}) })
|
||||
await page.goto("/certificates"); await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
release(); await expect(refresh(page)).toBeEnabled()
|
||||
})
|
||||
test("Сертификаты: до ответа нет ложного пустого каталога", async ({ page }) => {
|
||||
let release!: () => void; const gate = new Promise<void>(r => release = r)
|
||||
await page.route("**/api/certificates/state", async r => { await gate; await r.fulfill({ json: certificateState() }).catch(() => {}) })
|
||||
await page.goto("/certificates"); await expect(page.getByRole("status").filter({ hasText: "Загрузка сертификатов…" })).toBeVisible()
|
||||
await expect(page.getByText("Нет сертификатов", { exact: true })).toHaveCount(0)
|
||||
release(); await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
})
|
||||
|
||||
for (const serverRead of [false, true]) test("Сертификаты: read-only права и server read " + serverRead, async ({ page }) => {
|
||||
await page.addInitScript(({ serverRead }) => localStorage.setItem("mmapp_token", "header." + btoa(JSON.stringify({ sub: "test", apps: ["mm"], permissions: ["mm:certificates:read", ...(serverRead ? ["mm:servers:read"] : [])], exp: Math.floor(Date.now() / 1000) + 3600 })) + ".signature"), { serverRead })
|
||||
await page.route("**/api/auth/config", r => r.fulfill({ json: { required: true } }))
|
||||
let serverRequests = 0
|
||||
await page.route("**/api/servers", r => { serverRequests++; return r.fulfill({ json: [server] }) })
|
||||
await page.goto("/certificates")
|
||||
await expect(page.getByText(certificate.name, { exact: true })).toBeVisible()
|
||||
await expect(page.getByRole("button", { name: "Выпустить сертификат", exact: true })).toBeDisabled()
|
||||
await expect(page.getByRole("button", { name: "Сохранить настройки", exact: true })).toHaveCount(0)
|
||||
if (!serverRead) {
|
||||
await expect(page.getByRole("status").filter({ hasText: "mm:servers:read" })).toBeVisible()
|
||||
expect(serverRequests).toBe(0)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
export { server } from "./users"
|
||||
export const certificate = { id: "1:*A", rosId: "*A", name: "certificate-live", serverId: "1", commonName: "test.example", sans: [], issuedBy: "—", validFrom: null, validUntil: null, rawValidFrom: null, rawValidUntil: null, daysLeft: null, keySize: null, trusted: null, status: "unknown", usage: [] }
|
||||
export function certificateState(certificates: unknown[] = [certificate], overrides: Record<string, unknown> = {}) {
|
||||
return { collectedAt: "2026-10-01T00:00:00Z", servers: [{ serverId: "1", serverName: "Роутер", collectedAt: "2026-10-01T00:00:00Z", certificates, error: null, ...overrides }] }
|
||||
}
|
||||
Reference in new issue
Block a user