Files
MikrotikManager/tests/e2e/certificates-acme.spec.ts
T

62 lines
6.9 KiB
TypeScript

import { test, expect, type Page } from "@playwright/test"
import { certificateState, server } from "./fixtures/certificates"
const acme = { directoryUrl: "https://acme.example.test/directory", defaultZoneId: "old-zone", tokenConfigured: true }
const directory = (page: Page) => page.getByRole("textbox", { name: "ACME directory URL", exact: true })
const token = (page: Page) => page.getByLabel("Cloudflare API token", { exact: true })
const save = (page: Page) => page.getByRole("button", { name: "Сохранить настройки", exact: true })
const check = (page: Page) => page.getByRole("button", { name: "Проверить Cloudflare", 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() : path === "/api/certificates/renew-settings" ? { enabled: false, intervalSec: 300, renewBeforeDays: 30 } : acme })
})
})
for (const malformed of [false, true]) test("ACME: неизвестные настройки после отказа " + malformed, async ({ page }) => {
let fail = true; await page.route("**/api/certificates/acme-settings", r => r.fulfill(fail ? malformed ? { json: {} } : { status: 503, json: { error: "ACME чтение отказало" } } : { json: acme }))
await page.goto("/certificates"); await expect(page.getByRole("alert", { name: "Настройки ACME", exact: true })).toBeVisible()
await expect(save(page)).toBeDisabled(); await expect(check(page)).toBeDisabled(); await expect(directory(page)).toHaveValue("")
await expect(page.getByText("Состояние токена неизвестно.", { exact: true })).toBeVisible()
fail = false; await page.getByRole("button", { name: "Перечитать настройки ACME", exact: true }).click(); await expect(directory(page)).toHaveValue(acme.directoryUrl)
})
for (const kind of ["API", "malformed", "token-unconfirmed"]) test("ACME: отказ сохранения " + kind + " оставляет черновик и токен", async ({ page }) => {
await page.route("**/api/certificates/acme-settings", r => r.request().method() === "GET" ? r.fulfill({ json: acme }) : r.fulfill(kind === "API" ? { status: 503, json: { error: "Сохранение ACME отказало" } } : { json: kind === "malformed" ? {} : { ...acme, tokenConfigured: false } }))
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl)
await directory(page).fill("https://new.example.test/directory"); await token(page).fill("test-draft-token"); await save(page).click()
await expect(page.getByRole("alert", { name: "Настройки ACME", exact: true })).toContainText("Результат сохранения не подтверждён")
await expect(token(page)).toHaveValue("test-draft-token"); await expect(directory(page)).toHaveValue("https://new.example.test/directory")
await expect(page.getByText("Настройки ACME сохранены", { exact: true })).toHaveCount(0)
})
test("ACME: подтверждённое сохранение очищает только принятый токен", async ({ page }) => {
let body: Record<string, unknown> = {}
await page.route("**/api/certificates/acme-settings", r => { if (r.request().method() === "GET") return r.fulfill({ json: acme }); body = r.request().postDataJSON(); return r.fulfill({ json: { ...acme, directoryUrl: body.directoryUrl } }) })
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl); await token(page).fill("test-draft-token"); await save(page).click()
await expect(page.getByText("Настройки ACME сохранены", { exact: true })).toBeVisible(); await expect(token(page)).toHaveValue("")
expect(body.cloudflareApiToken).toBe("test-draft-token")
})
test("ACME: Cloudflare отказ не меняет настройки или токен", async ({ page }) => {
await page.route("**/api/certificates/acme-settings/test", r => r.fulfill({ status: 400, json: { ok: false, message: "Cloudflare denied" } }))
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl); await token(page).fill("test-draft-token"); await check(page).click()
await expect(page.getByRole("alert", { name: "Настройки ACME", exact: true })).toContainText("Cloudflare denied")
await expect(token(page)).toHaveValue("test-draft-token"); await expect(page.getByText("Настройки ACME сохранены", { exact: true })).toHaveCount(0)
})
test("ACME: save и test разделяют single flight", async ({ page }) => {
let writes = 0, tests = 0; let release!: () => void; const gate = new Promise<void>(r => release = r)
await page.route("**/api/certificates/acme-settings", r => { if (r.request().method() === "PUT") writes++; return r.fulfill({ json: acme }) })
await page.route("**/api/certificates/acme-settings/test", async r => { tests++; await gate; await r.fulfill({ json: { ok: true, message: "Cloudflare доступен" } }) })
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl); await check(page).click()
await expect(save(page)).toBeDisabled(); await expect(check(page)).toBeDisabled(); await expect(token(page)).toBeDisabled(); expect(writes).toBe(0); expect(tests).toBe(1)
release(); await expect(page.getByRole("status").filter({ hasText: "Cloudflare доступен" })).toBeVisible()
})
test("ACME: background read не стирает черновик", async ({ page }) => {
let second = false; await page.route("**/api/certificates/acme-settings", r => r.fulfill({ json: { ...acme, directoryUrl: second ? "https://server-new.example.test/directory" : acme.directoryUrl } }))
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl); await directory(page).fill("https://draft.example.test/directory"); await token(page).fill("test-draft-token")
second = true; await page.getByRole("button", { name: "Перечитать настройки ACME", exact: true }).click(); await expect(directory(page)).toBeEnabled()
await expect(directory(page)).toHaveValue("https://draft.example.test/directory"); await expect(token(page)).toHaveValue("test-draft-token")
})
test("ACME: неверный URL не отправляет mutation", async ({ page }) => {
let writes = 0; await page.route("**/api/certificates/acme-settings", r => { if (r.request().method() === "PUT") writes++; return r.fulfill({ json: acme }) })
await page.goto("/certificates"); await expect(directory(page)).toHaveValue(acme.directoryUrl); await directory(page).fill("bad"); await save(page).click()
await expect(page.getByRole("alert", { name: "Настройки ACME", exact: true })).toContainText("корректный URL"); expect(writes).toBe(0)
})