package ai import ( "github.com/google/uuid" "strings" "testing" ) // TestValidateIntentArgsClampsAndChecks proves the structured API hardening: a bounded ParamInt (max_depth) // is clamped to its range and a ParamEnum value outside its allowed set is refused, so a caller cannot bind // an unbounded recursion depth or an out of vocabulary enum on the /ai/query path. func TestValidateIntentArgsClampsAndChecks(t *testing.T) { closure, ok := IntentByName(IntentSkillSubstitutionClosure) if !ok { t.Fatalf("intent %q not registered", IntentSkillSubstitutionClosure) } id := uuid.New() // max_depth above Max clamps to Max (5), below Min clamps to Min (1), in range is unchanged. for _, tc := range []struct { in, want int }{{9999999, 5}, {0, 1}, {3, 3}, {-7, 1}, {5, 5}} { out, err := validateIntentArgs(closure, []any{id, tc.in}) if err != nil { t.Fatalf("validateIntentArgs(max_depth=%d): %v", tc.in, err) } if got := out[1].(int); got != tc.want { t.Errorf("max_depth %d clamped to %d, want %d", tc.in, got, tc.want) } } // A JSON style float64 whole number coerces and clamps; a fractional one is refused. if out, err := validateIntentArgs(closure, []any{id, float64(2)}); err != nil || out[1].(int) != 2 { t.Errorf("float64(2) should coerce to int 2, got %v err %v", out, err) } if _, err := validateIntentArgs(closure, []any{id, 2.5}); err == nil { t.Error("a fractional max_depth must be refused") } // A ParamEnum value outside Allowed is refused; a valid one passes. clearance, ok := IntentByName("count_candidates_by_clearance") if !ok { t.Fatal("count_candidates_by_clearance not registered") } if _, err := validateIntentArgs(clearance, []any{"cosmic_top_secret"}); err == nil { t.Error("an out of vocabulary clearance enum must be refused") } if _, err := validateIntentArgs(clearance, []any{"secret"}); err != nil { t.Errorf("a valid clearance enum must pass, got %v", err) } if _, err := validateIntentArgs(clearance, []any{""}); err != nil { t.Errorf("the empty all clearances sentinel must pass, got %v", err) } } // TestRecruitingIntentsAreRegisteredAndSafe covers the three recruiting operations intents. They must be // SQL intents, never model intents, so the model can only select one and bind its arguments. It can // never author the query. func TestRecruitingIntentsAreRegisteredAndSafe(t *testing.T) { want := []string{ "requirements_without_submissions", "available_candidates_not_on_a_slate", "slates_with_pending_decisions", } for _, name := range want { it, ok := IntentByName(name) if !ok { t.Errorf("intent %q is not registered", name) continue } if !it.IsSQLIntent() { t.Errorf("intent %q must be a SQL intent, never a model intent", name) } if it.Description == "" { t.Errorf("intent %q has no description, so the router cannot select it", name) } if err := GuardReadOnlyQuery(it.SQL, ReadAllowlist); err != nil { t.Errorf("intent %q fails the read only guard: %v", name, err) } } } // TestRecruitingIntentsDoNotReachUsers guards a deliberate decision. The users table is NOT on the read // allowlist because it carries email addresses, and a recruiter throughput intent is not worth widening // the model's reach to PII. The dashboard shows that attribution through a normal service query instead. func TestRecruitingIntentsDoNotReachUsers(t *testing.T) { for _, it := range Registry { if strings.Contains(strings.ToLower(it.SQL), " users ") || strings.Contains(strings.ToLower(it.SQL), "join users") || strings.Contains(strings.ToLower(it.SQL), "from users") { t.Errorf("intent %q reads the users table, which is not allowlisted", it.Name) } } if ReadAllowlist["users"] { t.Error("users was added to the read allowlist; that widens the brain to email addresses and needs a reviewed decision") } }