package acme import ( "encoding/base64" "sync" "testing" ) func TestNonceIssueAndConsume(t *testing.T) { store := NewNonceStore() nonce, err := store.Issue() if err != nil { t.Fatalf("Issue() error: %v", err) } if err := store.Consume(nonce); err != nil { t.Fatalf("Consume() error: %v", err) } } func TestNonceRejectUnknown(t *testing.T) { store := NewNonceStore() err := store.Consume("never-issued-nonce") if err == nil { t.Fatalf("expected error consuming unknown nonce, got nil") } } func TestNonceRejectReuse(t *testing.T) { store := NewNonceStore() nonce, err := store.Issue() if err != nil { t.Fatalf("Issue() error: %v", err) } if err := store.Consume(nonce); err != nil { t.Fatalf("first Consume() error: %v", err) } err = store.Consume(nonce) if err == nil { t.Fatalf("expected error on second Consume(), got nil") } } func TestNonceFormat(t *testing.T) { store := NewNonceStore() nonce, err := store.Issue() if err != nil { t.Fatalf("Issue() error: %v", err) } // 16 bytes base64url-encoded without padding = 22 characters. if len(nonce) != 22 { t.Fatalf("expected nonce length 22, got %d", len(nonce)) } // Verify it is valid base64url (no padding). decoded, err := base64.RawURLEncoding.DecodeString(nonce) if err != nil { t.Fatalf("nonce is not valid base64url: %v", err) } if len(decoded) != 16 { t.Fatalf("expected 16 decoded bytes, got %d", len(decoded)) } } func TestNonceConcurrentAccess(t *testing.T) { store := NewNonceStore() const goroutines = 50 var wg sync.WaitGroup wg.Add(goroutines) errs := make(chan error, goroutines) for i := 0; i < goroutines; i++ { go func() { defer wg.Done() nonce, err := store.Issue() if err != nil { errs <- err return } if err := store.Consume(nonce); err != nil { errs <- err return } }() } wg.Wait() close(errs) for err := range errs { t.Fatalf("concurrent nonce operation failed: %v", err) } }