whoissdk/rdap_client_test.go

152 lines
4.3 KiB
Go
Raw Permalink Normal View History

2026-03-19 11:53:07 +08:00
package whois
import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"time"
)
func TestRDAPClientQueryDomain(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/rdap/domain/example.com" {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "application/rdap+json")
_, _ = w.Write([]byte(`{"objectClassName":"domain","ldhName":"example.com"}`))
}))
defer srv.Close()
boot := &RDAPBootstrap{
Version: "1.0",
Services: []RDAPService{
{
TLDs: []string{"com"},
URLs: []string{srv.URL + "/rdap/"},
},
},
}
c, err := NewRDAPClientWithBootstrap(boot)
if err != nil {
t.Fatalf("NewRDAPClientWithBootstrap() error: %v", err)
}
resp, err := c.QueryDomain(context.Background(), "example.com")
if err != nil {
t.Fatalf("QueryDomain() error: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("unexpected status: %d", resp.StatusCode)
}
if !strings.Contains(string(resp.Body), `"ldhName":"example.com"`) {
t.Fatalf("unexpected body: %s", string(resp.Body))
}
}
func TestRDAPClientQueryDomainJSON(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/domain/example.org" {
http.NotFound(w, r)
return
}
_, _ = w.Write([]byte(`{"ldhName":"example.org","status":["active"]}`))
}))
defer srv.Close()
c, err := NewRDAPClientWithBootstrap(&RDAPBootstrap{
Version: "1.0",
Services: []RDAPService{
{TLDs: []string{"org"}, URLs: []string{srv.URL}},
},
})
if err != nil {
t.Fatalf("NewRDAPClientWithBootstrap() error: %v", err)
}
var out map[string]any
_, err = c.QueryDomainJSON(context.Background(), "example.org", &out)
if err != nil {
t.Fatalf("QueryDomainJSON() error: %v", err)
}
if out["ldhName"] != "example.org" {
t.Fatalf("unexpected json field: %#v", out)
}
}
func TestNewRDAPClientWithLayeredBootstrap(t *testing.T) {
rdapSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/rdap/domain/example.dev" {
http.NotFound(w, r)
return
}
_, _ = w.Write([]byte(`{"objectClassName":"domain","ldhName":"example.dev"}`))
}))
defer rdapSrv.Close()
localRaw := `{"version":"1.0","services":[[["dev"],["` + rdapSrv.URL + `/rdap/"]]]}`
localPath := filepath.Join(t.TempDir(), "rdap_local.json")
if err := os.WriteFile(localPath, []byte(localRaw), 0644); err != nil {
t.Fatalf("write local bootstrap failed: %v", err)
}
c, err := NewRDAPClientWithLayeredBootstrap(context.Background(), RDAPBootstrapLoadOptions{
LocalFiles: []string{localPath},
})
if err != nil {
t.Fatalf("NewRDAPClientWithLayeredBootstrap() error: %v", err)
}
resp, err := c.QueryDomain(context.Background(), "example.dev")
if err != nil {
t.Fatalf("QueryDomain() error: %v", err)
}
if resp.StatusCode != http.StatusOK || !strings.Contains(string(resp.Body), `"example.dev"`) {
t.Fatalf("unexpected response: status=%d body=%s", resp.StatusCode, string(resp.Body))
}
}
func TestNewRDAPClientWithLayeredBootstrapCacheTTL(t *testing.T) {
cacheKey := t.Name() + "-cache"
ClearRDAPBootstrapLayeredCache(cacheKey)
var bootstrapHit int32
bootstrapRaw := `{"version":"2.0","services":[[["dev"],["https://rdap.example.dev/rdap/"]]]}`
bootstrapSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt32(&bootstrapHit, 1)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(bootstrapRaw))
}))
defer bootstrapSrv.Close()
loadOpt := RDAPBootstrapLoadOptions{
RefreshRemote: true,
RemoteURL: bootstrapSrv.URL,
CacheTTL: time.Minute,
CacheKey: cacheKey,
}
c1, err := NewRDAPClientWithLayeredBootstrap(context.Background(), loadOpt)
if err != nil {
t.Fatalf("first NewRDAPClientWithLayeredBootstrap() error: %v", err)
}
if c1 == nil {
t.Fatal("first client should not be nil")
}
c2, err := NewRDAPClientWithLayeredBootstrap(context.Background(), loadOpt)
if err != nil {
t.Fatalf("second NewRDAPClientWithLayeredBootstrap() error: %v", err)
}
if c2 == nil {
t.Fatal("second client should not be nil")
}
if got := atomic.LoadInt32(&bootstrapHit); got != 1 {
t.Fatalf("expected cached bootstrap to fetch remote once, got=%d", got)
}
}