From 63a9107f9cb9f0835199f8c2eb261a5987c8d3ef Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Apr 2026 18:30:42 +0100 Subject: [PATCH] ax(batch): add missing test files and fix pre-existing test compilation errors Adds test files for 6 source files that had no corresponding test: bufpool_test.go (mining + node), metrics_test.go, version_test.go, supervisor_test.go, mining_profile_test.go. Each follows TestFilename_Function_{Good,Bad,Ugly} convention. Also fixes 2 pre-existing compilation errors: - ratelimiter_test.go: rl -> rateLimiter (leftover from AX rename) - worker_test.go: worker.node -> worker.nodeManager (field was renamed) Co-Authored-By: Charon --- pkg/mining/bufpool_test.go | 69 +++++++++++++++++++++++++++ pkg/mining/metrics_test.go | 77 +++++++++++++++++++++++++++++++ pkg/mining/mining_profile_test.go | 40 ++++++++++++++++ pkg/mining/ratelimiter_test.go | 4 +- pkg/mining/supervisor_test.go | 58 +++++++++++++++++++++++ pkg/mining/version_test.go | 31 +++++++++++++ pkg/node/bufpool_test.go | 69 +++++++++++++++++++++++++++ pkg/node/worker_test.go | 4 +- 8 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 pkg/mining/bufpool_test.go create mode 100644 pkg/mining/metrics_test.go create mode 100644 pkg/mining/mining_profile_test.go create mode 100644 pkg/mining/supervisor_test.go create mode 100644 pkg/mining/version_test.go create mode 100644 pkg/node/bufpool_test.go diff --git a/pkg/mining/bufpool_test.go b/pkg/mining/bufpool_test.go new file mode 100644 index 0000000..97de0c4 --- /dev/null +++ b/pkg/mining/bufpool_test.go @@ -0,0 +1,69 @@ +package mining + +import ( + "testing" +) + +func TestBufpool_MarshalJSON_Good(t *testing.T) { + data := map[string]string{"key": "value"} + result, err := MarshalJSON(data) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if len(result) == 0 { + t.Fatal("expected non-empty result") + } +} + +func TestBufpool_MarshalJSON_Bad(t *testing.T) { + // Channels cannot be marshalled + result, err := MarshalJSON(make(chan int)) + if err == nil { + t.Fatal("expected error for non-marshallable type") + } + if result != nil { + t.Fatalf("expected nil result, got %v", result) + } +} + +func TestBufpool_MarshalJSON_Ugly(t *testing.T) { + // nil value should produce "null" + result, err := MarshalJSON(nil) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if string(result) != "null" { + t.Fatalf("expected \"null\", got %q", string(result)) + } +} + +func TestBufpool_UnmarshalJSON_Good(t *testing.T) { + var target map[string]string + err := UnmarshalJSON([]byte(`{"key":"value"}`), &target) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if target["key"] != "value" { + t.Fatalf("expected value \"value\", got %q", target["key"]) + } +} + +func TestBufpool_UnmarshalJSON_Bad(t *testing.T) { + var target map[string]string + err := UnmarshalJSON([]byte(`not valid json`), &target) + if err == nil { + t.Fatal("expected error for invalid JSON") + } +} + +func TestBufpool_UnmarshalJSON_Ugly(t *testing.T) { + // Empty JSON object into a map + var target map[string]string + err := UnmarshalJSON([]byte(`{}`), &target) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if len(target) != 0 { + t.Fatalf("expected empty map, got %d entries", len(target)) + } +} diff --git a/pkg/mining/metrics_test.go b/pkg/mining/metrics_test.go new file mode 100644 index 0000000..8934e1c --- /dev/null +++ b/pkg/mining/metrics_test.go @@ -0,0 +1,77 @@ +package mining + +import ( + "testing" + "time" +) + +func TestMetrics_LatencyHistogram_Good(t *testing.T) { + histogram := NewLatencyHistogram(10) + histogram.Record(42 * time.Millisecond) + histogram.Record(58 * time.Millisecond) + + if histogram.Count() != 2 { + t.Fatalf("expected count 2, got %d", histogram.Count()) + } + avg := histogram.Average() + if avg != 50*time.Millisecond { + t.Fatalf("expected average 50ms, got %v", avg) + } +} + +func TestMetrics_LatencyHistogram_Bad(t *testing.T) { + histogram := NewLatencyHistogram(10) + + // No samples recorded — Average should be 0 + if histogram.Average() != 0 { + t.Fatalf("expected 0 average for empty histogram, got %v", histogram.Average()) + } + if histogram.Count() != 0 { + t.Fatalf("expected 0 count, got %d", histogram.Count()) + } +} + +func TestMetrics_LatencyHistogram_Ugly(t *testing.T) { + // Ring buffer overflow: maxSize=2, insert 3 samples + histogram := NewLatencyHistogram(2) + histogram.Record(10 * time.Millisecond) + histogram.Record(20 * time.Millisecond) + histogram.Record(30 * time.Millisecond) + + if histogram.Count() != 2 { + t.Fatalf("expected count 2 after overflow, got %d", histogram.Count()) + } + // Oldest sample (10ms) should have been evicted + avg := histogram.Average() + if avg != 25*time.Millisecond { + t.Fatalf("expected average 25ms (20+30)/2, got %v", avg) + } +} + +func TestMetrics_RecordRequest_Good(t *testing.T) { + before := DefaultMetrics.RequestsTotal.Load() + RecordRequest(false, 5*time.Millisecond) + after := DefaultMetrics.RequestsTotal.Load() + if after != before+1 { + t.Fatalf("expected total to increase by 1, got %d -> %d", before, after) + } +} + +func TestMetrics_RecordRequest_Bad(t *testing.T) { + beforeErr := DefaultMetrics.RequestsErrored.Load() + RecordRequest(true, 5*time.Millisecond) + afterErr := DefaultMetrics.RequestsErrored.Load() + if afterErr != beforeErr+1 { + t.Fatalf("expected errored to increase by 1, got %d -> %d", beforeErr, afterErr) + } +} + +func TestMetrics_RecordRequest_Ugly(t *testing.T) { + snapshot := GetMetricsSnapshot() + if snapshot == nil { + t.Fatal("expected non-nil snapshot") + } + if _, ok := snapshot["requests_total"]; !ok { + t.Fatal("expected requests_total in snapshot") + } +} diff --git a/pkg/mining/mining_profile_test.go b/pkg/mining/mining_profile_test.go new file mode 100644 index 0000000..837b84f --- /dev/null +++ b/pkg/mining/mining_profile_test.go @@ -0,0 +1,40 @@ +package mining + +import ( + "testing" +) + +func TestMiningProfile_RawConfig_MarshalJSON_Good(t *testing.T) { + raw := RawConfig([]byte(`{"pool":"pool.lthn.io:3333"}`)) + data, err := raw.MarshalJSON() + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if string(data) != `{"pool":"pool.lthn.io:3333"}` { + t.Fatalf("unexpected marshal result: %s", string(data)) + } +} + +func TestMiningProfile_RawConfig_MarshalJSON_Bad(t *testing.T) { + // nil RawConfig should marshal to "null" + var raw RawConfig + data, err := raw.MarshalJSON() + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if string(data) != "null" { + t.Fatalf("expected \"null\", got %q", string(data)) + } +} + +func TestMiningProfile_RawConfig_MarshalJSON_Ugly(t *testing.T) { + // UnmarshalJSON on a non-nil pointer + raw := RawConfig([]byte(`{}`)) + err := raw.UnmarshalJSON([]byte(`{"new":"data"}`)) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if string(raw) != `{"new":"data"}` { + t.Fatalf("unexpected unmarshal result: %s", string(raw)) + } +} diff --git a/pkg/mining/ratelimiter_test.go b/pkg/mining/ratelimiter_test.go index cacb55d..0cd1e9d 100644 --- a/pkg/mining/ratelimiter_test.go +++ b/pkg/mining/ratelimiter_test.go @@ -19,10 +19,10 @@ func TestRatelimiter_NewRateLimiter_Good(t *testing.T) { defer rateLimiter.Stop() if rateLimiter.requestsPerSecond != 10 { - t.Errorf("Expected requestsPerSecond 10, got %d", rl.requestsPerSecond) + t.Errorf("Expected requestsPerSecond 10, got %d", rateLimiter.requestsPerSecond) } if rateLimiter.burst != 20 { - t.Errorf("Expected burst 20, got %d", rl.burst) + t.Errorf("Expected burst 20, got %d", rateLimiter.burst) } } diff --git a/pkg/mining/supervisor_test.go b/pkg/mining/supervisor_test.go new file mode 100644 index 0000000..01c9bd1 --- /dev/null +++ b/pkg/mining/supervisor_test.go @@ -0,0 +1,58 @@ +package mining + +import ( + "context" + "sync/atomic" + "testing" + "time" +) + +func TestSupervisor_RegisterTask_Good(t *testing.T) { + supervisor := NewTaskSupervisor() + defer supervisor.Stop() + + var executed atomic.Bool + supervisor.RegisterTask("test-task", func(ctx context.Context) { + executed.Store(true) + <-ctx.Done() + }, time.Second, 0) + + supervisor.Start() + time.Sleep(50 * time.Millisecond) + + if !executed.Load() { + t.Fatal("expected task to have executed") + } +} + +func TestSupervisor_RegisterTask_Bad(t *testing.T) { + supervisor := NewTaskSupervisor() + defer supervisor.Stop() + + // Start with no tasks — should not panic + supervisor.Start() + + _, _, found := supervisor.GetTaskStatus("nonexistent") + if found { + t.Fatal("expected task not found") + } +} + +func TestSupervisor_RegisterTask_Ugly(t *testing.T) { + supervisor := NewTaskSupervisor() + + var panicCount atomic.Int32 + supervisor.RegisterTask("panicking-task", func(ctx context.Context) { + panicCount.Add(1) + panic("intentional panic for testing") + }, 10*time.Millisecond, 2) + + supervisor.Start() + time.Sleep(100 * time.Millisecond) + supervisor.Stop() + + // Task should have been restarted after panic + if panicCount.Load() < 2 { + t.Fatalf("expected at least 2 executions after panic recovery, got %d", panicCount.Load()) + } +} diff --git a/pkg/mining/version_test.go b/pkg/mining/version_test.go new file mode 100644 index 0000000..431dd46 --- /dev/null +++ b/pkg/mining/version_test.go @@ -0,0 +1,31 @@ +package mining + +import ( + "testing" +) + +func TestVersion_GetVersion_Good(t *testing.T) { + result := GetVersion() + if result == "" { + t.Fatal("expected non-empty version string") + } +} + +func TestVersion_GetVersion_Bad(t *testing.T) { + // Default version is "dev" when ldflags are not set + result := GetVersion() + if result != "dev" { + // Not a failure — just means ldflags were set + t.Logf("version is %q (ldflags may be set)", result) + } +} + +func TestVersion_GetVersion_Ugly(t *testing.T) { + // All three version functions should return non-empty strings + if GetCommit() == "" { + t.Fatal("expected non-empty commit string") + } + if GetBuildDate() == "" { + t.Fatal("expected non-empty build date string") + } +} diff --git a/pkg/node/bufpool_test.go b/pkg/node/bufpool_test.go new file mode 100644 index 0000000..172f72e --- /dev/null +++ b/pkg/node/bufpool_test.go @@ -0,0 +1,69 @@ +package node + +import ( + "testing" +) + +func TestBufpool_MarshalJSON_Good(t *testing.T) { + data := map[string]string{"key": "value"} + result, err := MarshalJSON(data) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if len(result) == 0 { + t.Fatal("expected non-empty result") + } +} + +func TestBufpool_MarshalJSON_Bad(t *testing.T) { + // Channels cannot be marshalled + result, err := MarshalJSON(make(chan int)) + if err == nil { + t.Fatal("expected error for non-marshallable type") + } + if result != nil { + t.Fatalf("expected nil result, got %v", result) + } +} + +func TestBufpool_MarshalJSON_Ugly(t *testing.T) { + // nil value should produce "null" + result, err := MarshalJSON(nil) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if string(result) != "null" { + t.Fatalf("expected \"null\", got %q", string(result)) + } +} + +func TestBufpool_UnmarshalJSON_Good(t *testing.T) { + var target map[string]string + err := UnmarshalJSON([]byte(`{"key":"value"}`), &target) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if target["key"] != "value" { + t.Fatalf("expected value \"value\", got %q", target["key"]) + } +} + +func TestBufpool_UnmarshalJSON_Bad(t *testing.T) { + var target map[string]string + err := UnmarshalJSON([]byte(`not valid json`), &target) + if err == nil { + t.Fatal("expected error for invalid JSON") + } +} + +func TestBufpool_UnmarshalJSON_Ugly(t *testing.T) { + // Empty JSON object into a map + var target map[string]string + err := UnmarshalJSON([]byte(`{}`), &target) + if err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if len(target) != 0 { + t.Fatalf("expected empty map, got %d entries", len(target)) + } +} diff --git a/pkg/node/worker_test.go b/pkg/node/worker_test.go index c6db901..d864b7c 100644 --- a/pkg/node/worker_test.go +++ b/pkg/node/worker_test.go @@ -41,8 +41,8 @@ func TestWorker_NewWorker_Good(t *testing.T) { if worker == nil { t.Fatal("NewWorker returned nil") } - if worker.node != nodeManager { - t.Error("worker.node not set correctly") + if worker.nodeManager != nodeManager { + t.Error("worker.nodeManager not set correctly") } if worker.transport != transport { t.Error("worker.transport not set correctly")