The Phase 6 e2e scaffold landed in a4df1f8 with t.Skip stubs for the
five harness primitives that the test needed but the integration_test.go
suite already provided. This commit replaces the stubs with real
implementations so TestCRLOCSPLifecycle + TestCRLOCSPPostEndpoint
actually exercise the CRL/OCSP backend end-to-end against a running
docker-compose.test.yml stack.
Wired helpers:
* issueLocalCert(commonName) → POSTs /api/v1/certificates against
iss-local with the test stack's seeded owner/team/policy/profile,
triggers /renew, waits for jobs via the existing waitForJobsDone
helper, GETs /versions, parses pem_chain into leaf + issuer CA.
Returns (leaf, pemChain, hexSerial). Records the cert ID in a
package-level registry keyed by hex serial.
* revokeCertViaAPI(hexSerial, reason) → resolves hex serial to
certctl cert ID via the registry (the API keys revocation by
cert ID, not X.509 serial) and POSTs /revoke with the RFC 5280
reason code.
* fetchCACert(issuerID) → returns the issuing CA from any cert
previously issued via issueLocalCert (chain[1], or chain[0] for
self-signed test root). Falls back to a just-in-time issuance if
the registry is empty so the helper is callable from any phase.
* requireServerReady → polls GET /health (the unauthenticated
Bearer-free liveness route from router.go) until 200 OK or 30s.
* serverBaseURL → returns the harness's serverURL package var
(CERTCTL_TEST_SERVER_URL, defaulting to https://localhost:8443).
* httpClient → returns newUnauthHTTPClient (TLS-trust-aware, no
Bearer) since /.well-known/pki/{crl,ocsp}/ run unauthenticated by
design (M-006: relying parties must validate revocation without
API keys).
New helper:
* parsePEMChain — decodes a PEM bundle into [leaf, issuer]. Handles
the self-signed-root edge case by returning the leaf twice rather
than nil. Used by issueLocalCert to populate the registry.
Constants block at top of file pins the test-stack identifiers
(iss-local, owner-test-admin, team-test-ops, rp-default,
prof-test-tls) — these match deploy/docker-compose.test.yml seed
data so the suite stays in sync with what the stack actually serves.
Verification (sandbox — Docker not available so the test bodies
themselves can't run here, but the static checks pass):
- gofmt: clean
- go vet -tags integration ./deploy/test/...: clean
- go test -tags integration -list '.*' ./deploy/test/...: lists
TestCRLOCSPLifecycle + TestCRLOCSPPostEndpoint among the existing
suite tests, confirming the file compiles + binds correctly.
CI runs the full suite via docker-compose.test.yml in the standard
integration-test workflow. Local repro per the file header doc:
cd deploy && docker compose -f docker-compose.test.yml up --build -d
cd deploy/test && go test -tags integration -v -run TestCRLOCSP \
-timeout 10m ./...
Phase 5 (admin endpoint slice) + Phase 6 (e2e test stub) of the
CRL/OCSP responder bundle. Closes the deferred items from the
backend-slice merge (77d6326).
What landed:
Phase 5 — admin observability:
* GET /api/v1/admin/crl/cache (handler.AdminCRLCacheHandler):
- Per-issuer cache state + most recent N generation events
- Admin-gated via middleware.IsAdmin (M-003 pattern); non-admin
callers get 403 + the service is never invoked
- Reveals issuer set + CRL cadence, hence the gate
- Returns CachePresent=false rows for never-generated issuers so
the GUI can show 'not yet generated' instead of 404
- Per-issuer Get failures decorate the row's RecentEvents rather
than failing the whole response
* AdminCRLCacheServiceImpl: thin handler-side composition over
repository.CRLCacheRepository + an issuer-IDs callback (avoids
importing internal/service from internal/api/handler)
* M-008 admin-gate pin updated: admin_crl_cache.go added to
AdminGatedHandlers; full triplet of tests
(NonAdmin_Returns403, AdminExplicitFalse_Returns403,
AdminPermitted_ForwardsActor) + RejectsNonGetMethod +
PropagatesServiceError
* Router registration + HandlerRegistry field + main.go wiring
(callback closure over issuerRegistry.List)
* OpenAPI entry under CRL & OCSP tag
Phase 6 — e2e scaffold:
* deploy/test/crl_ocsp_e2e_test.go with TestCRLOCSPLifecycle +
TestCRLOCSPPostEndpoint
* Lifecycle test exercises issue → fetch OCSP (Good) → revoke →
wait → fetch CRL (entry present) → fetch OCSP (Revoked) →
verify dedicated responder cert + id-pkix-ocsp-nocheck
* Helpers (issueLocalCert, revokeCertViaAPI, fetchCRL, fetchOCSP,
fetchCACert) currently call t.Skip with TODO markers — sandbox
has no Docker so the harness can't be wired end-to-end here;
when CI / a fresh dev workstation runs, the implementer wires
each helper to the existing integration_test.go primitives
* Build-tagged //go:build integration so the standard go test
sweep skips it; runs via the deploy/test integration workflow
Coverage: handler 80.6% (above 75 floor; was 79.8% pre-Phase-5).
All other packages unchanged.
Backward compat: admin endpoint inert until an admin Bearer key is
configured. The e2e test stub is no-op (skips) until wired.
Deferred:
* GUI cert-detail-page revocation panel — pure frontend work, no
backend impact, separate session
* E2E test helper wiring — depends on extracting the existing
integration-test harness primitives into shared helpers; doable
in a follow-up that has Docker available
* V3-Pro polish (delta CRLs, OCSP rate-limiting, OCSP stapling)