diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-24 12:13:57 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-24 12:13:57 +0300 |
| commit | 4a04cd517f66a5191150848bfd1fbca9955d2243 (patch) | |
| tree | 38397b5885e895f3233e3e531cba241b67b41aae | |
| parent | 22bff05c18d40f52398ec6cbda9e4e4df24dbb45 (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.go | 5 | ||||
| -rw-r--r-- | internal/rpn/operations_metric_cmd.go | 2 | ||||
| -rw-r--r-- | internal/rpn/parse_category_test.go | 72 |
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") + } +} |
