ea9475da54
examples/reviewer proves the core is sufficient for a static-binary light host (gadfly's shape) with NO batteries: - config.Env + model.Configure -> env-driven model fleet + tier overrides - model.ParseModelForContext -> tier resolution + failover - fanout.Run (PerKey caps) -> N models x M lenses swarm, per-provider bound - model.GenerateWith[T] -> structured findings per (model, lens) cell - Consolidate -> one verdict-led report section per model Hermetic test runs the full 2x3 swarm against majordomo's fake provider and asserts the consolidated verdicts. A go list -deps CI check asserts the canary imports ZERO batteries (the light-tier invariant) — gadfly's go.sum stays free of gorm/redis/discordgo/sqlite. README + docs updated. This is the canary; migrating the LIVE gadfly repo onto executus core is a follow-up (kept separate to not destabilize the active reviewer). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
103 lines
3.2 KiB
Go
103 lines
3.2 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
"testing"
|
||
|
||
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/fake"
|
||
|
||
"gitea.stevedudenhoeffer.com/steve/executus/fanout"
|
||
)
|
||
|
||
// TestReviewSwarm proves the light-tier path end-to-end against the fake
|
||
// provider: a 2-model × 3-lens swarm runs, structured findings parse, and
|
||
// consolidation produces one verdict-led section per model — no batteries, no
|
||
// network.
|
||
func TestReviewSwarm(t *testing.T) {
|
||
fp := fake.New("fakeprov")
|
||
|
||
// Model "hot" reports a high-severity finding on every lens; "cold" reports
|
||
// nothing. Each model is called once per lens (3×), so enqueue 3 each.
|
||
hot := `{"findings":[{"severity":"high","title":"SQL injection","detail":"unsanitized id in query"}]}`
|
||
cold := `{"findings":[]}`
|
||
for i := 0; i < 3; i++ {
|
||
fp.Enqueue("hot", fake.Reply(hot))
|
||
fp.Enqueue("cold", fake.Reply(cold))
|
||
}
|
||
hotM, err := fp.Model("hot")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
coldM, err := fp.Model("cold")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
models := []NamedModel{
|
||
{Name: "hot", Provider: "fakeprov", Model: hotM},
|
||
{Name: "cold", Provider: "fakeprov", Model: coldM},
|
||
}
|
||
lenses := []Lens{{Name: "security"}, {Name: "correctness"}, {Name: "error-handling"}}
|
||
|
||
results := Review(context.Background(), models, lenses, "some diff",
|
||
fanout.Options[cell]{MaxConcurrent: 6, PerKey: map[string]int{"fakeprov": 3}})
|
||
|
||
// 2 models × 3 lenses = 6 cells, all successful.
|
||
if len(results) != 6 {
|
||
t.Fatalf("got %d cells, want 6", len(results))
|
||
}
|
||
var hotFindings, coldFindings, errs int
|
||
for _, r := range results {
|
||
if r.Err != nil {
|
||
errs++
|
||
continue
|
||
}
|
||
switch r.Model {
|
||
case "hot":
|
||
hotFindings += len(r.Findings)
|
||
case "cold":
|
||
coldFindings += len(r.Findings)
|
||
}
|
||
}
|
||
if errs != 0 {
|
||
t.Errorf("expected no cell errors, got %d", errs)
|
||
}
|
||
if hotFindings != 3 { // one per lens
|
||
t.Errorf("hot model findings = %d, want 3", hotFindings)
|
||
}
|
||
if coldFindings != 0 {
|
||
t.Errorf("cold model findings = %d, want 0", coldFindings)
|
||
}
|
||
|
||
report := Consolidate(results)
|
||
if !strings.Contains(report, "hot — blocking issues found") {
|
||
t.Errorf("hot section should lead with a blocking verdict:\n%s", report)
|
||
}
|
||
if !strings.Contains(report, "cold — no issues found") {
|
||
t.Errorf("cold section should report no issues:\n%s", report)
|
||
}
|
||
if !strings.Contains(report, "SQL injection") {
|
||
t.Errorf("report should surface the finding:\n%s", report)
|
||
}
|
||
}
|
||
|
||
// TestConsolidateVerdicts checks the worst-severity-led header logic.
|
||
func TestConsolidateVerdicts(t *testing.T) {
|
||
got := Consolidate([]LensResult{
|
||
{Model: "m", Lens: "a", Findings: []Finding{{Severity: SevSmall, Title: "x"}}},
|
||
{Model: "m", Lens: "b", Findings: []Finding{{Severity: SevMedium, Title: "y"}}},
|
||
})
|
||
if !strings.Contains(got, "m — minor issues") {
|
||
t.Errorf("medium-max should be 'minor issues', got:\n%s", got)
|
||
}
|
||
// An errored lens is surfaced in the header.
|
||
got = Consolidate([]LensResult{
|
||
{Model: "m", Lens: "a", Findings: []Finding{{Severity: SevCritical, Title: "boom"}}},
|
||
{Model: "m", Lens: "b", Err: context.DeadlineExceeded},
|
||
})
|
||
if !strings.Contains(got, "blocking issues found") || !strings.Contains(got, "errored") {
|
||
t.Errorf("critical + errored lens header wrong:\n%s", got)
|
||
}
|
||
}
|