package setup import ( "context" "encoding/json" "errors" "strings" "sync" "testing" "time" ) type fakeDNSTester struct { err error } func TestDNSVerificationStoresOnlyCurrentAliyunCredentials(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) input := DNSVerificationInput{ProviderID: "aliyun", TestDomain: "www.example.com", Credentials: map[string]string{ "access_key_id": "test-id", "access_key_secret": "test-secret", "managed_zones": "example.com", "allowed_domains": "www.example.com", }} first, err := manager.Verify(t.Context(), input) if err != nil { t.Fatal(err) } if _, ok := store.values[first.CredentialRef]["managed_zones"]; ok { t.Fatal("legacy managed zone must not be persisted as a credential") } if _, ok := store.values[first.CredentialRef]["allowed_domains"]; ok { t.Fatal("legacy allowed domain must not be persisted as a credential") } input.Credentials["access_key_secret"] = "new-secret" second, err := manager.Verify(t.Context(), input) if err != nil { t.Fatal(err) } if first.CredentialRef == second.CredentialRef || store.values[first.CredentialRef]["access_key_secret"] != "test-secret" { t.Fatal("credential changes must not overwrite an already verified reference") } if err := manager.Check(first.VerificationToken, "aliyun", second.CredentialRef, input.TestDomain); err == nil { t.Fatal("old token must not authorize a new credential reference") } } func (f fakeDNSTester) Verify(context.Context, string, string, map[string]string) (DNSVerificationReport, error) { return DNSVerificationReport{}, f.err } type memorySecretWriter struct { mu sync.Mutex values map[string]map[string]string deleted []string putErr error deleteErr error } type cancellingSecretWriter struct { memorySecretWriter cancel context.CancelFunc } type retryableSecretWriter struct { memorySecretWriter failDelete bool } type cancellingRetryableSecretWriter struct { retryableSecretWriter cancel context.CancelFunc } func (s *cancellingSecretWriter) Put(ctx context.Context, reference string, value map[string]string) error { if err := s.memorySecretWriter.Put(ctx, reference, value); err != nil { return err } s.cancel() return nil } func (s *retryableSecretWriter) Delete(ctx context.Context, reference string) error { if s.failDelete { return errors.New("delete failed") } return s.memorySecretWriter.Delete(ctx, reference) } func (s *cancellingRetryableSecretWriter) Put(ctx context.Context, reference string, value map[string]string) error { if err := s.retryableSecretWriter.Put(ctx, reference, value); err != nil { return err } s.cancel() return nil } func (m *memorySecretWriter) Put(_ context.Context, reference string, value map[string]string) error { m.mu.Lock() defer m.mu.Unlock() if m.values == nil { m.values = map[string]map[string]string{} } copy := make(map[string]string, len(value)) for key, item := range value { copy[key] = item } m.values[reference] = copy return m.putErr } func (m *memorySecretWriter) Delete(_ context.Context, reference string) error { m.mu.Lock() defer m.mu.Unlock() m.deleted = append(m.deleted, reference) delete(m.values, reference) return m.deleteErr } func TestDNSVerificationManagerIssuesOneTimeToken(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatalf("DNS 在线验证失败:%v", err) } if result.CredentialRef == "" || result.VerificationToken == "" { t.Fatalf("DNS 验证结果不完整:%+v", result) } if len(store.values) != 1 { t.Fatalf("DNS 凭据没有写入敏感信息存储:%v", store.values) } if err := manager.Check(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err != nil { t.Fatalf("检查 DNS 验证令牌失败:%v", err) } if err := manager.Check(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err != nil { t.Fatalf("检查 DNS 验证令牌不应消费令牌:%v", err) } if err := manager.Consume(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err != nil { t.Fatalf("消费 DNS 验证令牌失败:%v", err) } if err := manager.Check(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err == nil { t.Fatal("已消费的 DNS 验证令牌不应继续通过检查") } if err := manager.Consume(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err == nil { t.Fatal("DNS 验证令牌只能使用一次") } } func TestDNSVerificationManagerRejectsMismatchedConfiguration(t *testing.T) { manager := NewDNSVerificationManager(fakeDNSTester{}, &memorySecretWriter{}) result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatalf("DNS 在线验证失败:%v", err) } if err := manager.Consume(result.VerificationToken, "cloudflare", result.CredentialRef, "other.example.com"); err == nil { t.Fatal("DNS 验证结果不应允许绑定到其他域名") } } func TestDNSVerificationDiscardOnlyRemovesSelectedToken(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) first, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: "first.example.com", Credentials: map[string]string{"access_key_id": "first-id", "access_key_secret": "first-secret"}, }) if err != nil { t.Fatal(err) } second, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: "second.example.com", Credentials: map[string]string{"access_key_id": "second-id", "access_key_secret": "second-secret"}, }) if err != nil { t.Fatal(err) } if err := manager.Discard(first.VerificationToken); err != nil { t.Fatalf("精确回收 DNS 验证令牌失败:%v", err) } if err := manager.Check(first.VerificationToken, "aliyun", first.CredentialRef, "first.example.com"); err == nil { t.Fatal("已回收的 DNS 验证令牌仍然有效") } if err := manager.Check(second.VerificationToken, "aliyun", second.CredentialRef, "second.example.com"); err != nil { t.Fatalf("精确回收影响了其他 DNS 验证令牌:%v", err) } store.mu.Lock() _, firstExists := store.values[first.CredentialRef] _, secondExists := store.values[second.CredentialRef] store.mu.Unlock() if firstExists || !secondExists { t.Fatalf("DNS 凭据回收范围错误:first=%v second=%v", firstExists, secondExists) } if err := manager.Discard(first.VerificationToken); err == nil { t.Fatal("DNS 验证令牌被重复回收") } } func TestDNSVerificationDiscardAllRemovesPendingCredentials(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) results := make([]DNSVerificationResult, 0, 2) for _, testDomain := range []string{"first.example.com", "second.example.com"} { result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: testDomain, Credentials: map[string]string{"access_key_id": "test-id", "access_key_secret": "test-secret"}, }) if err != nil { t.Fatal(err) } results = append(results, result) } manager.DiscardAll() for _, result := range results { if err := manager.Check(result.VerificationToken, "aliyun", result.CredentialRef, "first.example.com"); err == nil { t.Fatal("DiscardAll 后 DNS 验证令牌仍然有效") } } store.mu.Lock() remaining := len(store.values) store.mu.Unlock() if remaining != 0 { t.Fatalf("DiscardAll 后仍有 DNS 临时凭据:%d", remaining) } } func TestDNSVerificationDiscardAllExceptProtectsFormalCredential(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) protected, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: "formal.example.com", Credentials: map[string]string{"access_key_id": "formal-id", "access_key_secret": "formal-secret"}, }) if err != nil { t.Fatal(err) } pending, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: "pending.example.com", Credentials: map[string]string{"access_key_id": "pending-id", "access_key_secret": "pending-secret"}, }) if err != nil { t.Fatal(err) } manager.DiscardAllExcept(" " + protected.CredentialRef + " ") if _, err := manager.Resolve(protected.VerificationToken); err == nil { t.Fatal("批量清理后受保护凭据对应的验证令牌仍然有效") } if _, err := manager.Resolve(pending.VerificationToken); err == nil { t.Fatal("批量清理后待处理凭据对应的验证令牌仍然有效") } store.mu.Lock() _, protectedExists := store.values[protected.CredentialRef] _, pendingExists := store.values[pending.CredentialRef] store.mu.Unlock() if !protectedExists || pendingExists { t.Fatalf("DNS 凭据保护范围错误:protected=%v pending=%v", protectedExists, pendingExists) } } func TestDNSVerificationManagerRejectsExpiredToken(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) now := time.Date(2026, 8, 26, 8, 0, 0, 0, time.UTC) manager.now = func() time.Time { return now } result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatalf("DNS 在线验证失败:%v", err) } now = now.Add(dnsVerificationLifetime + time.Second) if err := manager.Consume(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err == nil { t.Fatal("过期 DNS 验证令牌应被拒绝") } if len(store.deleted) != 1 || store.deleted[0] != result.CredentialRef { t.Fatalf("expired session did not delete temporary credential: %v", store.deleted) } } func TestDNSVerificationManagerDeletesCredentialWhenPutReportsFailure(t *testing.T) { store := &memorySecretWriter{putErr: errors.New("write failed")} manager := NewDNSVerificationManager(fakeDNSTester{}, store) _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil { t.Fatal("Put failure unexpectedly succeeded") } if len(store.deleted) != 1 { t.Fatalf("Put failure did not trigger credential cleanup: %v", store.deleted) } if len(store.values) != 0 { t.Fatalf("Put failure left temporary credentials stored: %v", store.values) } } func TestDNSVerificationManagerRetriesCleanupAfterPutFailure(t *testing.T) { store := &retryableSecretWriter{failDelete: true} store.putErr = errors.New("write failed") manager := NewDNSVerificationManager(fakeDNSTester{}, store) _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil { t.Fatal("保存失败时应返回错误") } if len(store.values) != 1 || len(manager.pendingCleanup) != 1 { t.Fatalf("删除失败后应保留待清理凭据:values=%d pending=%d", len(store.values), len(manager.pendingCleanup)) } store.putErr = nil store.failDelete = false if _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "next-secret"}, }); err != nil { t.Fatalf("下一次验证应先重试待清理凭据:%v", err) } if len(manager.pendingCleanup) != 0 || len(store.values) != 1 { t.Fatalf("待清理凭据重试后状态错误:pending=%d values=%d", len(manager.pendingCleanup), len(store.values)) } } func TestDNSVerificationManagerRetriesCleanupAfterCancellation(t *testing.T) { ctx, cancel := context.WithCancel(t.Context()) store := &cancellingRetryableSecretWriter{ retryableSecretWriter: retryableSecretWriter{failDelete: true}, cancel: cancel, } manager := NewDNSVerificationManager(fakeDNSTester{}, store) _, err := manager.Verify(ctx, DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if !errors.Is(err, context.Canceled) { t.Fatalf("保存后取消应返回 context.Canceled:%v", err) } if len(store.values) != 1 || len(manager.pendingCleanup) != 1 { t.Fatalf("取消后的删除失败应进入待清理状态:values=%d pending=%d", len(store.values), len(manager.pendingCleanup)) } store.failDelete = false if _, err := manager.Resolve("missing-token"); err == nil { t.Fatal("不存在的令牌应返回错误") } if len(store.values) != 0 || len(manager.pendingCleanup) != 0 { t.Fatalf("取消后的凭据重试未完成:values=%d pending=%d", len(store.values), len(manager.pendingCleanup)) } } func TestDNSVerificationManagerRejectsAlreadyCancelledContextBeforeTesting(t *testing.T) { tester := &countingDNSTester{} store := &memorySecretWriter{} manager := NewDNSVerificationManager(tester, store) ctx, cancel := context.WithCancel(t.Context()) cancel() _, err := manager.Verify(ctx, DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if !errors.Is(err, context.Canceled) { t.Fatalf("已取消上下文应返回 context.Canceled:%v", err) } if tester.CallCount() != 0 || len(store.values) != 0 { t.Fatalf("已取消上下文不应执行测试或保存凭据:calls=%d values=%v", tester.CallCount(), store.values) } } func TestDNSVerificationManagerCleansCredentialWhenContextCancelsAfterStore(t *testing.T) { ctx, cancel := context.WithCancel(t.Context()) store := &cancellingSecretWriter{cancel: cancel} manager := NewDNSVerificationManager(fakeDNSTester{}, store) result, err := manager.Verify(ctx, DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if !errors.Is(err, context.Canceled) { t.Fatalf("保存凭据后取消应返回 context.Canceled:%v", err) } if result.CredentialRef != "" || result.VerificationToken != "" { t.Fatalf("取消的 DNS 验证不应发布令牌:%+v", result) } if len(store.values) != 0 { t.Fatalf("保存后取消未回收临时凭据:%v", store.values) } if len(store.deleted) != 1 { t.Fatalf("保存后取消未执行临时凭据回收:%v", store.deleted) } } func TestDNSVerificationManagerRetainsTokenWhenDiscardCleanupFails(t *testing.T) { store := &retryableSecretWriter{failDelete: true} manager := NewDNSVerificationManager(fakeDNSTester{}, store) result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatal(err) } if err := manager.Discard(result.VerificationToken); err == nil { t.Fatal("临时凭据回收失败时应返回错误") } if _, err := manager.Resolve(result.VerificationToken); err != nil { t.Fatalf("回收失败后应保留验证令牌以便重试:%v", err) } if len(store.values) != 1 { t.Fatalf("回收失败后不应丢失临时凭据:%v", store.values) } store.failDelete = false if err := manager.Discard(result.VerificationToken); err != nil { t.Fatalf("临时凭据回收重试失败:%v", err) } if len(store.values) != 0 { t.Fatalf("回收重试后仍有临时凭据:%v", store.values) } } func TestDNSVerificationManagerRetriesDiscardAllCleanupFailures(t *testing.T) { store := &retryableSecretWriter{failDelete: true} manager := NewDNSVerificationManager(fakeDNSTester{}, store) for range 2 { if _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }); err != nil { t.Fatal(err) } } manager.DiscardAll() if len(manager.sessions) != 2 || len(store.values) != 2 { t.Fatalf("批量回收失败后应保留可重试会话:sessions=%d values=%d", len(manager.sessions), len(store.values)) } store.failDelete = false manager.DiscardAll() if len(manager.sessions) != 0 || len(store.values) != 0 { t.Fatalf("批量回收重试后仍有临时状态:sessions=%d values=%d", len(manager.sessions), len(store.values)) } } type countingDNSTester struct { mu sync.Mutex calls int block <-chan struct{} } func (f *countingDNSTester) Verify(_ context.Context, _ string, _ string, _ map[string]string) (DNSVerificationReport, error) { f.mu.Lock() f.calls++ f.mu.Unlock() if f.block != nil { <-f.block } return DNSVerificationReport{}, nil } func (f *countingDNSTester) CallCount() int { f.mu.Lock() defer f.mu.Unlock() return f.calls } func TestDNSVerificationManagerRejectsCapacityBeforeTestingOrStoring(t *testing.T) { tester := &countingDNSTester{} store := &memorySecretWriter{} manager := NewDNSVerificationManager(tester, store) manager.sessions = make(map[string]dnsVerificationSession, dnsVerificationSessionCapacity) expiresAt := time.Now().UTC().Add(time.Hour) for index := 0; index < dnsVerificationSessionCapacity; index++ { manager.sessions[string(rune(index))] = dnsVerificationSession{CredentialRef: "secret://existing", ExpiresAt: expiresAt} } _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil || tester.CallCount() != 0 || len(store.values) != 0 { t.Fatalf("capacity rejection must precede testing and storage: err=%v calls=%d values=%v", err, tester.CallCount(), store.values) } } func TestDNSVerificationManagerCountsExpiredCleanupAgainstCapacity(t *testing.T) { tester := &countingDNSTester{} store := &memorySecretWriter{} manager := NewDNSVerificationManager(tester, store) manager.sessions = make(map[string]dnsVerificationSession, dnsVerificationSessionCapacity-1) manager.expiredSession = make(map[string]expiredDNSVerificationSession, 1) expiresAt := time.Now().UTC().Add(time.Hour) for index := 0; index < dnsVerificationSessionCapacity-1; index++ { manager.sessions[string(rune(index))] = dnsVerificationSession{CredentialRef: "secret://existing", ExpiresAt: expiresAt} } manager.expiredSession["secret://pending"] = expiredDNSVerificationSession{ Token: "pending-token", Session: dnsVerificationSession{CredentialRef: "secret://pending", ExpiresAt: expiresAt}, } _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil || tester.CallCount() != 0 || len(store.values) != 0 { t.Fatalf("待清理凭据应计入容量限制:err=%v calls=%d values=%v", err, tester.CallCount(), store.values) } } func TestDNSVerificationManagerRejectsInFlightCapacityBeforeTestingOrStoring(t *testing.T) { tester := &countingDNSTester{} store := &memorySecretWriter{} manager := NewDNSVerificationManager(tester, store) manager.inFlight = dnsVerificationInFlightCapacity _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil || tester.CallCount() != 0 || len(store.values) != 0 { t.Fatalf("in-flight rejection must precede testing and storage: err=%v calls=%d values=%v", err, tester.CallCount(), store.values) } } func TestDNSVerificationManagerConsumesTokenOnlyOnceUnderConcurrency(t *testing.T) { manager := NewDNSVerificationManager(fakeDNSTester{}, &memorySecretWriter{}) result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatal(err) } start := make(chan struct{}) results := make(chan error, 2) for range 2 { go func() { <-start results <- manager.Consume(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com") }() } close(start) successes := 0 for range 2 { if <-results == nil { successes++ } } if successes != 1 { t.Fatalf("concurrent Consume successes = %d; want 1", successes) } } func TestDNSVerificationManagerDoesNotStoreFailedCredential(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{err: errors.New("权限不足")}, store) _, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err == nil { t.Fatal("DNS 测试失败时应返回错误") } if len(store.values) != 0 { t.Fatal("DNS 测试失败时不应保存凭据") } } type recordingDNSTester struct { credentials map[string]string } func (f *recordingDNSTester) Verify(_ context.Context, _ string, _ string, credentials map[string]string) (DNSVerificationReport, error) { f.credentials = credentials return DNSVerificationReport{}, nil } func TestDNSVerificationManagerNormalizesCredentialCopyBeforeTestingAndStoring(t *testing.T) { store := &memorySecretWriter{} tester := &recordingDNSTester{} manager := NewDNSVerificationManager(tester, store) inputCredentials := map[string]string{" api_token ": " secret-token "} result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: inputCredentials, }) if err != nil { t.Fatalf("DNS 在线验证失败:%v", err) } if tester.credentials["api_token"] != "secret-token" { t.Fatalf("传给测试器的凭据未规范化:%v", tester.credentials) } if _, ok := tester.credentials[" api_token "]; ok { t.Fatal("测试器不应收到带首尾空格的凭据键") } stored := store.values[result.CredentialRef] if stored["api_token"] != "secret-token" { t.Fatalf("保存的凭据未规范化:%v", stored) } if inputCredentials[" api_token "] != " secret-token " { t.Fatal("规范化过程不应修改调用方的原始凭据 Map") } } func TestDNSVerificationResolveIsServerOnlyAndDoesNotConsumeToken(t *testing.T) { store := &memorySecretWriter{} manager := NewDNSVerificationManager(fakeDNSTester{}, store) result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "aliyun", TestDomain: "www.example.com", Credentials: map[string]string{"access_key_id": "test-id", "access_key_secret": "test-secret"}, }) if err != nil { t.Fatal(err) } resolved, err := manager.Resolve(result.VerificationToken) if err != nil { t.Fatalf("按令牌解析 DNS 验证结果失败:%v", err) } if resolved.ProviderID != "aliyun" || resolved.TestDomain != "www.example.com" || resolved.CredentialRef != result.CredentialRef || resolved.ExpiresAt != result.ExpiresAt { t.Fatalf("解析的 DNS 验证状态不完整:%+v", resolved) } encoded, err := json.Marshal(resolved) if err != nil { t.Fatal(err) } if strings.Contains(string(encoded), result.CredentialRef) || strings.Contains(string(encoded), resolved.TestDomain) { t.Fatalf("服务端 DNS 验证状态可被意外序列化:%s", encoded) } if err := manager.Consume(result.VerificationToken, resolved.ProviderID, resolved.CredentialRef, resolved.TestDomain); err != nil { t.Fatalf("Resolve 不应消费 DNS 验证令牌:%v", err) } if _, err := manager.Resolve(result.VerificationToken); err == nil { t.Fatal("已消费的 DNS 验证令牌仍可被解析") } } func TestDNSVerificationManagerRetriesExpiredCredentialCleanup(t *testing.T) { store := &retryableSecretWriter{failDelete: true} manager := NewDNSVerificationManager(fakeDNSTester{}, store) now := time.Date(2026, 9, 30, 12, 0, 0, 0, time.UTC) manager.now = func() time.Time { return now } result, err := manager.Verify(t.Context(), DNSVerificationInput{ ProviderID: "cloudflare", TestDomain: "example.com", Credentials: map[string]string{"api_token": "secret"}, }) if err != nil { t.Fatal(err) } now = now.Add(dnsVerificationLifetime + time.Second) if err := manager.Check(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err == nil { t.Fatal("expired DNS verification token should be rejected") } if len(manager.sessions) != 1 || len(store.values) != 1 { t.Fatalf("failed expired cleanup should remain retryable: sessions=%d values=%d", len(manager.sessions), len(store.values)) } store.failDelete = false if err := manager.Check(result.VerificationToken, "cloudflare", result.CredentialRef, "example.com"); err == nil { t.Fatal("expired DNS verification token should remain rejected after cleanup retry") } if len(manager.sessions) != 0 || len(store.values) != 0 { t.Fatalf("expired cleanup retry left temporary state: sessions=%d values=%d", len(manager.sessions), len(store.values)) } }