feat(server): /api/info aggregator + frontend version footer

Sprint 2 of autonomous trainer day 2026-05-05. Mistral-implemented
through ICM workspace ship-info-aggregator (bootstrapped backend +
BDD before hitting price limit at stage 02), Claude-completed for
frontend + Playwright + verifier + PR.

Backend:
- GET /api/info aggregator returning version, commit_short, build_date,
  uptime_seconds, cache_enabled, healthz_status (single round trip)
- Optional cache via existing cache service (X-Cache: HIT/MISS)
- BDD scenario @critical covers happy path + version regex; cache
  scenario kept under @skip @bdd-deferred until BDD harness gains a
  cache-enabled mode

Frontend:
- AppFooterView (dumb) + AppFooter (smart wrapper, useFetch) following
  the HealthDashboard / HealthDashboardView pattern
- layouts/default.vue auto-applied via NuxtLayout in app.vue
- humaniseUptime helper in utils/
- Playwright tests use route.fulfill mocking (decoupled from dev-proxy
  infra), assert visible AND content (PR #32 lesson)

Docs:
- documentation/API.md /api/info entry with schema and rationale
- ADR-0026 documents composite endpoint vs separate calls choice

Verifier verdict (skill-driven, audit at stage 04): APPROVE_WITH_NITS.
Nits: handleInfo is 51 lines (could split into builder + emitter);
X-Cache: DISABLED could improve ops clarity.

Out-of-scope follow-up: existing tests/e2e/health.spec.ts happy path
hits the same dev-proxy infra issue as my footer happy path before
mocking. Same fix (server: false + route.fulfill) would apply.
This commit is contained in:
2026-05-05 08:28:00 +02:00
parent 4a3f1bb138
commit 4d2e0c1a42
16 changed files with 587 additions and 1 deletions

View File

@@ -0,0 +1,38 @@
# features/info/info.feature
@info @critical
Feature: Info Endpoint
The /api/info endpoint should return composite application information
@basic @critical
Scenario: GET /api/info returns all required fields
Given the server is running
When I request the info endpoint
Then the status code should be 200
And the response should be JSON
And the response should contain "version"
And the response should contain "commit_short"
And the response should contain "build_date"
And the response should contain "uptime_seconds"
And the response should contain "cache_enabled"
And the response should contain "healthz_status"
And the "healthz_status" field should equal "healthy"
@version @critical
Scenario: version field matches semantic version pattern
Given the server is running
When I request the info endpoint
Then the status code should be 200
And the "version" field should match /^\d+\.\d+\.\d+$/
@cache @skip @bdd-deferred
Scenario: /api/info is cached when cache is enabled
# Deferred: the BDD testsetup currently runs with cache disabled
# (see "Cache service disabled" in test logs). Cache HIT/MISS behavior
# is covered by unit tests on the cache service. Reopen this scenario
# if/when the BDD harness gains a cache-enabled mode (likely after
# ADR-0022 Phase 2).
Given the server is running with cache enabled
When I request the info endpoint
Then the response header "X-Cache" should be "MISS"
When I request the info endpoint again
Then the response header "X-Cache" should be "HIT"

View File

@@ -0,0 +1,16 @@
package info
import (
"testing"
"dance-lessons-coach/pkg/bdd/testsetup"
)
func TestInfoBDD(t *testing.T) {
config := testsetup.NewFeatureConfig("info", "progress", false)
suite := testsetup.CreateTestSuite(t, config, "dance-lessons-coach BDD Tests - Info Feature")
if suite.Run() != 0 {
t.Fatal("non-zero status returned, failed to run info BDD tests")
}
}