package dht import ( "context" "strings" "testing" "time" libp2p "github.com/libp2p/go-libp2p" dhtmode "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p/core/test" ) type harness struct { ctx context.Context host libp2pHost dht *LibP2PDHT } type libp2pHost interface { Close() error } func newHarness(t *testing.T, opts ...Option) *harness { t.Helper() ctx, cancel := context.WithCancel(context.Background()) host, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) if err != nil { cancel() t.Fatalf("failed to create libp2p host: %v", err) } options := append([]Option{WithAutoBootstrap(false)}, opts...) d, err := NewLibP2PDHT(ctx, host, options...) if err != nil { host.Close() cancel() t.Fatalf("failed to create DHT: %v", err) } t.Cleanup(func() { d.Close() host.Close() cancel() }) return &harness{ctx: ctx, host: host, dht: d} } func TestDefaultConfig(t *testing.T) { cfg := DefaultConfig() if cfg.ProtocolPrefix != "/CHORUS" { t.Fatalf("expected protocol prefix '/CHORUS', got %s", cfg.ProtocolPrefix) } if cfg.BootstrapTimeout != 30*time.Second { t.Fatalf("expected bootstrap timeout 30s, got %v", cfg.BootstrapTimeout) } if cfg.Mode != dhtmode.ModeAuto { t.Fatalf("expected mode auto, got %v", cfg.Mode) } if !cfg.AutoBootstrap { t.Fatal("expected auto bootstrap to be enabled") } } func TestWithOptionsOverridesDefaults(t *testing.T) { h := newHarness(t, WithProtocolPrefix("/custom"), WithDiscoveryInterval(2*time.Minute), WithBootstrapTimeout(45*time.Second), WithMode(dhtmode.ModeClient), WithAutoBootstrap(true), ) cfg := h.dht.config if cfg.ProtocolPrefix != "/custom" { t.Fatalf("expected protocol prefix '/custom', got %s", cfg.ProtocolPrefix) } if cfg.DiscoveryInterval != 2*time.Minute { t.Fatalf("expected discovery interval 2m, got %v", cfg.DiscoveryInterval) } if cfg.BootstrapTimeout != 45*time.Second { t.Fatalf("expected bootstrap timeout 45s, got %v", cfg.BootstrapTimeout) } if cfg.Mode != dhtmode.ModeClient { t.Fatalf("expected mode client, got %v", cfg.Mode) } if !cfg.AutoBootstrap { t.Fatal("expected auto bootstrap to remain enabled") } } func TestProvideRequiresBootstrap(t *testing.T) { h := newHarness(t) err := h.dht.Provide(h.ctx, "key") if err == nil { t.Fatal("expected Provide to fail when not bootstrapped") } if !strings.Contains(err.Error(), "not bootstrapped") { t.Fatalf("expected error to indicate bootstrap requirement, got %v", err) } } func TestRegisterPeer(t *testing.T) { h := newHarness(t) peerID := test.RandPeerIDFatal(t) h.dht.RegisterPeer(peerID, "apollo", "platform", []string{"go"}) peers := h.dht.GetKnownPeers() info, ok := peers[peerID] if !ok { t.Fatalf("expected peer to be tracked") } if info.Agent != "apollo" { t.Fatalf("expected agent apollo, got %s", info.Agent) } if info.Role != "platform" { t.Fatalf("expected role platform, got %s", info.Role) } if len(info.Capabilities) != 1 || info.Capabilities[0] != "go" { t.Fatalf("expected capability go, got %v", info.Capabilities) } } func TestGetStatsProvidesUptime(t *testing.T) { h := newHarness(t) stats := h.dht.GetStats() if stats.TotalPeers != 0 { t.Fatalf("expected zero peers, got %d", stats.TotalPeers) } if stats.Uptime < 0 { t.Fatalf("expected non-negative uptime, got %v", stats.Uptime) } }