Files
cairnobs/api/ai/router/router_test.go
T
jcoffey-dev 7d316f92db Phase 7: AI-assisted query authoring (autocomplete, explain, fix, optimize, NL translation)
Adds a self-hosted (Ollama, qwen2.5-coder) model provider abstraction
with a pluggable opt-in cloud adapter, schema grounding, and a shared
cost/safety guard every AI-suggested query is assessed against --
compiling to and executing through the same unchanged Phase 2 IR/
compiler and Phase 4 tenant scoping as a hand-written query, no
parallel execution path.

Track A (built into the query bar): inline ghost-text autocomplete,
"Explain this query", "Fix this query" with a diff view, and a
rule-based "Optimize" suggestion. Track B: natural-language-to-query
translation, always a separate review step from execution, with
`sentryctl query --nl` requiring explicit confirmation to run.
Every accepted/dismissed translate-fix-optimize interaction is logged
into the same append-only audit_log table Phase 4 built.

Two real product bugs were found and fixed via live browser
verification (a Svelte effect re-running on every keystroke that
silently cancelled the ghost-text debounce; a ghost-text widget
positioned at document offset 0 instead of the cursor), and a real
costguard logic bug (unbounded-aggregation vs. raw-row) was caught by
its own test suite. New integration tests wire a real Ollama client
through the real HTTP handler against a mock server matching Ollama's
wire contract (hack/mock-ollama), keeping model-quality verification
out of CI as a disclosed, periodic human-run check instead.

See /docs/phase-7-ai-design.md and /docs/phase-7-runbook.md.
2026-08-16 18:06:27 -07:00

53 lines
1.7 KiB
Go

package router
import (
"context"
"testing"
"github.com/sentry/sentry/api/ai/provider"
)
// namedFakeProvider lets a test tell which configured provider actually
// answered a call -- the thing router.For needs to get right.
type namedFakeProvider struct {
name string
}
func (f *namedFakeProvider) Translate(context.Context, provider.TranslateRequest) (provider.TranslateResult, error) {
return provider.TranslateResult{Query: f.name}, nil
}
func (f *namedFakeProvider) Complete(context.Context, provider.CompleteRequest) (provider.CompleteResult, error) {
return provider.CompleteResult{Suggestion: f.name}, nil
}
func (f *namedFakeProvider) Explain(context.Context, provider.ExplainRequest) (provider.ExplainResult, error) {
return provider.ExplainResult{Explanation: f.name}, nil
}
func (f *namedFakeProvider) Fix(context.Context, provider.FixRequest) (provider.FixResult, error) {
return provider.FixResult{SuggestedQuery: f.name}, nil
}
var _ provider.Provider = (*namedFakeProvider)(nil)
func TestForReturnsFallbackWhenUnconfigured(t *testing.T) {
fallback := &namedFakeProvider{name: "default"}
r := New(fallback)
if got := r.For(OpTranslate); got != fallback {
t.Errorf("For(OpTranslate) = %v, want the fallback provider", got)
}
}
func TestSetOperationOverridesFallback(t *testing.T) {
fallback := &namedFakeProvider{name: "default"}
fast := &namedFakeProvider{name: "fast"}
r := New(fallback)
r.SetOperation(OpComplete, fast)
if got := r.For(OpComplete); got != fast {
t.Errorf("For(OpComplete) = %v, want the fast override", got)
}
if got := r.For(OpTranslate); got != fallback {
t.Errorf("For(OpTranslate) = %v, want unaffected fallback", got)
}
}