@@ -14,6 +14,7 @@ import (
14
14
"testing"
15
15
"time"
16
16
17
+ kitlog "github.com/go-kit/log"
17
18
"github.com/prometheus/client_golang/prometheus/testutil"
18
19
"github.com/prometheus/prometheus/model/labels"
19
20
"github.com/stretchr/testify/assert"
@@ -23,6 +24,7 @@ import (
23
24
24
25
"github.com/grafana/dskit/flagext"
25
26
27
+ "github.com/grafana/loki/v3/pkg/logproto"
26
28
util_log "github.com/grafana/loki/v3/pkg/util/log"
27
29
)
28
30
@@ -573,6 +575,80 @@ func TestRetentionPeriodToString(t *testing.T) {
573
575
}
574
576
}
575
577
578
+ // TestNegativeSizeHandling tests that the code handles negative size values
579
+ // properly without causing a panic when incrementing Prometheus counters.
580
+ func TestNegativeSizeHandling (t * testing.T ) {
581
+ // Reset metrics for accurate testing
582
+ structuredMetadataBytesIngested .Reset ()
583
+ bytesIngested .Reset ()
584
+ linesIngested .Reset ()
585
+
586
+ // Create a custom request parser that will generate negative sizes
587
+ var mockParser RequestParser = func (_ string , _ * http.Request , _ Limits , _ int , _ UsageTracker , _ StreamResolver , _ bool , _ kitlog.Logger ) (* logproto.PushRequest , * Stats , error ) {
588
+ // Create a minimal valid request
589
+ req := & logproto.PushRequest {
590
+ Streams : []logproto.Stream {
591
+ {
592
+ Labels : `{foo="bar"}` ,
593
+ Entries : []logproto.Entry {
594
+ {
595
+ Timestamp : time .Now (),
596
+ Line : "test line" ,
597
+ },
598
+ },
599
+ },
600
+ },
601
+ }
602
+
603
+ // Create stats with negative sizes to test our guard clauses
604
+ stats := NewPushStats ()
605
+ policy := ""
606
+ retention := time .Hour
607
+
608
+ // Set up negative sizes in both maps
609
+ stats .LogLinesBytes [policy ] = make (map [time.Duration ]int64 )
610
+ stats.LogLinesBytes [policy ][retention ] = - 100
611
+
612
+ stats .StructuredMetadataBytes [policy ] = make (map [time.Duration ]int64 )
613
+ stats.StructuredMetadataBytes [policy ][retention ] = - 200
614
+
615
+ return req , stats , nil
616
+ }
617
+
618
+ // Create a mock request
619
+ request := httptest .NewRequest ("POST" , "/loki/api/v1/push" , strings .NewReader ("{}" ))
620
+ request .Header .Add ("Content-Type" , "application/json" )
621
+
622
+ // Use a mock stream resolver to ensure consistent results
623
+ streamResolver := newMockStreamResolver ("fake" , & fakeLimits {})
624
+
625
+ // This should not panic with our guard clauses in place
626
+ _ , err := ParseRequest (
627
+ util_log .Logger ,
628
+ "fake" ,
629
+ 100 << 20 ,
630
+ request ,
631
+ & fakeLimits {},
632
+ mockParser ,
633
+ NewMockTracker (),
634
+ streamResolver ,
635
+ false ,
636
+ )
637
+
638
+ // No error should be returned
639
+ require .NoError (t , err )
640
+
641
+ // Check that the metrics were not incremented for negative values
642
+ userID := "fake"
643
+ isAggregatedMetric := "false"
644
+ policy := ""
645
+
646
+ // Verify no counters were incremented since all sizes were negative
647
+ // This test passes if no panic occurred and the counters remain at 0
648
+ require .Equal (t , float64 (0 ), testutil .ToFloat64 (bytesIngested .WithLabelValues (userID , "1" , isAggregatedMetric , policy )))
649
+ require .Equal (t , float64 (0 ), testutil .ToFloat64 (structuredMetadataBytesIngested .WithLabelValues (userID , "1" , isAggregatedMetric , policy )))
650
+ }
651
+
576
652
type fakeLimits struct {
577
653
enabled bool
578
654
labels []string
0 commit comments