From 5ac5a25dfcac996dbaa5ecf0501d48b1f283d61f Mon Sep 17 00:00:00 2001 From: Denozordec Date: Wed, 14 Oct 2026 00:31:50 +0700 Subject: [PATCH] =?UTF-8?q?fix(api):=20=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7?= =?UTF-8?q?=D1=8B=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=BF=D1=80=D0=BE=D1=81=D0=B0=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=80=D1=8B=D1=85=20OSPF=20=D0=B8=20BFD=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 2 +- .../scripts/acceptance/ospf-legacy-sql.mjs | 30 +++ backend/src/routes/ospf-legacy.test.ts | 32 +++ backend/src/routes/ospf.ts | 134 +++++-------- backend/src/services/ospf-legacy-poll.ts | 19 ++ docs/ux-ui-external-acceptance.md | 6 +- docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 12 +- docs/ux-ui-remaining-acceptance.md | 10 +- docs/ux-ui-stage-12.md | 13 +- docs/ux-ui-test-server.md | 6 +- docs/ux-ui/acceptance/ospf215-baseline.json | 188 ++++++++++++++++++ .../ospf215-build-proof-2026-10-14.json | 20 ++ .../ospf215-deployment-2026-10-14.json | 21 ++ docs/ux-ui/acceptance/ospf215-fixed.json | 188 ++++++++++++++++++ .../ospf215-initial-fixture-failure.json | 27 +++ .../acceptance/ospf215-live-2026-10-14.json | 74 +++++++ 17 files changed, 688 insertions(+), 96 deletions(-) create mode 100644 backend/scripts/acceptance/ospf-legacy-sql.mjs create mode 100644 backend/src/routes/ospf-legacy.test.ts create mode 100644 backend/src/services/ospf-legacy-poll.ts create mode 100644 docs/ux-ui/acceptance/ospf215-baseline.json create mode 100644 docs/ux-ui/acceptance/ospf215-build-proof-2026-10-14.json create mode 100644 docs/ux-ui/acceptance/ospf215-deployment-2026-10-14.json create mode 100644 docs/ux-ui/acceptance/ospf215-fixed.json create mode 100644 docs/ux-ui/acceptance/ospf215-initial-fixture-failure.json create mode 100644 docs/ux-ui/acceptance/ospf215-live-2026-10-14.json diff --git a/backend/package.json b/backend/package.json index bfb96aeb..b1e44e1a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -29,7 +29,7 @@ "test:scheduler": "tsx src/services/scheduler-running.test.ts && tsx src/services/scheduler-recorded-run.test.ts && tsx src/routes/scheduler.test.ts", "test:backups": "tsx src/services/backup-scheduler-run.test.ts && tsx src/services/backup-restore.test.ts && tsx src/services/backup-deletion.test.ts && tsx src/services/backup-dto.test.ts && tsx src/services/backup-storage-update.test.ts && tsx src/services/backup-job-execution.test.ts && tsx src/routes/backups-operations.test.ts && tsx src/services/s3-backup-client.test.ts", "test:ipsec": "tsx src/services/ipsec-config.test.ts && tsx src/services/ipsec-state.test.ts && tsx src/routes/ipsec-history.test.ts && tsx src/services/ipsec-restore.test.ts && tsx src/services/ipsec-operation.test.ts && tsx src/routes/ipsec-mutation.test.ts && tsx src/routes/ipsec-nat.test.ts && tsx src/routes/ipsec-group.test.ts", - "test:live-maps": "tsx src/services/gre-keepalive.test.ts && tsx src/services/gre-read.test.ts && tsx src/services/ospf-route-parse.test.ts && tsx src/services/vxlan-live.test.ts && tsx src/routes/vxlan.test.ts && tsx src/services/containers-live.test.ts && tsx src/services/firewall-poll.test.ts && tsx src/services/gre-catalog-poll.test.ts && tsx src/routes/filters-gre-catalog.test.ts && tsx src/routes/bgp-catalog.test.ts && tsx src/services/bgp-catalog-parse.test.ts && tsx src/routes/ospf-catalog.test.ts && tsx src/services/ospf-observed-values.test.ts", + "test:live-maps": "tsx src/services/gre-keepalive.test.ts && tsx src/services/gre-read.test.ts && tsx src/services/ospf-route-parse.test.ts && tsx src/services/vxlan-live.test.ts && tsx src/routes/vxlan.test.ts && tsx src/services/containers-live.test.ts && tsx src/services/firewall-poll.test.ts && tsx src/services/gre-catalog-poll.test.ts && tsx src/routes/filters-gre-catalog.test.ts && tsx src/routes/bgp-catalog.test.ts && tsx src/services/bgp-catalog-parse.test.ts && tsx src/routes/ospf-catalog.test.ts && tsx src/services/ospf-observed-values.test.ts && tsx src/routes/ospf-legacy.test.ts", "test": "node scripts/test.mjs", "test:evolution": "tsx src/services/evobgp-catalog.test.ts && tsx src/routes/evobgp-catalog.test.ts && tsx src/routes/evobgp-settings.test.ts && tsx src/plugins/auth.evolution-settings.test.ts", "test:netflow-purge": "tsx src/db/operation-lock.test.ts && tsx src/services/traffic-flow-purge-execution.test.ts && tsx src/routes/traffic-flow-purge.test.ts", diff --git a/backend/scripts/acceptance/ospf-legacy-sql.mjs b/backend/scripts/acceptance/ospf-legacy-sql.mjs new file mode 100644 index 00000000..b548fc6c --- /dev/null +++ b/backend/scripts/acceptance/ospf-legacy-sql.mjs @@ -0,0 +1,30 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import {createServer} from 'node:http' +import {randomBytes} from 'node:crypto' +const database=process.env.MMAPP_OWN_SQL_DB,url=new URL(process.env.DATABASE_URL) +assert(/^mm_accept_166_\d+$/.test(database??''));assert.equal(url.pathname,'/'+database);assert.equal(url.username,database) +process.env.AUTH_REQUIRED='true';process.env.AUTH_JWT_SECRET=randomBytes(32).toString('hex');process.env.AUTH_ISSUER='https://own215.invalid';process.env.AUTH_PORTAL_URL='https://own215.invalid' +const {pool,closePool}=await import('/app/backend/dist/db/index.js'),{initDatabase}=await import('/app/backend/dist/db/bootstrap.js'),{buildApp}=await import('/app/backend/dist/index.js') +assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database);assert(Object.values((await pool.query('SELECT rolsuper,rolcreatedb,rolcreaterole,rolbypassrls FROM pg_roles WHERE rolname=current_user')).rows[0]).every(v=>v===false)) +let phase='populated',app,nativeCalls=0,nativeWrites=0,disabledCalls=0;const cases=[] +const fixture=label=>createServer((req,res)=>{nativeCalls++;if(label==='disabled')disabledCalls++;if(req.method!=='GET')nativeWrites++;res.setHeader('content-type','application/json');if(phase==='failed'||(phase==='partial'&&label==='second')){res.statusCode=401;res.end(JSON.stringify({error:401,message:'own215 fixture denied'}));return}const rows=phase==='populated'&&req.url==='/rest/routing/ospf/instance'?[{'.id':'*1',name:'own215-ospf',disabled:'false'}]:phase==='populated'&&req.url==='/rest/routing/bfd/session'?[{'.id':'*1','local-address':'192.0.2.1','remote-address':'192.0.2.2',state:'up'}]:[];res.end(JSON.stringify(rows))}) +const natives=['first','second','disabled'].map(fixture);for(const n of natives)await new Promise(r=>n.listen(0,'127.0.0.1',r)) +const baseline=process.env.MMAPP_BASELINE==='true';const report={date:'2026-10-14',block:'12.5.215',baseline,database,cases,scope:'Compiled production routes/auth, isolated PostgreSQL and RouterOS HTTP fixture; real CHR unchanged',passed:false} +try{ + await initDatabase();const ids=[];for(let i=0;if.serverId),expected);assert(body.failures.every(f=>f.error.includes('401')));assert.match(body.error,/OSPF\/BFD/)} + else if(endpoint==='ospf/all'){assert.deepEqual(Object.keys(body).sort(),['bfdSessions','instances','interfaces','neighbors','routes']);assert.equal(body.instances.length,phase==='populated'?2:0);assert.equal(body.bfdSessions.length,phase==='populated'?2:0)} + else{assert(Array.isArray(body));assert.equal(body.length,phase==='populated'&&['ospf/instances','bfd/sessions'].includes(endpoint)?2:0)} + if(phase==='disabled')assert.equal(nativeCalls,before) + cases.push({phase,endpoint,status:r.statusCode,failures:body.failures?.length??0,nativeCalls:nativeCalls-before}) + } + } + assert.equal(nativeWrites,0);assert.equal(disabledCalls,0);report.passed=true +}catch(e){report.errorName=e.name;fs.writeFileSync('/app/backend/acceptance/ospf215-private.log',String(e.stack??e),{mode:0o600})}finally{if(app)await app.close();for(const n of natives){n.closeAllConnections();await new Promise(r=>n.close(r))}await closePool();report.nativeWrites=nativeWrites;report.disabledHomeRouterCalls=disabledCalls;fs.writeFileSync('/app/backend/acceptance/ospf215-'+(baseline?'baseline':'fixed')+'.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report))}if(!report.passed)process.exitCode=1 diff --git a/backend/src/routes/ospf-legacy.test.ts b/backend/src/routes/ospf-legacy.test.ts new file mode 100644 index 00000000..9c1fe9bc --- /dev/null +++ b/backend/src/routes/ospf-legacy.test.ts @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict' +import Fastify from 'fastify' +import {validatorCompiler,serializerCompiler} from '@fastify/type-provider-zod' +import {createOspfRoutes} from './ospf.js' +import {emptyOspfCatalog} from '../services/ospf-catalog-poll.js' +import {MikrotikClient} from '../services/mikrotik.js' +import {servers} from '../db/schema.js' +const devices=[{id:1,name:'Первый'},{id:2,name:'Второй'}] as typeof servers.$inferSelect[] +const original=MikrotikClient.fromServer +let failedIds:number[]=[],reads=0,enabled=true,populated=false +MikrotikClient.fromServer=server=>{ + const read=async()=>{reads++;if(failedIds.includes(server.id))throw Error('Недоступен '+server.name);return []} + return {getOspfNeighbors:read,getOspfAreas:read,getOspfInterfaceTemplates:read,getOspfInstances:async()=>{await read();return populated?[{'.id':'*1',name:'ospf-own'}]:[]},getBfdSessions:async()=>{await read();return populated?[{'.id':'*1','local-address':'192.0.2.1','remote-address':'192.0.2.2',state:'up'}]:[]},getIpRoutes:read,getIpv6Routes:read} as unknown as MikrotikClient +} +const app=Fastify();app.setValidatorCompiler(validatorCompiler);app.setSerializerCompiler(serializerCompiler) +let cases=0 +try{ + await app.register(createOspfRoutes({listEnabledServers:async()=>enabled?devices:[],readCatalog:async()=>emptyOspfCatalog()}),{prefix:'/api'}) + for(const phase of ['populated','empty','partial','failed','disabled'] as const){ + populated=phase==='populated';enabled=phase!=='disabled';failedIds=phase==='partial'?[2]:phase==='failed'?[1,2]:[] + for(const endpoint of ['ospf/neighbors','ospf/interfaces','ospf/instances','ospf/all','bfd/sessions']){ + const before=reads,r=await app.inject({method:'GET',url:'/api/'+endpoint}),body=r.json() + assert.equal(r.statusCode,failedIds.length?503:200) + if(failedIds.length){assert.deepEqual(body.failures.map((f:{serverId:number})=>f.serverId),failedIds);assert.match(body.error,/OSPF\/BFD/);assert.deepEqual(body.failures.map((f:{error:string})=>f.error),failedIds.map(id=>'Недоступен '+devices[id-1].name))} + else if(endpoint==='ospf/all'){assert.deepEqual(Object.keys(body).sort(),['bfdSessions','instances','interfaces','neighbors','routes']);assert.equal(body.instances.length,populated?2:0);assert.equal(body.bfdSessions.length,populated?2:0)} + else{assert(Array.isArray(body));assert.equal(body.length,populated&&['ospf/instances','bfd/sessions'].includes(endpoint)?2:0)} + if(phase==='disabled')assert.equal(reads,before) + cases++ + } + } +}finally{await app.close();MikrotikClient.fromServer=original} +console.log('Legacy OSPF/BFD: '+cases+' HTTP checks passed; populated/empty/partial/failed/no enabled devices') diff --git a/backend/src/routes/ospf.ts b/backend/src/routes/ospf.ts index 205d2ef8..46aa5aa4 100644 --- a/backend/src/routes/ospf.ts +++ b/backend/src/routes/ospf.ts @@ -18,6 +18,8 @@ import {readEditableOspfTemplates,validateOspfOptimizationPlan} from "../service import { observedInteger, observedDurationMs, observedBoolean } from "../services/ospf-observed-values.js" import { pollOspfCatalog, readOspfCatalogRaw, type OspfCatalogCollections } from "../services/ospf-catalog-poll.js" +import { pollLegacyOspf } from "../services/ospf-legacy-poll.js" + type ServerRow = typeof servers.$inferSelect type TrafficSampleRow = typeof trafficSamples.$inferSelect type SpeedProbeRow = typeof uptimeSpeedProbes.$inferSelect @@ -561,108 +563,66 @@ export function createOspfRoutes(dependencies: OspfCatalogDependencies = catalog return async (app) => { app.get("/ospf/catalog", async (_req, reply) => reply.send(await pollOspfCatalog(await dependencies.listEnabledServers(), dependencies.readCatalog))) - // GET /api/ospf/neighbors — aggregate OSPF neighbors from ALL enabled servers + // Legacy success envelopes have no place for partial failures. Fail explicitly. + const legacyFailure = (failures: { serverId: number; serverName: string; error: string }[]) => ({ + error: "Не удалось опросить все устройства OSPF/BFD", failures, + }) app.get("/ospf/neighbors", async (_req, reply) => { - const allServers = await db.select().from(servers).where(eq(servers.enabled, true)) - - const results = await Promise.all( - allServers.map(async (server) => { - try { - const { neighbors, areas } = await fetchServerOspf(server) - const areaMap = buildAreaMap(areas) - return parseNeighbors(server, neighbors, areaMap) - } catch { - return [] - } - }), - ) - - return reply.send(results.flat()) + const result = await pollLegacyOspf(await dependencies.listEnabledServers(), async server => { + const { neighbors, areas } = await fetchServerOspf(server) + return parseNeighbors(server, neighbors, buildAreaMap(areas)) + }) + if (result.failures.length) return reply.status(503).send(legacyFailure(result.failures)) + return reply.send(result.values.flat()) }) - // GET /api/ospf/interfaces — aggregate OSPF interface templates from ALL enabled servers app.get("/ospf/interfaces", async (_req, reply) => { - const allServers = await db.select().from(servers).where(eq(servers.enabled, true)) - - const results = await Promise.all( - allServers.map(async (server) => { - try { - const { ifaceTemplates, areas, instances } = await fetchServerOspf(server) - const areaMap = buildAreaMap(areas) - return parseInterfaces(server, ifaceTemplates, areas, instances, areaMap) - } catch { - return [] - } - }), - ) - - return reply.send(results.flat()) + const result = await pollLegacyOspf(await dependencies.listEnabledServers(), async server => { + const { ifaceTemplates, areas, instances } = await fetchServerOspf(server) + return parseInterfaces(server, ifaceTemplates, areas, instances, buildAreaMap(areas)) + }) + if (result.failures.length) return reply.status(503).send(legacyFailure(result.failures)) + return reply.send(result.values.flat()) }) - // GET /api/ospf/instances — aggregate OSPF instances from ALL enabled servers app.get("/ospf/instances", async (_req, reply) => { - const allServers = await db.select().from(servers).where(eq(servers.enabled, true)) - - const results = await Promise.all( - allServers.map(async (server) => { - try { - const { instances } = await fetchServerOspf(server) - return parseInstances(server, instances) - } catch { - return [] - } - }), - ) - - return reply.send(results.flat()) + const result = await pollLegacyOspf(await dependencies.listEnabledServers(), async server => { + const { instances } = await fetchServerOspf(server) + return parseInstances(server, instances) + }) + if (result.failures.length) return reply.status(503).send(legacyFailure(result.failures)) + return reply.send(result.values.flat()) }) - // GET /api/ospf/all — single round-trip: neighbors + interfaces + instances + BFD app.get("/ospf/all", async (_req, reply) => { - const allServers = await db.select().from(servers).where(eq(servers.enabled, true)) - - const perServer = await Promise.all( - allServers.map(async (server) => { - try { - const { neighbors, areas, ifaceTemplates, instances, bfdSessions, ipRoutes } = await fetchServerOspf(server) - const areaMap = buildAreaMap(areas) - return { - neighbors: parseNeighbors(server, neighbors, areaMap), - interfaces: parseInterfaces(server, ifaceTemplates, areas, instances, areaMap), - instances: parseInstances(server, instances), - bfdSessions: parseBfdSessions(server, bfdSessions), - routes: parseOspfRoutes(server, ipRoutes), - } - } catch { - return { neighbors: [], interfaces: [], instances: [], bfdSessions: [], routes: [] } - } - }), - ) - + const result = await pollLegacyOspf(await dependencies.listEnabledServers(), async server => { + const { neighbors, areas, ifaceTemplates, instances, bfdSessions, ipRoutes } = await fetchServerOspf(server) + const areaMap = buildAreaMap(areas) + return { + neighbors: parseNeighbors(server, neighbors, areaMap), + interfaces: parseInterfaces(server, ifaceTemplates, areas, instances, areaMap), + instances: parseInstances(server, instances), + bfdSessions: parseBfdSessions(server, bfdSessions), + routes: parseOspfRoutes(server, ipRoutes), + } + }) + if (result.failures.length) return reply.status(503).send(legacyFailure(result.failures)) return reply.send({ - neighbors: perServer.flatMap(r => r.neighbors), - interfaces: perServer.flatMap(r => r.interfaces), - instances: perServer.flatMap(r => r.instances), - bfdSessions: perServer.flatMap(r => r.bfdSessions), - routes: perServer.flatMap(r => r.routes), + neighbors: result.values.flatMap(r => r.neighbors), + interfaces: result.values.flatMap(r => r.interfaces), + instances: result.values.flatMap(r => r.instances), + bfdSessions: result.values.flatMap(r => r.bfdSessions), + routes: result.values.flatMap(r => r.routes), }) }) - // GET /api/bfd/sessions — BFD sessions only (for direct access) app.get("/bfd/sessions", async (_req, reply) => { - const allServers = await db.select().from(servers).where(eq(servers.enabled, true)) - const results = await Promise.all( - allServers.map(async (server) => { - try { - const client = MikrotikClient.fromServer(server) - const raw = await client.getBfdSessions() - return parseBfdSessions(server, raw) - } catch { - return [] - } - }), - ) - return reply.send(results.flat()) + const result = await pollLegacyOspf(await dependencies.listEnabledServers(), async server => { + const raw = await MikrotikClient.fromServer(server).getBfdSessions() + return parseBfdSessions(server, raw) + }) + if (result.failures.length) return reply.status(503).send(legacyFailure(result.failures)) + return reply.send(result.values.flat()) }) // GET /api/servers/:id/ospf — single server OSPF + BFD data diff --git a/backend/src/services/ospf-legacy-poll.ts b/backend/src/services/ospf-legacy-poll.ts new file mode 100644 index 00000000..31f17727 --- /dev/null +++ b/backend/src/services/ospf-legacy-poll.ts @@ -0,0 +1,19 @@ +/** Legacy APIs cannot represent partial data in their successful array envelopes. */ +export async function pollLegacyOspf( + devices: readonly S[], read: (device: S) => Promise, +) { + const results = await Promise.all(devices.map(async device => { + try { + return { ok: true as const, value: await read(device) } + } catch (error) { + return { ok: false as const, failure: { + serverId: device.id, serverName: device.name, + error: error instanceof Error ? error.message : "Ошибка опроса устройства", + } } + } + })) + return { + values: results.flatMap(result => result.ok ? [result.value] : []), + failures: results.flatMap(result => result.ok ? [] : [result.failure]), + } +} diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index 5e3bb79a..e59cbf17 100644 --- a/docs/ux-ui-external-acceptance.md +++ b/docs/ux-ui-external-acceptance.md @@ -1,6 +1,6 @@ # UX/UI: протокол внешней приёмки 12.5 -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md). @@ -2583,3 +2583,7 @@ Production compiled SQL/API29 на собственной ограниченно ## 12.5.214 — GRE badge contrast (2026-10-14) Подробности: [блок214 в журнале](ux-ui-progress.md). Scoped axe4 и live UI8 прошли; cleanup подтверждён. Backend ux-gre212, frontend ux-gre214. + +## 12.5.215 — OSPF/BFD (2026-10-14) + +Пять legacy endpoints возвращают503 при неполном опросе. Успешные envelopes сохранены; SQL25/localHTTP25 и live API/UI приняты. Подробности и границы — [журнал](ux-ui-progress.md#125215--явные-ошибки-legacy-ospfbfd-api-2026-10-14). Backend ux-ospf215, frontend ux-gre214; следующий216 — legacy BGP. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 499235f1..0b8a7715 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,6 +1,6 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index c50c404c..8d36a968 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -38,6 +38,16 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–196 и ограничения ниже. Runtime backend/frontend ux-peak196. | +## 12.5.215 — явные ошибки legacy OSPF/BFD API (2026-10-14) + +Пять legacy GET (/ospf/neighbors, /interfaces, /instances, /all и /bfd/sessions) раньше возвращали200 с пустыми/неполными данными при отказе RouterOS. Теперь любой неудачный опрос даёт503 с serverId/serverName/error. Успешные массивы и пять ключей /ospf/all сохранены; правила parser defaults, права и сетевые операции не менялись. Активный frontend использует /ospf/catalog с отдельным контрактом частичных результатов; он сохранён. + +Local HTTP25 и production SQL/native fixture25 проверили populated/empty/partial/all-failed/no enabled devices. Disabled Home Router не опрашивался, native writes0; временные PostgreSQL DB/roles удалены. Baseline предыдущего ux-gre212 подтвердил200 при отказах. Первичная fixture-проверка ошиблась из-за отсутствия disabled у instance и неверного учёта POST /get как записи: сохранён failed report, fixture исправлен без изменений CHR. Профили live-maps14 и ospf-optimizer5, backend build прошли. + +Backend ux-ospf215: compiled1052 и source481 matched, healthy; frontend ux-gre214 сохранён. С настоящим Portal JWT прошли шесть GET, шесть anonymous401, native resource200 на обоих CHR и OSPF UI1440/320 без page errors или mutation requests. Это проверка здорового live опроса; ошибки вводились только в собственном HTTP fixture. Физический WAN loss и полная регрессия этим блоком не принимаются. + +Протоколы: [baseline](ux-ui/acceptance/ospf215-baseline.json), [production SQL](ux-ui/acceptance/ospf215-fixed.json), [initial fixture failure](ux-ui/acceptance/ospf215-initial-fixture-failure.json), [build proof](ux-ui/acceptance/ospf215-build-proof-2026-10-14.json), [deployment](ux-ui/acceptance/ospf215-deployment-2026-10-14.json), [live](ux-ui/acceptance/ospf215-live-2026-10-14.json). Следующий216 — legacy BGP sessions для alerts: каталог содержит failures, но старый endpoint их отбрасывает. + ## 12.5.214 — контраст метки IPsec на заполненной GRE-таблице (2026-10-13 — 2026-10-14) После213 взят небольшой дополнительный блок доступности перед большим legacy OSPF/BFD. Baseline axe подтвердил color-contrast1,76:1 в светлой теме. В первоначальном mobile baseline метка не прокручивалась явно; отсутствие там violation не считается доказательством контраста. Ручные spans заменены штатным ReUI Badge invert-light, без изменения vendor components/theme. Конфигурация IPsec различается текстом и значком; метка не утверждает успешный handshake. diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index 202eae48..7bfbaad8 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,10 +1,10 @@ # Оставшаяся внешняя приёмка — 2026-10-13 -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. -Это актуальная матрица после 12.5.214. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.215. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. -Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-gre212`, frontend `ux-gre214`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. +Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-ospf215`, frontend `ux-gre214`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. | Раздел | Подтверждено | Осталось / условие завершения | | --- | --- | --- | @@ -1501,3 +1501,7 @@ Production compiled SQL/API29 на собственной ограниченно ## 12.5.214 — GRE badge contrast (2026-10-14) Подробности: [блок214 в журнале](ux-ui-progress.md). Scoped axe4 и live UI8 прошли; cleanup подтверждён. Backend ux-gre212, frontend ux-gre214. + +## 12.5.215 — OSPF/BFD (2026-10-14) + +Пять legacy endpoints возвращают503 при неполном опросе. Успешные envelopes сохранены; SQL25/localHTTP25 и live API/UI приняты. Подробности и границы — [журнал](ux-ui-progress.md#125215--явные-ошибки-legacy-ospfbfd-api-2026-10-14). Backend ux-ospf215, frontend ux-gre214; следующий216 — legacy BGP. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index aefbddd1..5b2d2a77 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1,6 +1,6 @@ # Этап 12: итоговая приёмка UX/UI -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push. @@ -3921,3 +3921,14 @@ Live: приложение создало по собственному откл Следующий215: /ospf/neighbors, /ospf/interfaces, /ospf/instances, /ospf/all и /bfd/sessions — сохранить success envelopes, перестать скрывать partial/all-failed native reads как200/empty; собственные SQL/HTTP fixtures, без отключения CHR. Оставшиеся внешние ограничения сохраняются в матрице. + +## 12.5.215 — явные ошибки legacy OSPF/BFD API (2026-10-14) + +Пять legacy GET (/ospf/neighbors, /interfaces, /instances, /all и /bfd/sessions) раньше возвращали200 с пустыми/неполными данными при отказе RouterOS. Теперь любой неудачный опрос даёт503 с serverId/serverName/error. Успешные массивы и пять ключей /ospf/all сохранены; правила parser defaults, права и сетевые операции не менялись. Активный frontend использует /ospf/catalog с отдельным контрактом частичных результатов; он сохранён. + +Local HTTP25 и production SQL/native fixture25 проверили populated/empty/partial/all-failed/no enabled devices. Disabled Home Router не опрашивался, native writes0; временные PostgreSQL DB/roles удалены. Baseline предыдущего ux-gre212 подтвердил200 при отказах. Первичная fixture-проверка ошиблась из-за отсутствия disabled у instance и неверного учёта POST /get как записи: сохранён failed report, fixture исправлен без изменений CHR. Профили live-maps14 и ospf-optimizer5, backend build прошли. + +Backend ux-ospf215: compiled1052 и source481 matched, healthy; frontend ux-gre214 сохранён. С настоящим Portal JWT прошли шесть GET, шесть anonymous401, native resource200 на обоих CHR и OSPF UI1440/320 без page errors или mutation requests. Это проверка здорового live опроса; ошибки вводились только в собственном HTTP fixture. Физический WAN loss и полная регрессия этим блоком не принимаются. + +Протоколы: [baseline](ux-ui/acceptance/ospf215-baseline.json), [production SQL](ux-ui/acceptance/ospf215-fixed.json), [initial fixture failure](ux-ui/acceptance/ospf215-initial-fixture-failure.json), [build proof](ux-ui/acceptance/ospf215-build-proof-2026-10-14.json), [deployment](ux-ui/acceptance/ospf215-deployment-2026-10-14.json), [live](ux-ui/acceptance/ospf215-live-2026-10-14.json). Следующий216 — legacy BGP sessions для alerts: каталог содержит failures, но старый endpoint их отбрасывает. + diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index b5287630..e87ba0c9 100644 --- a/docs/ux-ui-test-server.md +++ b/docs/ux-ui-test-server.md @@ -1,6 +1,6 @@ # Тестовый сервер UX/UI — 2026-10-10 -Актуальное продолжение 2026-10-14: блок214 завершён. Контраст заполненной метки GRE IPsec исправлен штатным ReUI Badge. Baseline1,76:1; четыре scoped axe fixtures и восемь live UI light/dark × reader/admin ×1440/320 прошли. Own GRE/ULA/PSK и Portal reader удалены. Backend ux-gre212, frontend ux-gre214/source527 совпал. Следующий215 — ошибки пяти legacy OSPF/BFD GET endpoints. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: блок215 завершён. Пять legacy OSPF/BFD GET больше не скрывают частичные/полные отказы за200. Успешные envelopes сохранены. Local HTTP25, isolated production SQL25, profile14+5; compiled1052/source481 matched. Real Portal API6/anonymous6, native resource2 и OSPF UI1440/320 прошли. Own DB/roles удалены; CHR пароли не менялись. Backend ux-ospf215, frontend ux-gre214. Следующий216 — legacy BGP GET для оповещений. Этап12.5 открыт. | Параметр | Проверенное состояние | | --- | --- | @@ -2314,3 +2314,7 @@ Production compiled SQL/API29 на собственной ограниченно ## 12.5.214 — GRE badge contrast (2026-10-14) Подробности: [блок214 в журнале](ux-ui-progress.md). Scoped axe4 и live UI8 прошли; cleanup подтверждён. Backend ux-gre212, frontend ux-gre214. + +## 12.5.215 — OSPF/BFD (2026-10-14) + +Пять legacy endpoints возвращают503 при неполном опросе. Успешные envelopes сохранены; SQL25/localHTTP25 и live API/UI приняты. Подробности и границы — [журнал](ux-ui-progress.md#125215--явные-ошибки-legacy-ospfbfd-api-2026-10-14). Backend ux-ospf215, frontend ux-gre214; следующий216 — legacy BGP. diff --git a/docs/ux-ui/acceptance/ospf215-baseline.json b/docs/ux-ui/acceptance/ospf215-baseline.json new file mode 100644 index 00000000..656f930c --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-baseline.json @@ -0,0 +1,188 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "baseline": true, + "database": "mm_accept_166_1791912497373", + "cases": [ + { + "phase": "populated", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "empty", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "partial", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "failed", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "disabled", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 0 + } + ], + "scope": "Compiled production routes/auth, isolated PostgreSQL and RouterOS HTTP fixture; real CHR unchanged", + "passed": true, + "nativeWrites": 0, + "disabledHomeRouterCalls": 0, + "ownedDatabaseAndRoleRemoved": true +} diff --git a/docs/ux-ui/acceptance/ospf215-build-proof-2026-10-14.json b/docs/ux-ui/acceptance/ospf215-build-proof-2026-10-14.json new file mode 100644 index 00000000..bf08d538 --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-build-proof-2026-10-14.json @@ -0,0 +1,20 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "compiled": { + "matched": 1052, + "missing": 0, + "different": 0 + }, + "source": [ + { + "kind": "backend", + "matched": 481 + } + ], + "profileScripts": { + "liveMaps": 14, + "ospfOptimizer": 5 + }, + "localHttpCases": 25 +} diff --git a/docs/ux-ui/acceptance/ospf215-deployment-2026-10-14.json b/docs/ux-ui/acceptance/ospf215-deployment-2026-10-14.json new file mode 100644 index 00000000..cb69cb15 --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-deployment-2026-10-14.json @@ -0,0 +1,21 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "backendSourceMatched": 481, + "compiledMatched": 1052, + "frontendChanged": false, + "services": { + "backend": { + "image": "mmapp-test/backend:ux-ospf215", + "imageId": "sha256:168e8fcb5e7779b4168194d87693c93a05cebbf3e923077d497a9eaee10b5bb0", + "running": true, + "health": "healthy" + }, + "frontend": { + "image": "mmapp-test/frontend:ux-gre214", + "imageId": "sha256:7035e5ba628276a9927cce4814739dadd920dba0606e3b9fc316cc0262f77d5f", + "running": true, + "health": null + } + } +} diff --git a/docs/ux-ui/acceptance/ospf215-fixed.json b/docs/ux-ui/acceptance/ospf215-fixed.json new file mode 100644 index 00000000..52ea57bd --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-fixed.json @@ -0,0 +1,188 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "baseline": false, + "database": "mm_accept_166_1791912590293", + "cases": [ + { + "phase": "populated", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "empty", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "empty", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 2 + }, + { + "phase": "partial", + "endpoint": "ospf/neighbors", + "status": 503, + "failures": 1, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/interfaces", + "status": 503, + "failures": 1, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/instances", + "status": 503, + "failures": 1, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "ospf/all", + "status": 503, + "failures": 1, + "nativeCalls": 14 + }, + { + "phase": "partial", + "endpoint": "bfd/sessions", + "status": 503, + "failures": 1, + "nativeCalls": 2 + }, + { + "phase": "failed", + "endpoint": "ospf/neighbors", + "status": 503, + "failures": 2, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/interfaces", + "status": 503, + "failures": 2, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/instances", + "status": 503, + "failures": 2, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "ospf/all", + "status": 503, + "failures": 2, + "nativeCalls": 14 + }, + { + "phase": "failed", + "endpoint": "bfd/sessions", + "status": 503, + "failures": 2, + "nativeCalls": 2 + }, + { + "phase": "disabled", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/instances", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "ospf/all", + "status": 200, + "failures": 0, + "nativeCalls": 0 + }, + { + "phase": "disabled", + "endpoint": "bfd/sessions", + "status": 200, + "failures": 0, + "nativeCalls": 0 + } + ], + "scope": "Compiled production routes/auth, isolated PostgreSQL and RouterOS HTTP fixture; real CHR unchanged", + "passed": true, + "nativeWrites": 0, + "disabledHomeRouterCalls": 0, + "ownedDatabaseAndRoleRemoved": true +} diff --git a/docs/ux-ui/acceptance/ospf215-initial-fixture-failure.json b/docs/ux-ui/acceptance/ospf215-initial-fixture-failure.json new file mode 100644 index 00000000..6b2d7f12 --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-initial-fixture-failure.json @@ -0,0 +1,27 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "baseline": true, + "database": "mm_accept_166_1791912375216", + "cases": [ + { + "phase": "populated", + "endpoint": "ospf/neighbors", + "status": 200, + "failures": 0, + "nativeCalls": 14 + }, + { + "phase": "populated", + "endpoint": "ospf/interfaces", + "status": 200, + "failures": 0, + "nativeCalls": 18 + } + ], + "scope": "Compiled production routes/auth, isolated PostgreSQL and RouterOS HTTP fixture; real CHR unchanged", + "passed": false, + "errorName": "AssertionError", + "nativeWrites": 6, + "disabledHomeRouterCalls": 0 +} diff --git a/docs/ux-ui/acceptance/ospf215-live-2026-10-14.json b/docs/ux-ui/acceptance/ospf215-live-2026-10-14.json new file mode 100644 index 00000000..f2311520 --- /dev/null +++ b/docs/ux-ui/acceptance/ospf215-live-2026-10-14.json @@ -0,0 +1,74 @@ +{ + "date": "2026-10-14", + "block": "12.5.215", + "backend": "ux-ospf215", + "frontend": "ux-gre214", + "cases": [ + { + "endpoint": "/api/ospf/neighbors", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + }, + { + "endpoint": "/api/ospf/interfaces", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + }, + { + "endpoint": "/api/ospf/instances", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + }, + { + "endpoint": "/api/ospf/all", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + }, + { + "endpoint": "/api/bfd/sessions", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + }, + { + "endpoint": "/api/ospf/catalog", + "status": 200, + "anonymousStatus": 401, + "envelopePreserved": true + } + ], + "hosts": [ + { + "host": "194.226.121.150", + "nativeResourceStatus": 200, + "passwordUnchanged": true + }, + { + "host": "104.165.244.7", + "nativeResourceStatus": 200, + "passwordUnchanged": true + } + ], + "ui": [ + { + "width": 1440, + "actualCatalogStatus": 200, + "pageErrors": 0, + "mutationRequests": 0 + }, + { + "width": 320, + "actualCatalogStatus": 200, + "pageErrors": 0, + "mutationRequests": 0 + } + ], + "realPortalJWT": true, + "apiMocks": false, + "nativeWrites": 0, + "scope": "Healthy live legacy/catalog GET and active OSPF UI; failures simulated only in isolated native HTTP fixture" +}