diff --git a/.github/workflows/ci-dashboard.yml b/.github/workflows/ci-dashboard.yml index bc22f16..dc8a9d1 100644 --- a/.github/workflows/ci-dashboard.yml +++ b/.github/workflows/ci-dashboard.yml @@ -32,6 +32,8 @@ jobs: run: npm install - name: Lint run: npm run lint + - name: Test + run: npm run test - name: Build run: npm run build env: diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f04d7..89f5ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,30 @@ Versions map to GitHub pull requests; the project does not use semver tags yet. --- +## [0.12.0] - 2026-03-24 - PR #13: Documentation refresh + +### Changed + +- `README.md`: Go badge 1.22 → 1.23; stack table updated; `npm install` step + added to Quick Start; project structure updated with all new dirs and files; + CI/CD diagram reflects Vitest step and build-on-PR behaviour. +- `ROADMAP.md`: status line updated; Milestone 4 reflects Husky and build-on-PR + fixes; dashboard moved from v2.0 backlog to a completed Milestone 8. +- `docs/development/GETTING_STARTED.md`: Go 1.22 → 1.23; Node.js prerequisite + added; `npm install` step added; ports table expanded with all 16 containers. +- `docs/deployment/CI_CD_GUIDE.md`: pipeline diagram and workflow table include + `ci-subscription.yml`, `ci-dashboard.yml`, and `ci-e2e.yml`; Docker build + behaviour clarified (build on PRs, push on main only). +- `docs/architecture/DECISIONS.md`: ADR-016 `next.config.ts` → `next.config.mjs`. + +### Added + +- `docs/architecture/DECISIONS.md`: ADR-017 documenting the Vitest + + `@testing-library/react` + `happy-dom` decision for dashboard testing. +- `ci-dashboard.yml`: `npm run test` step added before `next build`. + +--- + ## [0.11.0] - 2026-03-24 - PR #12: Vitest unit and integration tests for Next.js dashboard ### Added diff --git a/README.md b/README.md index a489bfd..0817c6b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Functionally simple. Architecturally serious.

