From 4a04cd517f66a5191150848bfd1fbca9955d2243 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 24 May 2026 12:13:57 +0300 Subject: 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. --- internal/rpn/metric_type.go | 5 +++ internal/rpn/operations_metric_cmd.go | 2 +- internal/rpn/parse_category_test.go | 72 +++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 internal/rpn/parse_category_test.go 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") + } +} -- cgit v1.2.3