Add comprehensive ACME test suite (60 tests, 2100 lines)

Test coverage for the entire ACME server implementation:

- helpers_test.go: memBarrier, key generation, JWS/EAB signing, test fixtures
- nonce_test.go: issue/consume lifecycle, reuse rejection, concurrency
- jws_test.go: JWS parsing/verification (ES256, ES384, RS256), JWK parsing,
  RFC 7638 thumbprints, EAB HMAC verification, key authorization
- eab_test.go: EAB credential CRUD, account/order listing
- validate_test.go: HTTP-01 challenge validation with httptest servers,
  authorization/order state machine transitions
- handlers_test.go: full ACME protocol flow via chi router — directory,
  nonce, account creation with EAB, order creation, authorization retrieval,
  challenge triggering, finalize (order-not-ready), cert retrieval/revocation,
  CSR identifier validation

One production change: extract dnsResolver variable in validate.go for
DNS-01 test injection (no behavior change).

All 60 tests pass with -race. Full project vet and test clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 21:01:23 -07:00
parent 7f9e7f433f
commit 7749c035ae
8 changed files with 2101 additions and 3 deletions

View File

@@ -14,6 +14,10 @@ import (
"time"
)
// dnsResolver is the DNS resolver used for DNS-01 challenge validation.
// It defaults to the system resolver and can be replaced in tests.
var dnsResolver = net.DefaultResolver
// validateChallenge dispatches to the appropriate validator and updates
// challenge, authorization, and order state in the barrier.
func (h *Handler) validateChallenge(ctx context.Context, chall *Challenge, accountJWK []byte) {
@@ -245,8 +249,7 @@ func validateDNS01(ctx context.Context, chall *Challenge, accountJWK []byte) err
// Strip trailing dot if present; add _acme-challenge prefix.
domain = "_acme-challenge." + domain
resolver := net.DefaultResolver
txts, err := resolver.LookupTXT(ctx, domain)
txts, err := dnsResolver.LookupTXT(ctx, domain)
if err != nil {
return fmt.Errorf("DNS-01: TXT lookup for %s failed: %w", domain, err)
}