Garmin run classification and progression tracker. Go backend (MCP client to mcp-garmin, SQLite store, deterministic rule engine, REST API) and React/TS frontend (Dashboard, Review Queue, Workout Kinds). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
62 lines
1.8 KiB
Go
62 lines
1.8 KiB
Go
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")
|
|
}
|
|
}
|