diff --git a/codex/debug.go b/codex/debug.go index e550c6f..a4f7d21 100644 --- a/codex/debug.go +++ b/codex/debug.go @@ -11,6 +11,10 @@ package codex static int cGoCodexLogLevel(void* codexCtx, char* logLevel, void* resp) { return codex_log_level(codexCtx, logLevel, (CodexCallback) callback, resp); } + + static int cGoCodexPeerDebug(void* codexCtx, char* peerId, void* resp) { + return codex_peer_debug(codexCtx, peerId, (CodexCallback) callback, resp); + } */ import "C" import ( @@ -39,6 +43,12 @@ type DebugInfo struct { PeersTable RoutingTable `json:"table"` } +type PeerRecord struct { + PeerId string `json:"peerId"` + SeqNo int `json:"seqNo"` + Addresses []string `json:"addresses,omitempty"` +} + // Debug retrieves debugging information from the Codex node. func (node CodexNode) Debug() (DebugInfo, error) { var info DebugInfo @@ -79,3 +89,26 @@ func (node CodexNode) UpdateLogLevel(logLevel string) error { _, err := bridge.wait() return err } + +func (node CodexNode) CodexPeerDebug(peerId string) (PeerRecord, error) { + var record PeerRecord + + bridge := newBridgeCtx() + defer bridge.free() + + var cPeerId = C.CString(peerId) + defer C.free(unsafe.Pointer(cPeerId)) + + if C.cGoCodexPeerDebug(node.ctx, cPeerId, bridge.resp) != C.RET_OK { + return record, bridge.callError("cGoCodexPeerDebug") + } + + value, err := bridge.wait() + if err != nil { + return record, err + } + + err = json.Unmarshal([]byte(value), &record) + + return record, err +} diff --git a/codex/debug_test.go b/codex/debug_test.go index b5cafcc..9355347 100644 --- a/codex/debug_test.go +++ b/codex/debug_test.go @@ -4,6 +4,7 @@ import ( "os" "strings" "testing" + "time" ) func TestDebug(t *testing.T) { @@ -32,7 +33,8 @@ func TestUpdateLogLevel(t *testing.T) { defer os.Remove(tmpFile.Name()) node, err := CodexNew(CodexConfig{ - LogFile: tmpFile.Name(), + LogFile: tmpFile.Name(), + MetricsEnabled: false, }) if err != nil { t.Fatalf("Failed to create Codex node: %v", err) @@ -87,3 +89,119 @@ func TestUpdateLogLevel(t *testing.T) { t.Errorf("Log file contains 'Starting discovery node'") } } + +func TestCodexPeerDebug(t *testing.T) { + var bootstrap, node1, node2 *CodexNode + var err error + + t.Cleanup(func() { + if bootstrap != nil { + if err := bootstrap.Stop(); err != nil { + t.Logf("cleanup bootstrap: %v", err) + } + + if err := bootstrap.Destroy(); err != nil { + t.Logf("cleanup bootstrap: %v", err) + } + } + if node1 != nil { + if err := node1.Stop(); err != nil { + t.Logf("cleanup node1: %v", err) + } + + if err := node1.Destroy(); err != nil { + t.Logf("cleanup node1: %v", err) + } + } + if node2 != nil { + if err := node2.Stop(); err != nil { + t.Logf("cleanup node2: %v", err) + } + + if err := node2.Destroy(); err != nil { + t.Logf("cleanup node2: %v", err) + } + } + }) + + bootstrap, err = CodexNew(CodexConfig{ + DataDir: t.TempDir(), + LogFormat: LogFormatNoColors, + MetricsEnabled: false, + DiscoveryPort: 8092, + }) + if err != nil { + t.Fatalf("Failed to create bootstrap: %v", err) + } + + if err := bootstrap.Start(); err != nil { + t.Fatalf("Failed to start bootstrap: %v", err) + } + + spr, err := bootstrap.Spr() + if err != nil { + t.Fatalf("Failed to get bootstrap spr: %v", err) + } + + bootstrapNodes := []string{spr} + + node1, err = CodexNew(CodexConfig{ + DataDir: t.TempDir(), + LogFormat: LogFormatNoColors, + MetricsEnabled: false, + DiscoveryPort: 8090, + BootstrapNodes: bootstrapNodes, + }) + if err != nil { + t.Fatalf("Failed to create codex: %v", err) + } + + if err := node1.Start(); err != nil { + t.Fatalf("Failed to start codex: %v", err) + } + + node2, err = CodexNew(CodexConfig{ + DataDir: t.TempDir(), + LogFormat: LogFormatNoColors, + MetricsEnabled: false, + DiscoveryPort: 8091, + BootstrapNodes: bootstrapNodes, + }) + if err != nil { + t.Fatalf("Failed to create codex2: %v", err) + } + + if err := node2.Start(); err != nil { + t.Fatalf("Failed to start codex2: %v", err) + } + + peerId, err := node2.PeerId() + if err != nil { + t.Fatal(err) + } + + var record PeerRecord + for range 10 { + record, err = node1.CodexPeerDebug(peerId) + if err == nil { + break + } + time.Sleep(1 * time.Second) + } + + if record.PeerId == "" { + t.Fatalf("CodexPeerDebug call failed: %v", err) + } + if record.PeerId == "" { + t.Error("CodexPeerDebug info PeerId is empty") + } + if record.SeqNo == 0 { + t.Error("CodexPeerDebug info SeqNo is empty") + } + if len(record.Addresses) == 0 { + t.Error("CodexPeerDebug info Addresses is empty") + } + if record.PeerId != peerId { + t.Errorf("CodexPeerDebug info PeerId (%s) does not match requested PeerId (%s)", record.PeerId, peerId) + } +} diff --git a/codex/testutil.go b/codex/testutil.go index 14af0aa..a05f672 100644 --- a/codex/testutil.go +++ b/codex/testutil.go @@ -1,11 +1,14 @@ package codex -import "testing" +import ( + "testing" +) func newCodexNode(t *testing.T) *CodexNode { node, err := CodexNew(CodexConfig{ - DataDir: t.TempDir(), - LogFormat: LogFormatNoColors, + DataDir: t.TempDir(), + LogFormat: LogFormatNoColors, + MetricsEnabled: false, }) if err != nil { t.Fatalf("Failed to create Codex node: %v", err)