package sandbox import ( "testing" "go.starlark.net/starlark" ) func runRoutingScript(t *testing.T, script string) starlark.StringDict { t.Helper() mod := BuildRoutingModule() predeclared := starlark.StringDict{"routing": mod} globals, err := starlark.ExecFile(&starlark.Thread{Name: "test"}, "test.star", script, predeclared) if err != nil { t.Fatal(err) } return globals } func runRoutingScriptErr(t *testing.T, script string) error { t.Helper() mod := BuildRoutingModule() predeclared := starlark.StringDict{"routing": mod} _, err := starlark.ExecFile(&starlark.Thread{Name: "test"}, "test.star", script, predeclared) return err } func TestRoutingEvaluate_SingleMatch(t *testing.T) { globals := runRoutingScript(t, ` result = routing.evaluate([ {"field": "priority", "op": "eq", "value": "high", "target": "escalation"}, ], {"priority": "high"}) `) result := globals["result"] if s, ok := result.(starlark.String); !ok || string(s) != "escalation" { t.Fatalf("expected 'escalation', got %v", result) } } func TestRoutingEvaluate_FirstMatchWins(t *testing.T) { globals := runRoutingScript(t, ` result = routing.evaluate([ {"field": "amount", "op": "gt", "value": 100, "target": "big"}, {"field": "amount", "op": "gt", "value": 50, "target": "medium"}, ], {"amount": 200}) `) result := globals["result"] if s, ok := result.(starlark.String); !ok || string(s) != "big" { t.Fatalf("expected 'big', got %v", result) } } func TestRoutingEvaluate_NoMatch(t *testing.T) { globals := runRoutingScript(t, ` result = routing.evaluate([ {"field": "status", "op": "eq", "value": "done", "target": "finish"}, ], {"status": "pending"}) `) if globals["result"] != starlark.None { t.Fatalf("expected None, got %v", globals["result"]) } } func TestRoutingEvaluate_EmptyRules(t *testing.T) { globals := runRoutingScript(t, ` result = routing.evaluate([], {"x": 1}) `) if globals["result"] != starlark.None { t.Fatalf("expected None, got %v", globals["result"]) } } func TestRoutingEvaluate_AllOperators(t *testing.T) { globals := runRoutingScript(t, ` # exists r1 = routing.evaluate([{"field": "x", "op": "exists", "value": "", "target": "yes"}], {"x": 1}) # not_exists r2 = routing.evaluate([{"field": "x", "op": "not_exists", "value": "", "target": "yes"}], {"y": 1}) # eq r3 = routing.evaluate([{"field": "s", "op": "eq", "value": "abc", "target": "yes"}], {"s": "abc"}) # neq r4 = routing.evaluate([{"field": "s", "op": "neq", "value": "abc", "target": "yes"}], {"s": "xyz"}) # gt r5 = routing.evaluate([{"field": "n", "op": "gt", "value": 10, "target": "yes"}], {"n": 20}) # lt r6 = routing.evaluate([{"field": "n", "op": "lt", "value": 10, "target": "yes"}], {"n": 5}) # gte r7 = routing.evaluate([{"field": "n", "op": "gte", "value": 10, "target": "yes"}], {"n": 10}) # lte r8 = routing.evaluate([{"field": "n", "op": "lte", "value": 10, "target": "yes"}], {"n": 10}) # in r9 = routing.evaluate([{"field": "s", "op": "in", "value": ["a", "b", "c"], "target": "yes"}], {"s": "b"}) # contains r10 = routing.evaluate([{"field": "s", "op": "contains", "value": "ell", "target": "yes"}], {"s": "hello"}) `) for _, name := range []string{"r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10"} { v := globals[name] if s, ok := v.(starlark.String); !ok || string(s) != "yes" { t.Errorf("%s: expected 'yes', got %v", name, v) } } } func TestRoutingEvaluate_TypeCoercion(t *testing.T) { globals := runRoutingScript(t, ` # Numeric string vs int r1 = routing.evaluate([ {"field": "n", "op": "gt", "value": 10, "target": "yes"}, ], {"n": "20"}) # Int equality via string normalization r2 = routing.evaluate([ {"field": "n", "op": "eq", "value": 42, "target": "yes"}, ], {"n": 42}) `) for _, name := range []string{"r1", "r2"} { v := globals[name] if s, ok := v.(starlark.String); !ok || string(s) != "yes" { t.Errorf("%s: expected 'yes', got %v", name, v) } } } func TestRoutingEvaluate_BadInput(t *testing.T) { // rules must be a list err1 := runRoutingScriptErr(t, `routing.evaluate("bad", {})`) if err1 == nil { t.Fatal("expected error for non-list rules") } // data must be a dict err2 := runRoutingScriptErr(t, `routing.evaluate([], "bad")`) if err2 == nil { t.Fatal("expected error for non-dict data") } // rule missing required fields err3 := runRoutingScriptErr(t, `routing.evaluate([{"field": "x"}], {})`) if err3 == nil { t.Fatal("expected error for incomplete rule") } } func TestRoutingEvaluate_MissingFields(t *testing.T) { globals := runRoutingScript(t, ` # Field not in data — eq should not match r1 = routing.evaluate([ {"field": "absent", "op": "eq", "value": "x", "target": "bad"}, ], {"other": "y"}) # not_exists matches when field is absent r2 = routing.evaluate([ {"field": "absent", "op": "not_exists", "value": "", "target": "good"}, ], {"other": "y"}) `) if globals["r1"] != starlark.None { t.Fatalf("r1: expected None for missing field, got %v", globals["r1"]) } if s, ok := globals["r2"].(starlark.String); !ok || string(s) != "good" { t.Fatalf("r2: expected 'good', got %v", globals["r2"]) } }