Files
geniusrun/backend/internal/classify/rule_test.go

62 lines
1.8 KiB
Go
Raw Normal View History

package classify
import (
"encoding/json"
"testing"
)
func TestNode_ValidateAcceptsWellFormedTree(t *testing.T) {
n := Node{Match: MatchAll, Conditions: []Node{
{Metric: "avg_pace_sec_per_km", Op: OpBetween, Value: []any{270.0, 300.0}},
{Match: MatchAny, Conditions: []Node{
{Metric: "aerobic_training_effect", Op: OpGte, Value: 3.5},
{Metric: "lap_interval_pattern", Op: OpEq, Value: true},
}},
}}
if err := n.Validate(); err != nil {
t.Errorf("Validate() = %v, want nil", err)
}
}
func TestNode_ValidateRejectsMalformed(t *testing.T) {
cases := []struct {
name string
n Node
}{
{"unknown match", Node{Match: "xor", Conditions: []Node{{Metric: "x", Op: OpGt, Value: 1.0}}}},
{"branch with no conditions", Node{Match: MatchAll}},
{"leaf with no metric", Node{Op: OpGt, Value: 1.0}},
{"between with non-array value", Node{Metric: "x", Op: OpBetween, Value: 5.0}},
{"between with wrong-length array", Node{Metric: "x", Op: OpBetween, Value: []any{1.0}}},
{"unsupported op", Node{Metric: "x", Op: "~=", Value: 1.0}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if err := c.n.Validate(); err == nil {
t.Error("Validate() = nil, want an error")
}
})
}
}
func TestNode_RoundTripsThroughJSON(t *testing.T) {
raw := `{
"match": "all",
"conditions": [
{"metric": "avg_pace_sec_per_km", "op": "between", "value": [270, 300]},
{"metric": "lap_interval_pattern", "op": "==", "value": true}
]
}`
var n Node
if err := json.Unmarshal([]byte(raw), &n); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if err := n.Validate(); err != nil {
t.Fatalf("Validate() after round-trip = %v", err)
}
matched, _ := n.Evaluate(MetricContext{"avg_pace_sec_per_km": 285, "lap_interval_pattern": 1})
if !matched {
t.Error("expected match after JSON round-trip")
}
}