-[![Go](https://img.shields.io/badge/Go-1.22-00ADD8?logo=go)](https://golang.org) +[![Go](https://img.shields.io/badge/Go-1.23-00ADD8?logo=go)](https://golang.org) [![License](https://img.shields.io/github/license/KaelSensei/OnChainHealthMonitor)](LICENSE) [![CI - api](https://github.com/KaelSensei/OnChainHealthMonitor/actions/workflows/ci-api.yml/badge.svg)](https://github.com/KaelSensei/OnChainHealthMonitor/actions/workflows/ci-api.yml) @@ -195,7 +195,7 @@ All services expose: | Theme | Tool | Why | |----------------------------|-----------------------------|----------------------------------------------------------| -| Language (backend) | Go 1.22 | Fast, minimal stdlib, perfect for microservices | +| Language (backend) | Go 1.23 | Fast, minimal stdlib, perfect for microservices | | Language (frontend) | TypeScript + Next.js 14 | App Router enables SSR for initial data; client components for real-time updates; API routes as BFF proxy | | Message Broker | Apache Kafka (KRaft) | High-throughput event streaming; decouples all services; replay support | | User notification routing | RabbitMQ | Topic exchange pattern routes alerts to specific users; auto-delete queues per connected client | @@ -203,7 +203,7 @@ All services expose: | Containers | Docker + Docker Compose | Reproducible local environment, mirrors prod topology | | Observability: Metrics | Prometheus + Grafana | Industry standard; scrape model fits pull-based services | | Observability: Tracing | OpenTelemetry + OTel Collector + Jaeger | OTLP gRPC pipeline: services -> collector -> Jaeger UI | -| CI/CD | GitHub Actions | Native to GitHub, path-based triggers for monorepos | +| CI/CD | GitHub Actions + Husky | GHA for remote; Husky for local commit-msg + pre-commit hooks | | Reliability / Alerting | Grafana Alerting | Unified alerting with SLO-based rules, no extra infra | | API Gateway | Kong (open-source) | Plugin ecosystem (rate limit, auth, logging) on OSS | | Infra as Code | Terraform | Declarative, provider-agnostic, auditable history | @@ -230,6 +230,9 @@ See [Infrastructure Guide](docs/deployment/INFRASTRUCTURE_GUIDE.md) for full det git clone https://github.com/KaelSensei/OnChainHealthMonitor.git cd OnChainHealthMonitor +# Install dev tooling (Husky hooks, commitlint, lint-staged) +npm install + # Start the full stack docker-compose up --build @@ -322,7 +325,8 @@ OnChainHealthMonitor/ │ ├── notifier/ # Alert engine │ ├── api/ # Public REST API │ └── subscription/ # Subscription CRUD + WebSocket alert delivery -├── dashboard/ # Next.js 14 frontend (protocol health + subscriptions) +├── dashboard/ # Next.js 14 frontend (protocol health + subscriptions + 55 Vitest tests) +├── e2e/ # End-to-end smoke test suite ├── infra/ │ ├── terraform/ # GCP/GKE infrastructure as code │ ├── helm/ # Helm charts per service @@ -333,11 +337,14 @@ OnChainHealthMonitor/ │ ├── otel/ # OpenTelemetry collector config │ └── jaeger/ # Jaeger configuration ├── docs/ -│ ├── architecture/ # ARCHITECTURE.md + ADRs +│ ├── architecture/ # ARCHITECTURE.md + 16 ADRs │ ├── deployment/ # Infrastructure & CI/CD guides -│ └── development/ # Onboarding guide +│ └── development/ # Onboarding, contributing, tracing guides +├── scripts/ # Developer utility scripts ├── .github/ -│ └── workflows/ # GitHub Actions pipelines +│ └── workflows/ # GitHub Actions pipelines (10 workflows) +├── .husky/ # Git hooks: commit-msg + pre-commit +├── package.json # Root dev tooling: Husky, commitlint, lint-staged ├── docker-compose.yml └── README.md ``` @@ -361,7 +368,8 @@ graph LR Build["go build"] NpmLint["npm run lint"] NpmBuild["npm run build"] - Docker["docker build\n+ push GHCR"] + NpmTest["npm run test"] + Docker["docker build (all branches)\n+ push GHCR (main only)"] end subgraph Infra["Infra validation"] @@ -378,7 +386,7 @@ graph LR Commit --> CommitLint Commit --> MDLint Commit --> Vet --> Static --> Test --> Build --> Docker - Commit --> NpmLint --> NpmBuild --> Docker + Commit --> NpmLint --> NpmBuild --> NpmTest --> Docker Commit --> Compose Commit --> Kong Commit --> OApi diff --git a/ROADMAP.md b/ROADMAP.md index a18748e..af95e38 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # OnChain Health Monitor - Roadmap -## Status: 🟢 v1.1 - Kafka event pipeline complete +## Status: 🟢 v1.1 - Event-driven pipeline + dashboard complete --- @@ -38,10 +38,11 @@ - [x] GitHub Actions: `go vet` + `staticcheck` + `go test -race` + Docker build per service - [x] Path-based triggers (only rebuilds the changed service) -- [x] Push to GHCR on merge to `main` -- [x] Semantic version releases on `v*.*.*` tags (matrix build across all 4 services) +- [x] Docker build runs on every PR; push to GHCR on merge to `main` only +- [x] Semantic version releases on `v*.*.*` tags (matrix build across all services) - [x] PR quality checks: commitlint + markdownlint -- [ ] Deploy to Kubernetes via Helm on merge to main _(planned v1.1)_ +- [x] Husky local hooks: `commit-msg` (commitlint) + `pre-commit` (lint-staged) +- [ ] Deploy to Kubernetes via Helm on merge to main _(planned v1.2)_ ## ✅ Milestone 5 - Infrastructure as Code @@ -120,10 +121,14 @@ Complete the three pillars of observability - metrics (Prometheus) and traces (J - [ ] Export k6 results to Prometheus remote write → Grafana dashboard for trend analysis - [ ] Define error budget burn rate alerts based on load test SLO targets -## 🔜 v2.0 - Next.js Dashboard +## ✅ Milestone 8 - Next.js Dashboard -- [ ] Next.js dashboard with real-time protocol health feed and subscription management UI -- [ ] Historical health score API (`GET /api/v1/protocols/{id}/history`) +- [x] Next.js 14 App Router dashboard (port 3001) +- [x] Protocol health feed with live score polling every 5s +- [x] Subscription management UI (create / list / delete) +- [x] Real-time alert panel via WebSocket +- [x] 55 Vitest unit + integration tests (components + API route handlers) +- [x] `CI - dashboard` workflow: ESLint lint + `next build` + Vitest + Docker build ## 🔜 v2.1 - Real On-Chain Indexing diff --git a/docs/architecture/DECISIONS.md b/docs/architecture/DECISIONS.md index f905b64..1fd41e6 100644 --- a/docs/architecture/DECISIONS.md +++ b/docs/architecture/DECISIONS.md @@ -419,7 +419,7 @@ Key design choices: - **WebSocket connects browser-direct** to the subscription service on port 8084 (`ws://{hostname}:8084/ws`). Proxying WebSocket through Next.js would require a custom server and is not worth the complexity for this scope. - **User identity via localStorage UUID** - no authentication layer required; a random UUID is generated on first visit and persisted, giving each browser session a stable user ID for subscription management. - **Port 3001** - port 3000 is already occupied by Grafana in the Compose stack. -- **Standalone output** (`output: 'standalone'` in `next.config.ts`) - produces a self-contained Node.js server without `node_modules`, keeping the Docker image small. +- **Standalone output** (`output: 'standalone'` in `next.config.mjs`) - produces a self-contained Node.js server without `node_modules`, keeping the Docker image small. **Consequences:** - ✅ Initial page load delivers a fully rendered protocol grid with no loading flash @@ -428,3 +428,45 @@ Key design choices: - ✅ Standalone Docker output means the runtime image is `node:20-alpine` + a ~50MB `.next/standalone` directory - ⚠️ WebSocket URL is derived from `window.location.hostname` at runtime; in a production deployment behind a reverse proxy, port 8084 must be exposed or the WebSocket traffic routed through the proxy - ⚠️ Protocol polling is client-side every 5 s; at scale, server-sent events or a Next.js streaming response would be more efficient + +--- + +## ADR-017: Vitest for dashboard testing + +**Date:** 2026-03-24 +**Status:** Accepted + +**Context:** +The Next.js dashboard needed a test suite covering React components and API route handlers. The existing Go services use the standard `go test` runner; the frontend needed an equivalent that integrates with the TypeScript and JSX build pipeline. + +Alternatives considered: + +| Option | Rejected because | +|---|---| +| Jest | Requires additional Babel transform config for ESM + Next.js; slower cold start | +| Playwright (only) | E2E tests only; cannot test components in isolation or mock fetch calls | +| No tests | Unacceptable; components and API routes have non-trivial logic | + +**Decision:** +Use Vitest with `@testing-library/react` and `happy-dom`. + +- **Vitest** shares the Vite transform pipeline so TypeScript + JSX work without extra config. +- **`@testing-library/react`** encourages testing from the user's perspective (rendered output, interactions) rather than implementation details. +- **`happy-dom`** is a lightweight DOM implementation that is significantly faster than `jsdom` for unit tests that do not need a full browser environment. +- API route handlers are tested directly (imported and called), with `fetch` mocked via `vi.spyOn(global, 'fetch')`. + +**Test coverage:** + +| Suite | Tests | What is covered | +|---|---|---| +| `ProtocolCard` | 13 | Health state rendering, score display, protocol metadata | +| `AlertFeed` | 12 | WebSocket lifecycle, incoming messages, empty state | +| `SubscriptionPanel` | 15 | Create/list/delete flows, threshold validation, error states | +| API `/api/protocols` | 7 | Backend proxy, error handling, response shape | +| API `/api/subscriptions` | 8 | Full CRUD route handlers | + +**Consequences:** +- ✅ `npm run test` runs all 55 tests in under 3 seconds +- ✅ No Babel config needed; Vitest uses the same esbuild transform as the Next.js build +- ✅ Tests run in CI as part of `ci-dashboard.yml` before the Docker build step +- ⚠️ `happy-dom` does not support all browser APIs; tests requiring `WebSocket` or `localStorage` need explicit mocking diff --git a/docs/deployment/CI_CD_GUIDE.md b/docs/deployment/CI_CD_GUIDE.md index debff3e..958a9b7 100644 --- a/docs/deployment/CI_CD_GUIDE.md +++ b/docs/deployment/CI_CD_GUIDE.md @@ -20,16 +20,22 @@ GitHub Actions detects the push │ commitlint + markdownlint │ ├─ services/api/** changed? ──► ci-api.yml - │ go vet → staticcheck → go test → docker build → push to GHCR + │ go vet → staticcheck → go test → docker build (PR) / push GHCR (main) │ ├─ services/collector/** changed? ──► ci-collector.yml - │ go vet → staticcheck → go test → docker build → push to GHCR + │ go vet → staticcheck → go test → docker build (PR) / push GHCR (main) │ ├─ services/analyzer/** changed? ──► ci-analyzer.yml - │ go vet → staticcheck → go test → docker build → push to GHCR + │ go vet → staticcheck → go test → docker build (PR) / push GHCR (main) │ ├─ services/notifier/** changed? ──► ci-notifier.yml - │ go vet → staticcheck → go test → docker build → push to GHCR + │ go vet → staticcheck → go test → docker build (PR) / push GHCR (main) + │ + ├─ services/subscription/** changed? ──► ci-subscription.yml + │ go vet → staticcheck → go test → docker build (PR) / push GHCR (main) + │ + ├─ dashboard/** changed? ──► ci-dashboard.yml + │ eslint → next build → vitest → docker build (PR) / push GHCR (main) │ ├─ docker-compose / Kong / OpenAPI changed? ──► ci-infra.yml │ validate configs @@ -46,11 +52,14 @@ All workflows live in `.github/workflows/`. Here is what each one does: | File | Trigger | What it does | |------|---------|--------------| -| `ci-api.yml` | Push/PR touching `services/api/**` | Lint, test, build, push `api` image | -| `ci-collector.yml` | Push/PR touching `services/collector/**` | Lint, test, build, push `collector` image | -| `ci-analyzer.yml` | Push/PR touching `services/analyzer/**` | Lint, test, build, push `analyzer` image | -| `ci-notifier.yml` | Push/PR touching `services/notifier/**` | Lint, test, build, push `notifier` image | +| `ci-api.yml` | Push/PR touching `services/api/**` | Lint, test, build image (push to GHCR on main only) | +| `ci-collector.yml` | Push/PR touching `services/collector/**` | Lint, test, build image (push to GHCR on main only) | +| `ci-analyzer.yml` | Push/PR touching `services/analyzer/**` | Lint, test, build image (push to GHCR on main only) | +| `ci-notifier.yml` | Push/PR touching `services/notifier/**` | Lint, test, build image (push to GHCR on main only) | +| `ci-subscription.yml` | Push/PR touching `services/subscription/**` | Lint, test, build image (push to GHCR on main only) | +| `ci-dashboard.yml` | Push/PR touching `dashboard/**` | ESLint, next build, vitest, build image (push to GHCR on main only) | | `ci-infra.yml` | Push/PR touching infra files | Validate docker-compose, Kong config, OpenAPI spec | +| `ci-e2e.yml` | Push/PR touching `e2e/**` | End-to-end smoke tests | | `release.yml` | Push of a `v*.*.*` tag | Matrix build of all services with semver tags | | `pr-checks.yml` | Every pull request | commitlint + markdownlint | diff --git a/docs/development/GETTING_STARTED.md b/docs/development/GETTING_STARTED.md index 5099d05..df09b1c 100644 --- a/docs/development/GETTING_STARTED.md +++ b/docs/development/GETTING_STARTED.md @@ -8,7 +8,8 @@ This guide walks you through setting up the project locally and verifying everyt | Tool | Minimum version | Install | |------|----------------|---------| -| Go | 1.22 | https://go.dev/dl/ | +| Go | 1.23 | https://go.dev/dl/ | +| Node.js | 20.x | https://nodejs.org/ (for dashboard dev + Husky hooks) | | Docker | 24.x | https://docs.docker.com/get-docker/ | | Docker Compose | v2.x (`docker compose`) | Bundled with Docker Desktop; or `apt install docker-compose-plugin` | | Make | any | Usually pre-installed; `brew install make` or `apt install make` | @@ -24,19 +25,24 @@ This guide walks you through setting up the project locally and verifying everyt git clone https://github.com/KaelSensei/OnChainHealthMonitor.git cd OnChainHealthMonitor -# Build all 4 services and start the full stack (7 containers) +# Install dev tooling (activates Husky hooks automatically via prepare script) +npm install + +# Build all services and start the full stack docker compose up --build ``` -The first build downloads base images and compiles the Go binaries - expect ~2 minutes. +The first build downloads base images and compiles the Go binaries - expect ~2 minutes. On subsequent runs, `docker compose up` (without `--build`) starts in seconds. You should see log lines like: + ``` -onchain_collector | [collector] Starting collector service on :8081 (mock mode) -onchain_analyzer | [analyzer] Starting analyzer service on :8082 -onchain_notifier | [notifier] Starting notifier service on :8083 (alert threshold: score < 30) -onchain_api | [api] Starting API service on :8080 +onchain_collector | [collector] Starting collector service on :8081 (mock mode) +onchain_analyzer | [analyzer] Starting analyzer service on :8082 +onchain_notifier | [notifier] Starting notifier service on :8083 (alert threshold: score < 30) +onchain_api | [api] Starting API service on :8080 +onchain_subscription | [subscription] Starting subscription service on :8084 ``` --- @@ -46,14 +52,20 @@ onchain_api | [api] Starting API service on :8080 | Service | Container name | Port | Purpose | |---------|---------------|------|---------| | `api` | `onchain_api` | `8080` | Public REST API | -| `collector` | `onchain_collector` | `8081` | Mock event generator | +| `collector` | `onchain_collector` | `8081` | Mock DeFi event generator | | `analyzer` | `onchain_analyzer` | `8082` | Health score computation | -| `notifier` | `onchain_notifier` | `8083` | Alert engine | +| `notifier` | `onchain_notifier` | `8083` | Alert engine + RabbitMQ routing | +| `subscription` | `onchain_subscription` | `8084` | Subscription CRUD + WebSocket alerts | +| `dashboard` | `onchain_dashboard` | `3001` | Next.js frontend | +| Kafka | `onchain_kafka` | `9092` | Event streaming (KRaft mode) | +| RabbitMQ | `onchain_rabbitmq` | `5672` / `15672` | Per-user alert routing / management UI | +| Redis | `onchain_redis` | `6379` | Subscription storage | | Prometheus | `onchain_prometheus` | `9090` | Metrics scraping + query UI | | Grafana | `onchain_grafana` | `3000` | Dashboards (admin / admin) | | Jaeger | `onchain_jaeger` | `16686` | Distributed trace UI | -| OTel Collector | `onchain_otel_collector` | `4317` | OTLP gRPC receiver (used by services internally) | -| OTel Collector | `onchain_otel_collector` | `4318` | OTLP HTTP receiver | +| Kong | `onchain_kong` | `8000` / `8001` | API gateway proxy / admin | +| Swagger UI | `onchain_swagger` | `8090` | Interactive API docs | +| OTel Collector | `onchain_otel_collector` | `4317` | OTLP gRPC receiver | | OTel Collector | `onchain_otel_collector` | `55679` | zpages debug interface | ---