From 31489715060b3f818ba384b46d0e881281f77889 Mon Sep 17 00:00:00 2001 From: Dylan Joss Date: Wed, 21 Jan 2026 00:15:35 -0800 Subject: [PATCH 1/3] test: add unit tests for RingBuffer in cmd/status Add comprehensive test coverage for the RingBuffer circular buffer data structure used in network history tracking. Test cases: - Constructor initialization (NewRingBuffer) - Empty buffer returns nil slice - Adding elements within capacity - Exact capacity boundary - Wrap-around behavior with chronological ordering - Multiple wrap-arounds stress test - Single-element buffer edge case - Slice returns independent copy (not reference) --- cmd/status/metrics_test.go | 160 +++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 cmd/status/metrics_test.go diff --git a/cmd/status/metrics_test.go b/cmd/status/metrics_test.go new file mode 100644 index 0000000..b269782 --- /dev/null +++ b/cmd/status/metrics_test.go @@ -0,0 +1,160 @@ +package main + +import ( + "reflect" + "testing" +) + +func TestNewRingBuffer(t *testing.T) { + tests := []struct { + name string + capacity int + }{ + {"small buffer", 5}, + {"standard buffer", 120}, + {"single element", 1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rb := NewRingBuffer(tt.capacity) + if rb == nil { + t.Fatal("NewRingBuffer returned nil") + } + if rb.cap != tt.capacity { + t.Errorf("expected capacity %d, got %d", tt.capacity, rb.cap) + } + if rb.size != 0 { + t.Errorf("expected size 0 for new buffer, got %d", rb.size) + } + if rb.index != 0 { + t.Errorf("expected index 0 for new buffer, got %d", rb.index) + } + if len(rb.data) != tt.capacity { + t.Errorf("expected data slice length %d, got %d", tt.capacity, len(rb.data)) + } + }) + } +} + +func TestRingBuffer_EmptyBuffer(t *testing.T) { + rb := NewRingBuffer(5) + result := rb.Slice() + + if result != nil { + t.Errorf("expected nil for empty buffer, got %v", result) + } +} + +func TestRingBuffer_AddWithinCapacity(t *testing.T) { + rb := NewRingBuffer(5) + + // Add 3 elements (less than capacity) + rb.Add(1.0) + rb.Add(2.0) + rb.Add(3.0) + + if rb.size != 3 { + t.Errorf("expected size 3, got %d", rb.size) + } + + result := rb.Slice() + expected := []float64{1.0, 2.0, 3.0} + + if !reflect.DeepEqual(result, expected) { + t.Errorf("expected %v, got %v", expected, result) + } +} + +func TestRingBuffer_ExactCapacity(t *testing.T) { + rb := NewRingBuffer(5) + + // Fill exactly to capacity + for i := 1; i <= 5; i++ { + rb.Add(float64(i)) + } + + if rb.size != 5 { + t.Errorf("expected size 5, got %d", rb.size) + } + + result := rb.Slice() + expected := []float64{1.0, 2.0, 3.0, 4.0, 5.0} + + if !reflect.DeepEqual(result, expected) { + t.Errorf("expected %v, got %v", expected, result) + } +} + +func TestRingBuffer_WrapAround(t *testing.T) { + rb := NewRingBuffer(5) + + // Add 7 elements to trigger wrap-around (2 past capacity) + // Internal state after: data=[6, 7, 3, 4, 5], index=2, size=5 + // Oldest element is at index 2 (value 3) + for i := 1; i <= 7; i++ { + rb.Add(float64(i)) + } + + if rb.size != 5 { + t.Errorf("expected size to cap at 5, got %d", rb.size) + } + + result := rb.Slice() + // Should return chronological order: 3, 4, 5, 6, 7 + expected := []float64{3.0, 4.0, 5.0, 6.0, 7.0} + + if !reflect.DeepEqual(result, expected) { + t.Errorf("expected %v, got %v", expected, result) + } +} + +func TestRingBuffer_MultipleWrapArounds(t *testing.T) { + rb := NewRingBuffer(3) + + // Add 10 elements (wraps multiple times) + for i := 1; i <= 10; i++ { + rb.Add(float64(i)) + } + + result := rb.Slice() + // Should have the last 3 values: 8, 9, 10 + expected := []float64{8.0, 9.0, 10.0} + + if !reflect.DeepEqual(result, expected) { + t.Errorf("expected %v, got %v", expected, result) + } +} + +func TestRingBuffer_SingleElementBuffer(t *testing.T) { + rb := NewRingBuffer(1) + + rb.Add(5.0) + result := rb.Slice() + if !reflect.DeepEqual(result, []float64{5.0}) { + t.Errorf("expected [5.0], got %v", result) + } + + // Overwrite the single element + rb.Add(10.0) + result = rb.Slice() + if !reflect.DeepEqual(result, []float64{10.0}) { + t.Errorf("expected [10.0], got %v", result) + } +} + +func TestRingBuffer_SliceReturnsNewSlice(t *testing.T) { + rb := NewRingBuffer(3) + rb.Add(1.0) + rb.Add(2.0) + + slice1 := rb.Slice() + slice2 := rb.Slice() + + // Modify slice1 and verify slice2 is unaffected + slice1[0] = 999.0 + + if slice2[0] == 999.0 { + t.Error("Slice should return a new copy, not a reference to internal data") + } +} From 046843a0125b44d278202cdb7da76781367ef4e2 Mon Sep 17 00:00:00 2001 From: Dylan Joss Date: Wed, 21 Jan 2026 11:29:37 -0800 Subject: [PATCH 2/3] Add more tests --- cmd/status/metrics_test.go | 89 +++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/cmd/status/metrics_test.go b/cmd/status/metrics_test.go index b269782..f0f0a11 100644 --- a/cmd/status/metrics_test.go +++ b/cmd/status/metrics_test.go @@ -22,16 +22,16 @@ func TestNewRingBuffer(t *testing.T) { t.Fatal("NewRingBuffer returned nil") } if rb.cap != tt.capacity { - t.Errorf("expected capacity %d, got %d", tt.capacity, rb.cap) + t.Errorf("NewRingBuffer(%d).cap = %d, want %d", tt.capacity, rb.cap, tt.capacity) } if rb.size != 0 { - t.Errorf("expected size 0 for new buffer, got %d", rb.size) + t.Errorf("NewRingBuffer(%d).size = %d, want 0", tt.capacity, rb.size) } if rb.index != 0 { - t.Errorf("expected index 0 for new buffer, got %d", rb.index) + t.Errorf("NewRingBuffer(%d).index = %d, want 0", tt.capacity, rb.index) } if len(rb.data) != tt.capacity { - t.Errorf("expected data slice length %d, got %d", tt.capacity, len(rb.data)) + t.Errorf("len(NewRingBuffer(%d).data) = %d, want %d", tt.capacity, len(rb.data), tt.capacity) } }) } @@ -39,10 +39,10 @@ func TestNewRingBuffer(t *testing.T) { func TestRingBuffer_EmptyBuffer(t *testing.T) { rb := NewRingBuffer(5) - result := rb.Slice() + got := rb.Slice() - if result != nil { - t.Errorf("expected nil for empty buffer, got %v", result) + if got != nil { + t.Errorf("Slice() on empty buffer = %v, want nil", got) } } @@ -55,14 +55,14 @@ func TestRingBuffer_AddWithinCapacity(t *testing.T) { rb.Add(3.0) if rb.size != 3 { - t.Errorf("expected size 3, got %d", rb.size) + t.Errorf("size after 3 adds = %d, want 3", rb.size) } - result := rb.Slice() - expected := []float64{1.0, 2.0, 3.0} + got := rb.Slice() + want := []float64{1.0, 2.0, 3.0} - if !reflect.DeepEqual(result, expected) { - t.Errorf("expected %v, got %v", expected, result) + if !reflect.DeepEqual(got, want) { + t.Errorf("Slice() = %v, want %v", got, want) } } @@ -75,14 +75,14 @@ func TestRingBuffer_ExactCapacity(t *testing.T) { } if rb.size != 5 { - t.Errorf("expected size 5, got %d", rb.size) + t.Errorf("size after filling to capacity = %d, want 5", rb.size) } - result := rb.Slice() - expected := []float64{1.0, 2.0, 3.0, 4.0, 5.0} + got := rb.Slice() + want := []float64{1.0, 2.0, 3.0, 4.0, 5.0} - if !reflect.DeepEqual(result, expected) { - t.Errorf("expected %v, got %v", expected, result) + if !reflect.DeepEqual(got, want) { + t.Errorf("Slice() = %v, want %v", got, want) } } @@ -97,15 +97,20 @@ func TestRingBuffer_WrapAround(t *testing.T) { } if rb.size != 5 { - t.Errorf("expected size to cap at 5, got %d", rb.size) + t.Errorf("size after wrap-around = %d, want 5", rb.size) } - result := rb.Slice() - // Should return chronological order: 3, 4, 5, 6, 7 - expected := []float64{3.0, 4.0, 5.0, 6.0, 7.0} + // Verify index points to oldest element position + if rb.index != 2 { + t.Errorf("index after adding 7 elements to cap-5 buffer = %d, want 2", rb.index) + } - if !reflect.DeepEqual(result, expected) { - t.Errorf("expected %v, got %v", expected, result) + got := rb.Slice() + // Should return chronological order: oldest (3) to newest (7) + want := []float64{3.0, 4.0, 5.0, 6.0, 7.0} + + if !reflect.DeepEqual(got, want) { + t.Errorf("Slice() = %v, want %v", got, want) } } @@ -117,12 +122,12 @@ func TestRingBuffer_MultipleWrapArounds(t *testing.T) { rb.Add(float64(i)) } - result := rb.Slice() + got := rb.Slice() // Should have the last 3 values: 8, 9, 10 - expected := []float64{8.0, 9.0, 10.0} + want := []float64{8.0, 9.0, 10.0} - if !reflect.DeepEqual(result, expected) { - t.Errorf("expected %v, got %v", expected, result) + if !reflect.DeepEqual(got, want) { + t.Errorf("Slice() after 10 adds to cap-3 buffer = %v, want %v", got, want) } } @@ -130,16 +135,14 @@ func TestRingBuffer_SingleElementBuffer(t *testing.T) { rb := NewRingBuffer(1) rb.Add(5.0) - result := rb.Slice() - if !reflect.DeepEqual(result, []float64{5.0}) { - t.Errorf("expected [5.0], got %v", result) + if got := rb.Slice(); !reflect.DeepEqual(got, []float64{5.0}) { + t.Errorf("Slice() = %v, want [5.0]", got) } // Overwrite the single element rb.Add(10.0) - result = rb.Slice() - if !reflect.DeepEqual(result, []float64{10.0}) { - t.Errorf("expected [10.0], got %v", result) + if got := rb.Slice(); !reflect.DeepEqual(got, []float64{10.0}) { + t.Errorf("Slice() after overwrite = %v, want [10.0]", got) } } @@ -152,9 +155,27 @@ func TestRingBuffer_SliceReturnsNewSlice(t *testing.T) { slice2 := rb.Slice() // Modify slice1 and verify slice2 is unaffected + // This ensures Slice() returns a copy, not a reference to internal data slice1[0] = 999.0 if slice2[0] == 999.0 { - t.Error("Slice should return a new copy, not a reference to internal data") + t.Error("Slice() should return a new copy, not a reference to internal data") + } +} + +func TestRingBuffer_NegativeAndZeroValues(t *testing.T) { + rb := NewRingBuffer(4) + + // Test that negative and zero values are handled correctly + rb.Add(-5.0) + rb.Add(0.0) + rb.Add(-0.0) // Negative zero should work same as zero + rb.Add(3.5) + + got := rb.Slice() + want := []float64{-5.0, 0.0, 0.0, 3.5} + + if !reflect.DeepEqual(got, want) { + t.Errorf("Slice() with negative/zero values = %v, want %v", got, want) } } From 3c761865f8918594c32ca2dd1d64b7496e3eae30 Mon Sep 17 00:00:00 2001 From: Dylan Joss Date: Wed, 21 Jan 2026 11:46:50 -0800 Subject: [PATCH 3/3] Uses slices for equality comparison --- cmd/status/metrics_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/status/metrics_test.go b/cmd/status/metrics_test.go index f0f0a11..5515104 100644 --- a/cmd/status/metrics_test.go +++ b/cmd/status/metrics_test.go @@ -1,7 +1,7 @@ package main import ( - "reflect" + "slices" "testing" ) @@ -61,7 +61,7 @@ func TestRingBuffer_AddWithinCapacity(t *testing.T) { got := rb.Slice() want := []float64{1.0, 2.0, 3.0} - if !reflect.DeepEqual(got, want) { + if !slices.Equal(got, want) { t.Errorf("Slice() = %v, want %v", got, want) } } @@ -81,7 +81,7 @@ func TestRingBuffer_ExactCapacity(t *testing.T) { got := rb.Slice() want := []float64{1.0, 2.0, 3.0, 4.0, 5.0} - if !reflect.DeepEqual(got, want) { + if !slices.Equal(got, want) { t.Errorf("Slice() = %v, want %v", got, want) } } @@ -109,7 +109,7 @@ func TestRingBuffer_WrapAround(t *testing.T) { // Should return chronological order: oldest (3) to newest (7) want := []float64{3.0, 4.0, 5.0, 6.0, 7.0} - if !reflect.DeepEqual(got, want) { + if !slices.Equal(got, want) { t.Errorf("Slice() = %v, want %v", got, want) } } @@ -126,7 +126,7 @@ func TestRingBuffer_MultipleWrapArounds(t *testing.T) { // Should have the last 3 values: 8, 9, 10 want := []float64{8.0, 9.0, 10.0} - if !reflect.DeepEqual(got, want) { + if !slices.Equal(got, want) { t.Errorf("Slice() after 10 adds to cap-3 buffer = %v, want %v", got, want) } } @@ -135,13 +135,13 @@ func TestRingBuffer_SingleElementBuffer(t *testing.T) { rb := NewRingBuffer(1) rb.Add(5.0) - if got := rb.Slice(); !reflect.DeepEqual(got, []float64{5.0}) { + if got := rb.Slice(); !slices.Equal(got, []float64{5.0}) { t.Errorf("Slice() = %v, want [5.0]", got) } // Overwrite the single element rb.Add(10.0) - if got := rb.Slice(); !reflect.DeepEqual(got, []float64{10.0}) { + if got := rb.Slice(); !slices.Equal(got, []float64{10.0}) { t.Errorf("Slice() after overwrite = %v, want [10.0]", got) } } @@ -169,13 +169,13 @@ func TestRingBuffer_NegativeAndZeroValues(t *testing.T) { // Test that negative and zero values are handled correctly rb.Add(-5.0) rb.Add(0.0) - rb.Add(-0.0) // Negative zero should work same as zero + rb.Add(0.0) rb.Add(3.5) got := rb.Slice() want := []float64{-5.0, 0.0, 0.0, 3.5} - if !reflect.DeepEqual(got, want) { + if !slices.Equal(got, want) { t.Errorf("Slice() with negative/zero values = %v, want %v", got, want) } }