test(web): add Vitest CI, ADRs, and PR Definition of Done
FE unit-тесты селекторов/api-client в CI; ADR по durable jobs, OpenAPI codegen и legacy Svelte; PR template WEB-19; процесс KPI cross-app sync. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
## Summary
|
||||
|
||||
<!-- 1–3 bullets: what and why -->
|
||||
|
||||
## Checklist
|
||||
|
||||
### General
|
||||
|
||||
- [ ] Docs updated if behavior/API changed (DOC-02)
|
||||
- [ ] Conventional Commits (EN title / RU body)
|
||||
|
||||
### Backend (if Go / OpenAPI / migrations)
|
||||
|
||||
- [ ] `go test ./... -race -count=1` (or scoped packages) when touching jobs/pipeline/httpapi
|
||||
- [ ] OpenAPI lint + regenerate `apps/web/src/types/api.gen.ts` if `docs/openapi.yaml` changed
|
||||
- [ ] Migration pairs postgres+sqlite (DEP-03)
|
||||
|
||||
### Frontend (if `apps/web` / `packages/ui`) — WEB-19
|
||||
|
||||
- [ ] `pnpm --filter @evobgp/web run typecheck`
|
||||
- [ ] `pnpm --filter @evobgp/web run lint`
|
||||
- [ ] `pnpm --filter @evobgp/web run build`
|
||||
- [ ] `pnpm --filter @evobgp/web run test`
|
||||
- [ ] UI follows ReUI PRO + `docs/ui-design-contract.md` (surface `frame`, kit; cite previewUrl)
|
||||
|
||||
### KPI / Quick Actions
|
||||
|
||||
- [ ] If changing `kpi-stat-grid.tsx` / `quick-action-grid.tsx`, note sibling-app sync (vps / CFDM / fw / auth-portal)
|
||||
@@ -180,12 +180,13 @@ jobs:
|
||||
node-version: "22"
|
||||
- name: Enable pnpm via corepack
|
||||
run: corepack enable
|
||||
- name: pnpm install, typecheck, lint, build
|
||||
- name: pnpm install, typecheck, lint, test, build
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm --filter @evobgp/web run typecheck
|
||||
pnpm --filter @evobgp/web run lint
|
||||
pnpm --filter @evobgp/web run test
|
||||
pnpm --filter @evobgp/web run build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { normalizeApiToken, DEV_API_TOKEN } from '@/lib/api-client'
|
||||
import {
|
||||
aggregateNetworkMetrics,
|
||||
moduleNameById,
|
||||
runningJobCount,
|
||||
} from '@/queries/overview'
|
||||
import type { JobRow, ModuleRow, PeerRow, SpeakerRow } from '@/types/api'
|
||||
|
||||
describe('normalizeApiToken', () => {
|
||||
it('trims and strips Bearer prefix', () => {
|
||||
expect(normalizeApiToken(' Bearer dev ')).toBe('dev')
|
||||
expect(normalizeApiToken(DEV_API_TOKEN)).toBe('dev')
|
||||
})
|
||||
})
|
||||
|
||||
describe('overview selectors', () => {
|
||||
it('runningJobCount counts queued and running', () => {
|
||||
const jobs = [
|
||||
{ status: 'queued' },
|
||||
{ status: 'running' },
|
||||
{ status: 'succeeded' },
|
||||
] as JobRow[]
|
||||
expect(runningJobCount(jobs)).toBe(2)
|
||||
})
|
||||
|
||||
it('moduleNameById maps ids', () => {
|
||||
const modules = [
|
||||
{ id: 'a', name: 'Alpha' },
|
||||
{ id: 'b', name: 'Beta' },
|
||||
] as ModuleRow[]
|
||||
expect(moduleNameById(modules).get('a')).toBe('Alpha')
|
||||
})
|
||||
|
||||
it('aggregateNetworkMetrics sums peers and speakers', () => {
|
||||
const peers = [
|
||||
{ enabled: true, session_state: 'Established', session_mismatch: false },
|
||||
{ enabled: false, session_state: 'Idle', session_mismatch: true },
|
||||
] as PeerRow[]
|
||||
const speakers = [{ live: { agent_ok: true } }, { live: { agent_ok: false } }] as SpeakerRow[]
|
||||
const m = aggregateNetworkMetrics(peers, speakers)
|
||||
expect(m.peersTotal).toBe(2)
|
||||
expect(m.peersEnabled).toBe(1)
|
||||
expect(m.peersEstablished).toBe(1)
|
||||
expect(m.peersMismatch).toBe(1)
|
||||
expect(m.speakersOnline).toBe(1)
|
||||
})
|
||||
})
|
||||
@@ -23,6 +23,8 @@
|
||||
| [remote-speakers.md](remote-speakers.md) | Удалённые BGP-реплики: Traefik, agent sync, compose |
|
||||
| [releasing.md](releasing.md) | Автоматические релизы, Conventional Commits, CI |
|
||||
| [ui-design-contract.md](ui-design-contract.md) | UI SoT: ReUI Frame, kit, KPI hybrid |
|
||||
| [kpi-cross-app-sync.md](kpi-cross-app-sync.md) | Синхронизация KPI/QuickActions DNA с sibling-apps |
|
||||
| [adr/](adr/) | Architecture Decision Records |
|
||||
| [openapi.yaml](openapi.yaml) | Источник правды по контракту API |
|
||||
| [OPENAPI-GITEA.md](OPENAPI-GITEA.md) | Как открыть HTML-документацию API (в т.ч. из Gitea) |
|
||||
| [evobgp-api-sketches.md](evobgp-api-sketches.md) | Ранний черновик идей API (контекст, не замена OpenAPI) |
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# ADR-001: Durable job queue via PostgreSQL job_audit
|
||||
|
||||
## Status
|
||||
|
||||
Accepted (2026-07)
|
||||
|
||||
## Context
|
||||
|
||||
`jobs.Registry` is in-process. ARCH-04 notes workers do not share memory across API replicas. Jobs were already audited to `job_audit`, but orphans after restart were not reclaimed.
|
||||
|
||||
## Decision
|
||||
|
||||
Keep in-process execution for latency; add durable reclaim:
|
||||
|
||||
1. Persist lifecycle via existing `SetPersistHooks` → `job_audit`.
|
||||
2. Periodically `ReclaimStaleRunning` + `ClaimQueued` with `FOR UPDATE SKIP LOCKED`.
|
||||
3. `Registry.Adopt` injects claimed rows into the local worker pool.
|
||||
4. Claim grace (`EVOBGP_JOB_CLAIM_GRACE`, default 30s) avoids double-run of fresh local enqueues.
|
||||
|
||||
NATS JetStream remains optional (`EVOBGP_BROKER_URL` logged only).
|
||||
|
||||
## Consequences
|
||||
|
||||
Two `evobgp-api` processes can reclaim orphaned work. Fresh jobs still run on the enqueueing process. Full broker-based fan-out is out of scope for this ADR.
|
||||
@@ -0,0 +1,20 @@
|
||||
# ADR-002: OpenAPI → TypeScript codegen
|
||||
|
||||
## Status
|
||||
|
||||
Accepted (2026-07)
|
||||
|
||||
## Context
|
||||
|
||||
HTTP SoT is `docs/openapi.yaml`. Frontend DTOs in `apps/web/src/types/api.ts` were hand-maintained and drifted.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Generate `apps/web/src/types/api.gen.ts` with `openapi-typescript` (`pnpm --filter @evobgp/web run openapi:gen`).
|
||||
2. Keep `api.ts` as UI-facing aliases; re-export `OpenAPISchemas` for gradual adoption.
|
||||
3. CI (`scripts/check-openapi-gen.sh`) fails when gen is stale vs OpenAPI.
|
||||
4. `packages/shared` deferred until Zod DTOs are needed cross-app.
|
||||
|
||||
## Consequences
|
||||
|
||||
PRs that change OpenAPI must regenerate types. Hand DTOs remain until routes migrate to `api.gen` schemas.
|
||||
@@ -0,0 +1,17 @@
|
||||
# ADR-003: Removal of web-legacy-svelte
|
||||
|
||||
## Status
|
||||
|
||||
Accepted (2026-07)
|
||||
|
||||
## Context
|
||||
|
||||
`web-legacy-svelte/` (~294 files) confused agents/codegraph after the React + ReUI migration to `apps/web/`.
|
||||
|
||||
## Decision
|
||||
|
||||
Delete `web-legacy-svelte/` from `main`. Historical reference remains in git history. Docs and `AGENTS.md` point only to `apps/web/`.
|
||||
|
||||
## Consequences
|
||||
|
||||
No Svelte imports allowed. Restore from git history if a one-off migration reference is needed.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Cross-app KPI / Quick Actions sync
|
||||
|
||||
EvoBGP is the **source of truth** for hybrid KPI and Quick Actions markup:
|
||||
|
||||
- `apps/web/src/components/reui-kit/kpi-stat-grid.tsx`
|
||||
- `apps/web/src/components/reui-kit/quick-action-grid.tsx`
|
||||
|
||||
Preview: https://reui.io/preview/base/stats-12 · https://reui.io/preview/base/card-12
|
||||
|
||||
## Process
|
||||
|
||||
1. Change markup/tokens only in EvoBGP first.
|
||||
2. Diff against sibling apps (`@cfdm/ui` / `@evofw/ui` / `@authportal/ui` import prefixes only):
|
||||
- `vps-tracker/apps/web/src/components/reui-kit/kpi-stat-grid.tsx`
|
||||
- `cloudflare-domain-manager/.../kpi-stat-grid.tsx`
|
||||
- `EvoFirewall/.../kpi-stat-grid.tsx`
|
||||
- `auth-portal/.../kpi-stat-grid.tsx`
|
||||
3. Port structural/class changes; keep package alias (`@evobgp/ui` → `@scope/ui`).
|
||||
4. Mention sync in the PR checklist when EvoBGP kit DNA changes.
|
||||
Reference in New Issue
Block a user