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:
176
internal/acme/eab_test.go
Normal file
176
internal/acme/eab_test.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreateEAB(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
cred, err := h.CreateEAB(ctx, "alice")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateEAB() error: %v", err)
|
||||
}
|
||||
if cred.KID == "" {
|
||||
t.Fatalf("expected non-empty KID")
|
||||
}
|
||||
if len(cred.HMACKey) != 32 {
|
||||
t.Fatalf("expected 32-byte HMAC key, got %d bytes", len(cred.HMACKey))
|
||||
}
|
||||
if cred.Used {
|
||||
t.Fatalf("expected Used=false for new credential")
|
||||
}
|
||||
if cred.CreatedBy != "alice" {
|
||||
t.Fatalf("expected CreatedBy=alice, got %s", cred.CreatedBy)
|
||||
}
|
||||
if cred.CreatedAt.IsZero() {
|
||||
t.Fatalf("expected non-zero CreatedAt")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEAB(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
created, err := h.CreateEAB(ctx, "bob")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateEAB() error: %v", err)
|
||||
}
|
||||
|
||||
got, err := h.GetEAB(ctx, created.KID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEAB() error: %v", err)
|
||||
}
|
||||
if got.KID != created.KID {
|
||||
t.Fatalf("KID mismatch: got %s, want %s", got.KID, created.KID)
|
||||
}
|
||||
if got.CreatedBy != "bob" {
|
||||
t.Fatalf("CreatedBy mismatch: got %s, want bob", got.CreatedBy)
|
||||
}
|
||||
if len(got.HMACKey) != 32 {
|
||||
t.Fatalf("expected 32-byte HMAC key, got %d bytes", len(got.HMACKey))
|
||||
}
|
||||
if got.Used != false {
|
||||
t.Fatalf("expected Used=false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEABNotFound(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := h.GetEAB(ctx, "nonexistent-kid")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for non-existent KID, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkEABUsed(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
cred, err := h.CreateEAB(ctx, "carol")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateEAB() error: %v", err)
|
||||
}
|
||||
if cred.Used {
|
||||
t.Fatalf("expected Used=false before marking")
|
||||
}
|
||||
|
||||
if err := h.MarkEABUsed(ctx, cred.KID); err != nil {
|
||||
t.Fatalf("MarkEABUsed() error: %v", err)
|
||||
}
|
||||
|
||||
got, err := h.GetEAB(ctx, cred.KID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEAB() after mark error: %v", err)
|
||||
}
|
||||
if !got.Used {
|
||||
t.Fatalf("expected Used=true after marking, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAccountsEmpty(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
accounts, err := h.ListAccounts(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("ListAccounts() error: %v", err)
|
||||
}
|
||||
if len(accounts) != 0 {
|
||||
t.Fatalf("expected 0 accounts, got %d", len(accounts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAccounts(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Store two accounts directly in the barrier.
|
||||
for i, name := range []string{"user-a", "user-b"} {
|
||||
acc := &Account{
|
||||
ID: name,
|
||||
Status: StatusValid,
|
||||
Contact: []string{"mailto:" + name + "@example.com"},
|
||||
JWK: []byte(`{"kty":"EC"}`),
|
||||
CreatedAt: time.Now(),
|
||||
MCIASUsername: name,
|
||||
}
|
||||
data, err := json.Marshal(acc)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal account %d: %v", i, err)
|
||||
}
|
||||
path := h.barrierPrefix() + "accounts/" + name + ".json"
|
||||
if err := h.barrier.Put(ctx, path, data); err != nil {
|
||||
t.Fatalf("store account %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
accounts, err := h.ListAccounts(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("ListAccounts() error: %v", err)
|
||||
}
|
||||
if len(accounts) != 2 {
|
||||
t.Fatalf("expected 2 accounts, got %d", len(accounts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOrders(t *testing.T) {
|
||||
h := testHandler(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Store two orders directly in the barrier.
|
||||
for i, id := range []string{"order-1", "order-2"} {
|
||||
order := &Order{
|
||||
ID: id,
|
||||
AccountID: "test-account",
|
||||
Status: StatusPending,
|
||||
Identifiers: []Identifier{{Type: IdentifierDNS, Value: "example.com"}},
|
||||
AuthzIDs: []string{"authz-1"},
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
CreatedAt: time.Now(),
|
||||
IssuerName: "test-issuer",
|
||||
}
|
||||
data, err := json.Marshal(order)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal order %d: %v", i, err)
|
||||
}
|
||||
path := h.barrierPrefix() + "orders/" + id + ".json"
|
||||
if err := h.barrier.Put(ctx, path, data); err != nil {
|
||||
t.Fatalf("store order %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
orders, err := h.ListOrders(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("ListOrders() error: %v", err)
|
||||
}
|
||||
if len(orders) != 2 {
|
||||
t.Fatalf("expected 2 orders, got %d", len(orders))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user