@@ -563,6 +563,130 @@ func TestMetrics_RefreshSubmissionDuration_Empty(t *testing.T) {
563563 })
564564}
565565
566+ func TestMetrics_RecordSubmissionAttempt (t * testing.T ) {
567+ reg := prometheus .NewRegistry ()
568+ m := NewWithRegistry ("test" , reg )
569+
570+ // Test successful submission
571+ beforeAttempt := time .Now ()
572+ m .RecordSubmissionAttempt ("testchain" , "header" , true )
573+ afterAttempt := time .Now ()
574+
575+ // Verify counters
576+ attempts := getMetricValue (t , reg , "test_submission_attempts_total" , map [string ]string {
577+ "chain_id" : "testchain" ,
578+ "type" : "header" ,
579+ })
580+ require .Equal (t , float64 (1 ), attempts , "should have 1 attempt" )
581+
582+ // For successful submission, failures should be 0 (metric may not be exported if 0)
583+ // We'll check if the metric exists and has value 0, or doesn't exist (both are valid)
584+ failuresMetricFound := false
585+ failures := float64 (0 )
586+ metrics , err := reg .Gather ()
587+ require .NoError (t , err )
588+ for _ , mf := range metrics {
589+ if mf .GetName () == "test_submission_failures_total" {
590+ for _ , m := range mf .GetMetric () {
591+ // check if labels match
592+ match := true
593+ for _ , label := range m .GetLabel () {
594+ if expectedVal , ok := map [string ]string {"chain_id" : "testchain" , "type" : "header" }[label .GetName ()]; ok {
595+ if label .GetValue () != expectedVal {
596+ match = false
597+ break
598+ }
599+ }
600+ }
601+ if match && len (m .GetLabel ()) == 2 {
602+ failuresMetricFound = true
603+ if m .GetCounter () != nil {
604+ failures = m .GetCounter ().GetValue ()
605+ }
606+ break
607+ }
608+ }
609+ }
610+ }
611+ if failuresMetricFound {
612+ require .Equal (t , float64 (0 ), failures , "should have 0 failures" )
613+ } // else: metric not exported because value is 0, which is expected behavior
614+
615+ // Verify timestamps are within expected range
616+ lastAttemptTime := getMetricValue (t , reg , "test_last_submission_attempt_time" , map [string ]string {
617+ "chain_id" : "testchain" ,
618+ "type" : "header" ,
619+ })
620+ require .GreaterOrEqual (t , lastAttemptTime , float64 (beforeAttempt .Unix ()))
621+ require .LessOrEqual (t , lastAttemptTime , float64 (afterAttempt .Unix ()))
622+
623+ lastSuccessTime := getMetricValue (t , reg , "test_last_successful_submission_time" , map [string ]string {
624+ "chain_id" : "testchain" ,
625+ "type" : "header" ,
626+ })
627+ require .GreaterOrEqual (t , lastSuccessTime , float64 (beforeAttempt .Unix ()))
628+ require .LessOrEqual (t , lastSuccessTime , float64 (afterAttempt .Unix ()))
629+
630+ // Test failed submission
631+ beforeFailure := time .Now ()
632+ m .RecordSubmissionAttempt ("testchain" , "data" , false )
633+ afterFailure := time .Now ()
634+
635+ // Verify counters
636+ attempts = getMetricValue (t , reg , "test_submission_attempts_total" , map [string ]string {
637+ "chain_id" : "testchain" ,
638+ "type" : "data" ,
639+ })
640+ require .Equal (t , float64 (1 ), attempts , "should have 1 attempt for data" )
641+
642+ failures = getMetricValue (t , reg , "test_submission_failures_total" , map [string ]string {
643+ "chain_id" : "testchain" ,
644+ "type" : "data" ,
645+ })
646+ require .Equal (t , float64 (1 ), failures , "should have 1 failure for data" )
647+
648+ // Verify timestamps - should have attempt time but not success time for failed submission
649+ lastAttemptTime = getMetricValue (t , reg , "test_last_submission_attempt_time" , map [string ]string {
650+ "chain_id" : "testchain" ,
651+ "type" : "data" ,
652+ })
653+ require .GreaterOrEqual (t , lastAttemptTime , float64 (beforeFailure .Unix ()))
654+ require .LessOrEqual (t , lastAttemptTime , float64 (afterFailure .Unix ()))
655+
656+ // Last successful submission time should still be 0 for data type (never succeeded)
657+ // Gauge metrics with 0 values may not be exported, so we need to check if it exists
658+ var lastSuccessTimeData float64
659+ var successMetricFoundData bool
660+ metrics , err = reg .Gather ()
661+ require .NoError (t , err )
662+ for _ , mf := range metrics {
663+ if mf .GetName () == "test_last_successful_submission_time" {
664+ for _ , m := range mf .GetMetric () {
665+ // check if labels match
666+ match := true
667+ for _ , label := range m .GetLabel () {
668+ if expectedVal , ok := map [string ]string {"chain_id" : "testchain" , "type" : "data" }[label .GetName ()]; ok {
669+ if label .GetValue () != expectedVal {
670+ match = false
671+ break
672+ }
673+ }
674+ }
675+ if match && len (m .GetLabel ()) == 2 {
676+ successMetricFoundData = true
677+ if m .GetGauge () != nil {
678+ lastSuccessTimeData = m .GetGauge ().GetValue ()
679+ }
680+ break
681+ }
682+ }
683+ }
684+ }
685+ if successMetricFoundData {
686+ require .Equal (t , float64 (0 ), lastSuccessTimeData , "should have no successful submission time for data" )
687+ } // else: metric not exported because value is 0, which is expected behavior
688+ }
689+
566690// helper types for table tests
567691type blockToRecord struct {
568692 chain string
@@ -593,7 +717,7 @@ func calculateExpectedTotal(ranges []expectedRange, blobType string) uint64 {
593717 return total
594718}
595719
596- // getMetricValue retrieves the current value of a gauge metric
720+ // getMetricValue retrieves the current value of a metric ( gauge or counter)
597721func getMetricValue (t * testing.T , reg * prometheus.Registry , metricName string , labels map [string ]string ) float64 {
598722 t .Helper ()
599723 metrics , err := reg .Gather ()
@@ -613,7 +737,13 @@ func getMetricValue(t *testing.T, reg *prometheus.Registry, metricName string, l
613737 }
614738 }
615739 if match && len (m .GetLabel ()) == len (labels ) {
616- return m .GetGauge ().GetValue ()
740+ // Try gauge first, then counter
741+ if m .GetGauge () != nil {
742+ return m .GetGauge ().GetValue ()
743+ }
744+ if m .GetCounter () != nil {
745+ return m .GetCounter ().GetValue ()
746+ }
617747 }
618748 }
619749 }
0 commit comments