summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-24 12:13:57 +0300
committerPaul Buetow <paul@buetow.org>2026-05-24 12:13:57 +0300
commit4a04cd517f66a5191150848bfd1fbca9955d2243 (patch)
tree38397b5885e895f3233e3e531cba241b67b41aae
parent22bff05c18d40f52398ec6cbda9e4e4df24dbb45 (diff)
rpn: use named sentinel for invalid parseCategory result
Define const invalidCategory Category = -1 so parseCategory returns a clearly-invalid sentinel instead of Category(0)/Universal on unknown names. This prevents silent misclassification if a callers ignores the bool return value. Adds tests for parseCategory with known categories, unknown names, empty string, and sentinel validity.
-rw-r--r--internal/rpn/metric_type.go5
-rw-r--r--internal/rpn/operations_metric_cmd.go2
-rw-r--r--internal/rpn/parse_category_test.go72
3 files changed, 78 insertions, 1 deletions
diff --git a/internal/rpn/metric_type.go b/internal/rpn/metric_type.go
index 8f49ea4..6c77a8c 100644
--- a/internal/rpn/metric_type.go
+++ b/internal/rpn/metric_type.go
@@ -31,6 +31,11 @@ const (
_sentinel
)
+// invalidCategory is a sentinel value returned by parseCategory for unknown names.
+// It must not equal any valid Category (which start at 0), so callers that
+// ignore the bool return cannot accidentally treat it as Universal.
+const invalidCategory Category = -1
+
// String returns the human-readable name of the category.
func (c Category) String() string {
switch c {
diff --git a/internal/rpn/operations_metric_cmd.go b/internal/rpn/operations_metric_cmd.go
index 8e0e519..bde134b 100644
--- a/internal/rpn/operations_metric_cmd.go
+++ b/internal/rpn/operations_metric_cmd.go
@@ -90,7 +90,7 @@ func parseCategory(name string) (Category, bool) {
return cat, true
}
}
- return 0, false
+ return invalidCategory, false
}
// CustomShow returns detailed info for custom metrics.
diff --git a/internal/rpn/parse_category_test.go b/internal/rpn/parse_category_test.go
new file mode 100644
index 0000000..8f67b9f
--- /dev/null
+++ b/internal/rpn/parse_category_test.go
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2026 Paul Buetow
+
+package rpn
+
+import "testing"
+
+// TestParseCategoryKnown tests that known category names resolve correctly.
+func TestParseCategoryKnown(t *testing.T) {
+ tests := []struct {
+ name string
+ want Category
+ }{
+ {"Universal", Universal},
+ {"DataRate", DataRate},
+ {"DataSize", DataSize},
+ {"Time", Time},
+ {"Weight", Weight},
+ {"Speed", Speed},
+ {"Distance", Distance},
+ {"Custom", Custom},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, ok := parseCategory(tt.name)
+ if !ok {
+ t.Errorf("parseCategory(%q) = !ok, want ok", tt.name)
+ }
+ if got != tt.want {
+ t.Errorf("parseCategory(%q) = %v, want %v", tt.name, got, tt.want)
+ }
+ })
+ }
+}
+
+// TestParseCategoryUnknown does not return Universal.
+// If parseCategory returned Category(0) on failure, callers ignoring the
+// bool return would silently get Universal — this test prevents that regression.
+func TestParseCategoryUnknown(t *testing.T) {
+ got, ok := parseCategory("DoesNotExist")
+ if ok {
+ t.Fatal("parseCategory(\"DoesNotExist\") = ok, want !ok")
+ }
+ if got == Universal {
+ t.Errorf("parseCategory(\"DoesNotExist\") returned Universal (Category(0)); "+
+ "use a sentinel like Category(-1) so callers can't mistake it for a valid category")
+ }
+ if got != invalidCategory {
+ t.Errorf("parseCategory(\"DoesNotExist\") = %v, want %v (invalidCategory)", got, invalidCategory)
+ }
+}
+
+// TestParseCategoryEmpty tests that an empty string is not treated as a valid category.
+func TestParseCategoryEmpty(t *testing.T) {
+ got, ok := parseCategory("")
+ if ok {
+ t.Fatal("parseCategory(\"\") = ok, want !ok")
+ }
+ if got == Universal {
+ t.Error("parseCategory(\"\") must not return Universal")
+ }
+}
+
+// TestInvalidCategorySentinel verifies the sentinel is not a valid category.
+func TestInvalidCategorySentinel(t *testing.T) {
+ if invalidCategory >= 0 && invalidCategory < _sentinel {
+ t.Errorf("invalidCategory (%d) overlaps with valid range [0, %d)", invalidCategory, _sentinel)
+ }
+ if invalidCategory == Universal {
+ t.Error("invalidCategory must never equal Universal")
+ }
+}