fix(traffic): сохранять историю и показывать достоверную свежесть
This commit is contained in:
1 parent
ebe62ae2ef
commit
1275dd7b9b
48 files changed
+1899
-187
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
for (const outcome of ['success', 'error'] as const) {
|
||||
@@ -6,23 +7,23 @@ for (const outcome of ['success', 'error'] as const) {
|
||||
const gate = new Promise<void>(resolve => { release = resolve })
|
||||
const individual: string[] = []
|
||||
const server = { id: '1', name: 'Подтверждённый сервер', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, rxPeak: 5, txPeak: 6, rxTotal: 100, txTotal: 200, sessions: 1, rxSeries: [1, 2], txSeries: [2, 3] }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', async route => {
|
||||
const path = new URL(route.request().url()).pathname
|
||||
if (/^\/api\/traffic\/servers\/[^/]+/.test(path)) individual.push(path)
|
||||
if (path === '/api/traffic/servers') {
|
||||
await gate
|
||||
if (outcome === 'error') return route.fulfill({ status: 503, json: { error: 'Каталог временно недоступен' } })
|
||||
return route.fulfill({ json: { servers: [server] } })
|
||||
if (outcome === 'error') return route.fulfill({ status: 503, json: observedTrafficFixturePayload({ error: 'Каталог временно недоступен' }) })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload({ servers: [server] }) })
|
||||
}
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: {} })
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: observedTrafficFixturePayload({}) })
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
: path === '/api/traffic/servers/1' ? { server }
|
||||
: path === '/api/traffic/servers/1/interfaces' ? { interfaces: [] }
|
||||
: path === '/api/traffic/users' ? { users: [] }
|
||||
: path === '/api/traffic/bound-interfaces' ? { interfaces: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
return route.fulfill({ json })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
const catalog = page.waitForRequest(r => new URL(r.url()).pathname === '/api/traffic/servers')
|
||||
await page.goto('/traffic')
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
|
||||
async function setup(page: Page) {
|
||||
const control = { holdRange: '', failIface: false, series: undefined as Array<number | null> | undefined, txSeries: undefined as number[] | undefined, release: () => {} }
|
||||
const gate = new Promise<void>(resolve => { control.release = resolve })
|
||||
const server = { id: '1', name: 'Первый сервер', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, rxPeak: 5, txPeak: 6, rxTotal: 100, txTotal: 200, sessions: 1, rxSeries: [1, 2], txSeries: [2, 3] }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', async route => {
|
||||
const url = new URL(route.request().url()), path = url.pathname
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: {} })
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: observedTrafficFixturePayload({}) })
|
||||
if (path === '/api/traffic/servers/1') {
|
||||
const range = url.searchParams.get('range'), iface = url.searchParams.get('iface')
|
||||
if (control.holdRange && range === control.holdRange) await gate
|
||||
if (control.failIface && iface === 'ether2') return route.fulfill({ status: 500, json: { error: 'Интерфейс временно недоступен' } })
|
||||
return route.fulfill({ json: { server: { ...server, rxSeries: control.series ?? server.rxSeries, txSeries: control.txSeries ?? control.series ?? server.txSeries, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } } })
|
||||
if (control.failIface && iface === 'ether2') return route.fulfill({ status: 500, json: observedTrafficFixturePayload({ error: 'Интерфейс временно недоступен' }) })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload({ server: { ...server, rxSeries: control.series ?? server.rxSeries, txSeries: control.txSeries ?? control.series ?? server.txSeries, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } }) })
|
||||
}
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
: path === '/api/traffic/servers' ? { servers: [{ ...server, rxSeries: control.series ?? server.rxSeries, txSeries: control.txSeries ?? control.series ?? server.txSeries }] }
|
||||
@@ -20,7 +21,7 @@ async function setup(page: Page) {
|
||||
: path.endsWith('/interfaces') || path === '/api/traffic/bound-interfaces' ? { interfaces: [] }
|
||||
: path === '/api/traffic/users' ? { users: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
await route.fulfill({ json })
|
||||
await route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
await page.goto('/traffic')
|
||||
await expect(page.getByRole('heading', { name: 'Детали 1h __all__', exact: true })).toBeVisible()
|
||||
@@ -118,7 +119,7 @@ test('Карточка трафика: пустая история, одна т
|
||||
await page.getByRole('button', { name: '15м', exact: true }).click()
|
||||
await expect(card.locator('circle')).toHaveCount(1)
|
||||
await expect(card.locator('circle')).toHaveAttribute('cx', '150')
|
||||
control.series = [2, 3, -1, 4, 5]
|
||||
control.series = [2, 3, null, 4, 5]
|
||||
control.txSeries = [0, 0, 0, 0, 0]
|
||||
await page.getByRole('button', { name: '4ч', exact: true }).click()
|
||||
await expect(card.locator('[data-direction="rx"] polyline')).toHaveCount(2)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { test, expect, type Page } from "@playwright/test"
|
||||
const at = new Date("2026-10-12T12:00:00Z")
|
||||
async function setup(page: Page, state: "error" | "current" | "legacy", status = "online") {
|
||||
let calls = 0
|
||||
const observations = { rxNow: state === "current", txNow: state === "current", rxSeries: [false, true], txSeries: [false, true],
|
||||
sampledAt: at.toISOString(), lastAttemptAt: at.toISOString(), lastSucceededAt: at.toISOString(),
|
||||
expiresAt: new Date(at.getTime()+90_000).toISOString(), currentState: state === "legacy" ? "unknown" : state,
|
||||
error: state === "error" ? "CHR: HTTP 401 — опрос не выполнен" : null }
|
||||
const server = { id: "1", name: "История CHR", site: "TEST", country: "RU", status,
|
||||
rxNow: state === "current" ? 0 : 17, txNow: 0, rxPeak: 5, txPeak: 6, rxTotal: 1, txTotal: 2, sessions: 1,
|
||||
rxSeries: [0,5], txSeries: [0,6], ...(state !== "legacy" ? {observations} : {}) }
|
||||
await page.clock.install({time:at})
|
||||
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.endsWith("/live")) return r.fulfill({status:503,json:{error:"Live недоступен"}})
|
||||
if(path==="/api/traffic/servers")calls++
|
||||
return r.fulfill({json:path==="/api/auth/config"?{required:false}:path==="/api/traffic/servers"?{servers:[server]}:
|
||||
path==="/api/traffic/servers/1"?{server}:path.endsWith("/interfaces")||path==="/api/traffic/bound-interfaces"?{interfaces:[]}:
|
||||
path==="/api/traffic/users"?{users:[]}:path==="/api/servers"?[]:{}})
|
||||
})
|
||||
await page.goto("/traffic")
|
||||
await expect(page.getByRole("heading",{name:"История CHR",exact:true})).toBeVisible()
|
||||
return ()=>calls
|
||||
}
|
||||
for(const width of [1440,320]) test(`Ошибка опроса: история сохранена, текущая скорость неизвестна ${width}`,async({page})=>{
|
||||
await page.setViewportSize({width,height:1000});await setup(page,"error","offline")
|
||||
await expect(page.getByRole("status").filter({hasText:"Ошибка опроса трафика"})).toContainText("HTTP 401")
|
||||
await expect(page.getByRole("region",{name:"История RX/TX"})).toBeVisible()
|
||||
await expect(page.getByLabel("Скорость выбранного сервера")).not.toContainText("17.0")
|
||||
await expect(page.getByLabel("Скорость выбранного сервера").getByText("—",{exact:true})).toHaveCount(2)
|
||||
await expect(page.getByLabel("Попытка опроса трафика")).toBeVisible()
|
||||
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true)
|
||||
await page.getByRole("status").filter({hasText:"Ошибка опроса трафика"}).scrollIntoViewIfNeeded()
|
||||
await page.getByRole("status").filter({hasText:"Ошибка опроса трафика"}).evaluate(el => {
|
||||
let parent = el.parentElement
|
||||
while (parent && (!/auto|scroll/.test(getComputedStyle(parent).overflowY) || parent.scrollHeight <= parent.clientHeight)) parent = parent.parentElement
|
||||
if (parent) parent.scrollTop += el.getBoundingClientRect().top - parent.getBoundingClientRect().top - 24
|
||||
})
|
||||
await page.screenshot({path:`docs/ux-ui/screens/traffic-history-error-${width}-2026-10-12.png`,fullPage:true})
|
||||
})
|
||||
test("Измеренный ноль сохраняется; по TTL скорость становится неизвестной без подмены истории",async({page})=>{
|
||||
const calls=await setup(page,"current")
|
||||
await expect(page.getByLabel("Скорость выбранного сервера").getByText("0 Кбит/с",{exact:true})).toHaveCount(2)
|
||||
const before=calls();await page.clock.fastForward(100_001)
|
||||
await expect(page.getByRole("status").filter({hasText:"Измерения трафика устарели"})).toBeVisible()
|
||||
await expect(page.getByLabel("Скорость выбранного сервера").getByText("—",{exact:true})).toHaveCount(2)
|
||||
await expect(page.getByRole("region",{name:"История RX/TX"})).toBeVisible();expect(calls()).toBe(before)
|
||||
})
|
||||
test("Legacy numeric DTO без происхождения не изображает измеренную историю",async({page})=>{
|
||||
await setup(page,"legacy")
|
||||
await expect(page.getByText("Нет исторических измерений за выбранный период",{exact:true})).toBeVisible()
|
||||
await expect(page.getByRole("status").filter({hasText:"Скорость пока не измерена"})).toBeVisible()
|
||||
await expect(page.getByLabel("Скорость выбранного сервера")).not.toContainText("17.0")
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
for (const width of [1440, 320]) {
|
||||
@@ -10,10 +11,10 @@ for (const width of [1440, 320]) {
|
||||
{ src: '10.0.0.1', dst: '8.8.8.8', srcPort: 50002, dstPort: 443, internetPeer: '8.8.8.8', internetPeerPort: 443, proto: 6, protoName: 'TCP', application: 'HTTPS' },
|
||||
].map(row => ({ ...row, serverId: '1', serverName: 'IPv6 exporter', clientIp: row.src, inIface: 'ether1', bps: 1000, bytes: 1000, packets: 10 }))
|
||||
const stats = { bpsNow: 0, bytes: 4000, packets: 40, conversations: 4, uniqueSrc: 4, uniqueDst: 4, topCategory: 'Прочее', topProto: 'ICMPv6', rxSeries: [], txSeries: [], interfaces: [], ifaces: [], live: false, timeline: [], applications: [], categories: [], services: [], countries: [], asns: [], protocols: [], sources: [], destinations: [], conversationsList: rows, dedupApplied: false }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', route => {
|
||||
const path = new URL(route.request().url()).pathname
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: {} })
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: observedTrafficFixturePayload({}) })
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
: path === '/api/traffic/flow/analytics' ? stats
|
||||
: path === '/api/traffic/flow/exporters' ? { exporters: [{ id: '1', name: 'IPv6 exporter', subtitle: '', site: 'TEST', country: '', status: 'online', rxNow: 0, txNow: 0, sessions: 4, bytes: 4000, rxSeries: [], txSeries: [] }] }
|
||||
@@ -23,7 +24,7 @@ for (const width of [1440, 320]) {
|
||||
: path === '/api/traffic/flow/clients' ? { clients: [] }
|
||||
: path.endsWith('/interfaces') ? { interfaces: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
return route.fulfill({ json })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
await page.goto('/traffic')
|
||||
await page.getByRole('group', { name: 'Разделы трафика' }).getByRole('button', { name: 'Потоки', exact: true }).click()
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
|
||||
async function setup(page: Page) {
|
||||
const control = { holdRange: '', failList: false, release: () => {} }
|
||||
const gate = new Promise<void>(resolve => { control.release = resolve })
|
||||
const server = { id: '1', name: 'Первый сервер', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, rxPeak: 5, txPeak: 6, rxTotal: 100, txTotal: 200, sessions: 1, rxSeries: [1, 2], txSeries: [2, 3] }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', async route => {
|
||||
const url = new URL(route.request().url()), path = url.pathname
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: {} })
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: observedTrafficFixturePayload({}) })
|
||||
if (path === '/api/traffic/servers/1') {
|
||||
const range = url.searchParams.get('range'), iface = url.searchParams.get('iface')
|
||||
return route.fulfill({ json: { server: { ...server, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } } })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload({ server: { ...server, name: `Детали ${range} ${iface}`, rxNow: range === '24h' ? 24 : 15 } }) })
|
||||
}
|
||||
if (path === '/api/traffic/servers') {
|
||||
const range = url.searchParams.get('range')
|
||||
if (control.holdRange === range) await gate
|
||||
if (control.failList) return route.fulfill({ status: 500, json: { error: 'Сводка временно недоступна' } })
|
||||
return route.fulfill({ json: { servers: [{ ...server, name: 'Список ' + range }] } })
|
||||
if (control.failList) return route.fulfill({ status: 500, json: observedTrafficFixturePayload({ error: 'Сводка временно недоступна' }) })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload({ servers: [{ ...server, name: 'Список ' + range }] }) })
|
||||
}
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
: path === '/api/traffic/servers' ? { servers: [server] }
|
||||
@@ -24,7 +25,7 @@ async function setup(page: Page) {
|
||||
: path.endsWith('/interfaces') || path === '/api/traffic/bound-interfaces' ? { interfaces: [] }
|
||||
: path === '/api/traffic/users' ? { users: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
await route.fulfill({ json })
|
||||
await route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
await page.goto('/traffic')
|
||||
await expect(page.getByRole('heading', { name: 'Детали 1h __all__', exact: true })).toBeVisible()
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { installOpenLiveStream, resumeLiveStream, sendLiveHeartbeat } from "./live-stream-fixture"
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
for (const variant of ['closed', 'invalid', 'http-error', 'silent', 'awaiting'] as const) {
|
||||
test(`Трафик: SSE ${variant} не выдаёт старое измерение за live`, async ({ page }) => {
|
||||
const server = { id: '1', name: 'Тестовый сервер', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, rxPeak: 5, txPeak: 6, rxTotal: 100, txTotal: 200, sessions: 1, rxSeries: [1, 2], txSeries: [2, 3] }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', route => {
|
||||
const url = new URL(route.request().url())
|
||||
const path = url.pathname
|
||||
if (path.endsWith('/live')) {
|
||||
if (variant === 'http-error' || url.searchParams.get('iface') === 'ether2') return route.fulfill({ status: 503, json: { error: 'Поток недоступен' } })
|
||||
if (variant === 'http-error' || url.searchParams.get('iface') === 'ether2') return route.fulfill({ status: 503, json: observedTrafficFixturePayload({ error: 'Поток недоступен' }) })
|
||||
const sample = { rxMbps: 17, txMbps: 19, at: '2026-09-23T12:00:00Z' }
|
||||
const body = 'event: sample\ndata: ' + JSON.stringify(sample) + '\n\n' + (variant === 'invalid' ? 'event: sample\ndata: {"rxMbps":null,"txMbps":-1}\n\n' : '')
|
||||
return route.fulfill({ contentType: 'text/event-stream', body })
|
||||
@@ -21,7 +22,7 @@ for (const variant of ['closed', 'invalid', 'http-error', 'silent', 'awaiting']
|
||||
: path.endsWith('/interfaces') || path === '/api/traffic/bound-interfaces' ? { interfaces: [] }
|
||||
: path === '/api/traffic/users' ? { users: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
return route.fulfill({ json })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
if (variant === 'silent' || variant === 'awaiting') await installOpenLiveStream(page, '/api/traffic/servers/1/live', { rxMbps: 17, txMbps: 19, at: '2026-09-23T12:00:00Z' }, variant !== 'awaiting')
|
||||
const received = new Date('2026-09-28T06:00:00Z')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
|
||||
const longName = 'customer-with-a-very-long-unbroken-identifier-12345678901234567890'
|
||||
@@ -5,7 +6,7 @@ async function setup(page: Page, empty = false) {
|
||||
const metrics = { rxNow: 2, txNow: 3, rxPeak: 4, txPeak: 5, rxTotal: 100, txTotal: 200, rxSeries: [1, 2], txSeries: [2, 3] }
|
||||
const iface = { ...metrics, id: 'i1', bindingId: 'b1', userId: 'u1', userLogin: longName, userName: longName, interfaceName: longName, interfaceType: 'gre', comment: longName, serverId: '1', serverName: longName, serverSite: 'TEST', serverCountry: 'RU', status: 'online' }
|
||||
const user = { ...metrics, id: 'u1', login: longName, displayName: longName, role: 'viewer', interfaces: [iface] }
|
||||
await page.route('**/health', r => r.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', r => r.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', route => {
|
||||
const path = new URL(route.request().url()).pathname
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
@@ -14,7 +15,7 @@ async function setup(page: Page, empty = false) {
|
||||
: path === '/api/traffic/bound-interfaces' ? { interfaces: empty ? [] : [iface] }
|
||||
: path.endsWith('/interfaces') ? { interfaces: [] }
|
||||
: path === '/api/servers' ? [] : {}
|
||||
return route.fulfill({ json })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
await page.goto('/traffic')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/** Positive fixtures explicitly declare measured rates; legacy absence is tested separately. */
|
||||
export function observedTrafficFixturePayload(value: unknown): unknown {
|
||||
if (Array.isArray(value)) return value.map(observedTrafficFixturePayload)
|
||||
if (!value || typeof value !== "object") return value
|
||||
const r = Object.fromEntries(Object.entries(value).map(([key, v]) => [key, observedTrafficFixturePayload(v)]))
|
||||
if ("rxNow" in r && "txNow" in r && "rxTotal" in r && "txTotal" in r && Array.isArray(r.rxSeries) && Array.isArray(r.txSeries) && !("observations" in r)) {
|
||||
r.observations = { rxNow: true, txNow: true, sampledAt: "2026-10-12T12:00:00Z",
|
||||
rxSeries: r.rxSeries.map(v => typeof v === "number" && Number.isFinite(v) && v >= 0),
|
||||
txSeries: r.txSeries.map(v => typeof v === "number" && Number.isFinite(v) && v >= 0) }
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { observedTrafficFixturePayload } from "./traffic-observation-fixture"
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
for (const variant of ['missing-sessions', 'negative-series', 'monthly-missing', 'monthly-mismatch'] as const) {
|
||||
@@ -9,7 +10,7 @@ for (const variant of ['missing-sessions', 'negative-series', 'monthly-missing',
|
||||
const stats = { bpsNow: 1000000, bytes: 200, packets: 2, conversations: 1, uniqueSrc: 1, uniqueDst: 1, topProto: 'TCP', topCategory: 'Восстановлено', rxSeries: [1], txSeries: [2], applications: [], protocols: [], sources: [], destinations: [], interfaces: [], conversationsList: [], ifaces: [], live: false }
|
||||
const card = { id: '1', name: 'Экспортёр DTO', subtitle: '', site: 'TEST', country: 'RU', status: 'online', rxNow: 2, txNow: 3, sessions: 1, bytes: 200, rxSeries: [1], txSeries: [2] }
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/health', route => route.fulfill({ json: observedTrafficFixturePayload({ ok: true }) }))
|
||||
await page.route('**/api/**', route => {
|
||||
const url = new URL(route.request().url()), path = url.pathname
|
||||
if (path === '/api/traffic/flow/analytics' || path === '/api/traffic/flow/monthly') {
|
||||
@@ -18,13 +19,13 @@ for (const variant of ['missing-sessions', 'negative-series', 'monthly-missing',
|
||||
const month = url.searchParams.get('month')!
|
||||
const data = bad && variant === 'monthly-missing' ? { month, bytes: 1500 }
|
||||
: { month: bad && variant === 'monthly-mismatch' ? (month === '2026-01' ? '2026-02' : '2026-01') : month, bytes: 1500, countries: [], services: [], asns: [] }
|
||||
return route.fulfill({ json: data })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(data) })
|
||||
}
|
||||
const data = bad && variant === 'missing-sessions' ? { ...stats, conversationsList: undefined }
|
||||
: bad && variant === 'negative-series' ? { ...stats, rxSeries: [-1] } : stats
|
||||
return route.fulfill({ json: data })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(data) })
|
||||
}
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: {} })
|
||||
if (path.endsWith('/live')) return route.fulfill({ status: 503, json: observedTrafficFixturePayload({}) })
|
||||
const json = path === '/api/auth/config' ? { required: false }
|
||||
: path === '/api/traffic/flow/exporters' ? { exporters: [card] }
|
||||
: path === '/api/traffic/flows' ? { exportersOnline: 1, bytesPerMin: 200, uniqueSrc: 1, uniqueDst: 1, topProto: 'TCP', talkers: [], listenerBound: true }
|
||||
@@ -33,7 +34,7 @@ for (const variant of ['missing-sessions', 'negative-series', 'monthly-missing',
|
||||
: path === '/api/traffic/flow/clients' ? { clients: [] }
|
||||
: path.endsWith('/interfaces') ? { interfaces: [] }
|
||||
: path === '/api/servers' ? [{ id: 1, name: card.name, host: '127.0.0.1', port: 80, useSsl: false, verifySsl: false, username: 'admin', password: '', type: 'jump-host', site: 'TEST', country: 'RU', asn: '', comment: '', enabled: true, lanSubnet: '', wanUplinks: [], createdAt: '', updatedAt: '', status: null, latency: null, os: null, model: null, uptime: null, cpuLoad: null, freeMemory: null, totalMemory: null, identityName: null, sessions: 0, polledAt: null }] : {}
|
||||
return route.fulfill({ json })
|
||||
return route.fulfill({ json: observedTrafficFixturePayload(json) })
|
||||
})
|
||||
await page.goto('/traffic')
|
||||
await page.getByRole('group', { name: 'Разделы трафика' }).getByRole('button', { name: 'Потоки', exact: true }).click()
|
||||
|
||||
Reference in new issue
Block a user