From 1c75f4fcb8a94fc0d3c61da0dd54beba1da2c02e Mon Sep 17 00:00:00 2001
From: alanprot <alanprot@gmail.com>
Date: Mon, 27 Jan 2025 15:11:23 -0800
Subject: [PATCH 1/4] Allowing to create Not/Regex/NotRegex matchers

Signed-off-by: alanprot <alanprot@gmail.com>
---
 .github/workflows/ci.yml |  6 ++----
 walk.go                  | 39 ++++++++++++++++++++++++++++++++++++---
 walk_test.go             | 14 +++++++++++---
 3 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dbb1d7f..ce5664d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,7 +31,5 @@ jobs:
         with:
           go-version-file: .go-version
           cache: true
-      - name: Lint
-        uses: golangci/golangci-lint-action@v6.1.0
-        with:
-          version: v1.59.0
+      - name: Golangci-lint
+        uses: golangci/golangci-lint-action@v6.2.0
diff --git a/walk.go b/walk.go
index 2426841..74ac07d 100644
--- a/walk.go
+++ b/walk.go
@@ -460,12 +460,45 @@ func (s *PromQLSmith) walkLabelMatchers() []*labels.Matcher {
 		lbls = append(lbls, l)
 	})
 
+	valF := func(v string) string {
+		val := s.rnd.Float64()
+		switch {
+		case val > 0.95:
+			return ""
+		case val > 0.90:
+			return ".*"
+		case val > 0.85:
+			return ".+"
+		case val > 0.75:
+			return fmt.Sprintf(".*%v", v[len(v)/2:])
+		default:
+			return fmt.Sprintf("%v.*", v[:len(v)/2])
+		}
+	}
+
 	for i := 0; i < items; i++ {
 
+		var matcher *labels.Matcher
+
 		if lbls[orders[i]].Name == labels.MetricName {
 			containsName = true
+			matcher = labels.MustNewMatcher(labels.MatchEqual, lbls[orders[i]].Name, lbls[orders[i]].Value)
+		} else {
+			res := s.rnd.Intn(4)
+			matchType := labels.MatchType(res)
+			switch matchType {
+			case labels.MatchEqual:
+				matcher = labels.MustNewMatcher(labels.MatchEqual, lbls[orders[i]].Name, lbls[orders[i]].Value)
+			case labels.MatchNotEqual:
+				matcher = labels.MustNewMatcher(labels.MatchNotEqual, lbls[orders[i]].Name, lbls[orders[i]].Value)
+			case labels.MatchRegexp:
+				matcher = labels.MustNewMatcher(labels.MatchRegexp, lbls[orders[i]].Name, valF(lbls[orders[i]].Value))
+			case labels.MatchNotRegexp:
+				matcher = labels.MustNewMatcher(labels.MatchNotRegexp, lbls[orders[i]].Name, valF(lbls[orders[i]].Value))
+			}
 		}
-		matchers = append(matchers, labels.MustNewMatcher(labels.MatchEqual, lbls[orders[i]].Name, lbls[orders[i]].Value))
+
+		matchers = append(matchers, matcher)
 	}
 
 	if !containsName {
@@ -482,8 +515,8 @@ func (s *PromQLSmith) walkLabelMatchers() []*labels.Matcher {
 	return matchers
 }
 
-// walkSelectors is similar to walkLabelMatchers, but used for generating various
-// types of matchers more than simple equal matcher.
+// walkSelectors is similar to walkLabelMatchers, but does not guarantee the equal
+// matcher on the metric name
 func (s *PromQLSmith) walkSelectors() []*labels.Matcher {
 	if len(s.seriesSet) == 0 {
 		return nil
diff --git a/walk_test.go b/walk_test.go
index 37d1670..d61b35a 100644
--- a/walk_test.go
+++ b/walk_test.go
@@ -497,7 +497,7 @@ func TestWalkVectorSelector(t *testing.T) {
 	require.True(t, ok)
 	containsMetricName := false
 	for _, matcher := range vs.LabelMatchers {
-		require.Equal(t, labels.MatchEqual, matcher.Type)
+		require.LessOrEqual(t, matcher.Type, 4)
 		if matcher.Name == labels.MetricName {
 			containsMetricName = true
 		}
@@ -509,7 +509,8 @@ func TestWalkLabelMatchers(t *testing.T) {
 	rnd := rand.New(rand.NewSource(time.Now().Unix()))
 	opts := []Option{WithEnableOffset(true), WithEnableAtModifier(true)}
 	for i, tc := range []struct {
-		ss []labels.Labels
+		expectedMatchers int
+		ss               []labels.Labels
 	}{
 		{
 			ss: nil,
@@ -526,9 +527,16 @@ func TestWalkLabelMatchers(t *testing.T) {
 	} {
 		t.Run(fmt.Sprintf("test_case_%d", i), func(t *testing.T) {
 			p := New(rnd, tc.ss, opts...)
+			labelNames := make(map[string]struct{})
+			for _, s := range tc.ss {
+				s.Range(func(l labels.Label) {
+					labelNames[l.Name] = struct{}{}
+				})
+			}
 			matchers := p.walkLabelMatchers()
 			for _, matcher := range matchers {
-				require.Equal(t, labels.MatchEqual, matcher.Type)
+				require.LessOrEqual(t, matcher.Type, 4)
+				require.Contains(t, labelNames, matcher.Name)
 			}
 		})
 	}

From 7608ad20b7956eb3853d63a7103945b325bfcea1 Mon Sep 17 00:00:00 2001
From: alanprot <alanprot@gmail.com>
Date: Mon, 27 Jan 2025 15:24:42 -0800
Subject: [PATCH 2/4] fix lint

Signed-off-by: alanprot <alanprot@gmail.com>
---
 walk.go | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/walk.go b/walk.go
index 74ac07d..e7b44d7 100644
--- a/walk.go
+++ b/walk.go
@@ -720,13 +720,6 @@ func keepValueTypes(input []parser.ValueType, keep []parser.ValueType) []parser.
 	return out
 }
 
-func min(a, b int) int {
-	if a > b {
-		return b
-	}
-	return a
-}
-
 // generate a non-zero float64 value randomly.
 func getNonZeroFloat64(rnd *rand.Rand) float64 {
 	for {

From f9908dc15a131f27f8eff759d912541f81c74506 Mon Sep 17 00:00:00 2001
From: alanprot <alanprot@gmail.com>
Date: Mon, 27 Jan 2025 15:30:03 -0800
Subject: [PATCH 3/4] Adding != case

Signed-off-by: alanprot <alanprot@gmail.com>
---
 walk.go | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/walk.go b/walk.go
index e7b44d7..d493745 100644
--- a/walk.go
+++ b/walk.go
@@ -490,7 +490,11 @@ func (s *PromQLSmith) walkLabelMatchers() []*labels.Matcher {
 			case labels.MatchEqual:
 				matcher = labels.MustNewMatcher(labels.MatchEqual, lbls[orders[i]].Name, lbls[orders[i]].Value)
 			case labels.MatchNotEqual:
-				matcher = labels.MustNewMatcher(labels.MatchNotEqual, lbls[orders[i]].Name, lbls[orders[i]].Value)
+				val := lbls[orders[i]].Value
+				if s.rnd.Float64() > 0.9 {
+					val = ""
+				}
+				matcher = labels.MustNewMatcher(labels.MatchNotEqual, lbls[orders[i]].Name, val)
 			case labels.MatchRegexp:
 				matcher = labels.MustNewMatcher(labels.MatchRegexp, lbls[orders[i]].Name, valF(lbls[orders[i]].Value))
 			case labels.MatchNotRegexp:

From 3dda82ab3dc91751d56dbfa9cfc9c46081c0f174 Mon Sep 17 00:00:00 2001
From: alanprot <alanprot@gmail.com>
Date: Mon, 27 Jan 2025 16:13:17 -0800
Subject: [PATCH 4/4] using ceil when  discovering how many extra matcher will
 be added on walkLabelMatchers function

Signed-off-by: alanprot <alanprot@gmail.com>
---
 walk.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/walk.go b/walk.go
index d493745..06d5506 100644
--- a/walk.go
+++ b/walk.go
@@ -2,6 +2,7 @@ package promqlsmith
 
 import (
 	"fmt"
+	"math"
 	"math/rand"
 	"sort"
 	"strings"
@@ -452,7 +453,7 @@ func (s *PromQLSmith) walkLabelMatchers() []*labels.Matcher {
 	}
 	series := s.seriesSet[s.rnd.Intn(len(s.seriesSet))]
 	orders := s.rnd.Perm(series.Len())
-	items := s.rnd.Intn((series.Len() + 1) / 2)
+	items := s.rnd.Intn(int(math.Ceil(float64(series.Len()+1) / 2)))
 	matchers := make([]*labels.Matcher, 0, items)
 	containsName := false
 	lbls := make([]labels.Label, 0, series.Len())