package ai import ( "context" "testing" ) // Trust-boundary suite H1.6, invariant (c) part 1: the AI gate stays gated. // No-consent / zero-budget / exhausted-budget always PARK and call the // provider exactly zero times before the gate. A future second entry point, // or a refactor that moves the budget check below the provider call, fails // this one named guard. func TestBoundary_GatedNeverSpends(t *testing.T) { ctx := context.Background() cases := []struct { name string consent bool budget int prime bool // run one successful pass first, to exhaust a budget of 1 wantCalls int // provider calls expected after the gated invocation }{ {"no-consent", false, 5, false, 0}, {"zero-budget", true, 0, false, 0}, {"exhausted-budget", true, 1, true, 1}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { fp := &fakeProvider{text: "ok"} g, state := newGate(t, fp, tc.consent, tc.budget) if tc.prime { out, err := g.Run(ctx, Request{Label: "prime", User: "p"}) if err != nil || !out.Ran { t.Fatalf("priming pass should run: out=%+v err=%v", out, err) } } out, err := g.Run(ctx, Request{Label: "b", User: "p"}) if err != nil { t.Fatalf("gated call returned a hard error: %v", err) } assertParked(t, state, out) if fp.calls != tc.wantCalls { t.Errorf("provider calls = %d, want %d (the gate must park BEFORE the provider)", fp.calls, tc.wantCalls) } }) } }