Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

178 lines
6.9 KiB
Go

package review
import (
"strings"
"testing"
)
// fixedReport is a representative report: a BLOCK verdict with three findings
// across two files (one critical, one error, one warning), a per-file risk
// ranking, a summary line, an adaptive depth, and a populated cost block.
func fixedReport() *ReviewReport {
return &ReviewReport{
Verdict: VerdictBlock,
Summary: "BLOCK: 3 finding(s) across 2 changed file(s)",
Depth: "deep",
Findings: []Finding{
{
File: "internal/svc/loop.go", Line: 12, Severity: SevWarning,
Rule: "go-loop-query-call", Category: "performance",
Message: "query in loop", Confidence: 0.8,
},
{
File: "internal/svc/handler.go", Line: 7, Severity: SevError,
Rule: "go-inverted-err-check", Category: "correctness",
Message: "inverted error check", Confidence: 0.9,
},
{
File: "internal/svc/handler.go", Line: 3, Severity: SevCritical,
Rule: "nil-deref", Category: "correctness",
Message: "possible nil dereference", Confidence: 0.95,
},
},
FileRisk: []FileRisk{
{File: "internal/svc/handler.go", Risk: "high", Findings: 2},
{File: "internal/svc/loop.go", Risk: "medium", Findings: 1},
},
Cost: &CostBreakdown{
InputTokens: 1234, OutputTokens: 456,
CacheReadTokens: 2000, CacheWriteTokens: 0,
USD: 0.012, Estimated: true, ElapsedMs: 4200,
},
}
}
// TestRenderSummary_AgentGolden pins the terse agent rendering byte-for-byte.
// Findings are ordered worst-first (critical → error → warning); the verdict
// line carries the severity histogram; one compact line per finding; one cost
// line. There is no narrative prose.
func TestRenderSummary_AgentGolden(t *testing.T) {
got := RenderSummary(fixedReport(), AudienceAgent)
want := "" +
"VERDICT: block (1 critical, 1 error, 1 warning)\n" +
"findings:\n" +
" internal/svc/handler.go:3 critical nil-deref — possible nil dereference\n" +
" internal/svc/handler.go:7 error go-inverted-err-check — inverted error check\n" +
" internal/svc/loop.go:12 warning go-loop-query-call — query in loop\n" +
"cost: in=1234 out=456 cache_r=2000 cache_w=0 usd=0.012000 elapsed=4.2s\n"
if got != want {
t.Fatalf("agent rendering drift:\n--- got ---\n%s\n--- want ---\n%s", got, want)
}
}
// TestRenderSummary_AgentTerse_NoNarrative asserts the agent rendering carries
// only the verdict line, the findings block, and the cost line — no markdown
// headers, no per-file risk section, no prose.
func TestRenderSummary_AgentTerse_NoNarrative(t *testing.T) {
got := RenderSummary(fixedReport(), AudienceAgent)
// One-line verdict first.
first := strings.SplitN(got, "\n", 2)[0]
if !strings.HasPrefix(first, "VERDICT: block") {
t.Fatalf("agent output must lead with a one-line VERDICT, got %q", first)
}
// Cost line present.
if !strings.Contains(got, "cost: in=1234 out=456 cache_r=2000 cache_w=0 usd=0.012000 elapsed=4.2s") {
t.Errorf("agent output must carry the cost line, got:\n%s", got)
}
// Prose / narrative markers from the human packet must NOT appear.
for _, marker := range []string{"## ", "Verdict:", "File risk:", "Findings (", "No inline findings", "finding(s) across"} {
if strings.Contains(got, marker) {
t.Errorf("agent output must not contain narrative marker %q, got:\n%s", marker, got)
}
}
// Every finding line is a single compact line under the findings block.
if c := strings.Count(got, " — "); c != 3 {
t.Errorf("expected 3 compact finding lines, found %d em-dashes in:\n%s", c, got)
}
}
// TestRenderSummary_HumanGolden pins the readable section packet byte-for-byte:
// the verdict header, the summary line, the per-file risk ranking, and the
// findings grouped by file and ordered by line, then the cost block.
func TestRenderSummary_HumanGolden(t *testing.T) {
got := RenderSummary(fixedReport(), AudienceHuman)
want := "" +
"Verdict: BLOCK\n" +
"BLOCK: 3 finding(s) across 2 changed file(s)\n" +
"\n" +
"File risk:\n" +
" high internal/svc/handler.go (2 finding(s))\n" +
" medium internal/svc/loop.go (1 finding(s))\n" +
"\n" +
"Findings (3):\n" +
"\n" +
"internal/svc/handler.go\n" +
" L3 critical possible nil dereference [correctness/nil-deref]\n" +
" L7 error inverted error check [correctness/go-inverted-err-check]\n" +
"\n" +
"internal/svc/loop.go\n" +
" L12 warning query in loop [performance/go-loop-query-call]\n" +
"\n" +
"cost: in=1234 out=456 cache_r=2000 cache_w=0 usd=0.012000 elapsed=4.2s\n"
if got != want {
t.Fatalf("human rendering drift:\n--- got ---\n%q\n--- want ---\n%q", got, want)
}
}
// TestRenderSummary_HumanIsSectionPacket asserts the human rendering carries the
// readable sections the agent form omits.
func TestRenderSummary_HumanIsSectionPacket(t *testing.T) {
got := RenderSummary(fixedReport(), AudienceHuman)
for _, want := range []string{"Verdict: BLOCK", "File risk:", "Findings (3):", "internal/svc/handler.go", "[correctness/nil-deref]"} {
if !strings.Contains(got, want) {
t.Errorf("human packet missing section marker %q, got:\n%s", want, got)
}
}
// The terse one-line verdict form must NOT be present in the human packet.
if strings.Contains(got, "VERDICT: block") {
t.Errorf("human packet must use the readable header, not the terse VERDICT line:\n%s", got)
}
}
// TestRenderSummary_DefaultIsHuman asserts AudienceHuman is the zero value, so a
// caller that does not opt in gets the readable packet.
func TestRenderSummary_DefaultIsHuman(t *testing.T) {
var aud Audience // zero value
if aud != AudienceHuman {
t.Fatalf("the zero-value Audience must be AudienceHuman, got %d", aud)
}
if RenderSummary(fixedReport(), aud) != RenderSummary(fixedReport(), AudienceHuman) {
t.Error("zero-value Audience must render identically to AudienceHuman")
}
}
// TestRenderSummary_NilReport asserts a nil report renders an empty APPROVE for
// both audiences without panicking.
func TestRenderSummary_NilReport(t *testing.T) {
agent := RenderSummary(nil, AudienceAgent)
if !strings.HasPrefix(agent, "VERDICT: approve") {
t.Errorf("nil report agent rendering must lead with approve, got %q", agent)
}
if !strings.Contains(agent, "cost: in=0 out=0") {
t.Errorf("nil report agent rendering must carry a zero cost line, got %q", agent)
}
human := RenderSummary(nil, AudienceHuman)
if !strings.Contains(human, "Verdict: APPROVE") || !strings.Contains(human, "No inline findings") {
t.Errorf("nil report human rendering must be an empty APPROVE packet, got %q", human)
}
}
// TestRenderSummary_NoCostStillEmitsZeroLine asserts a report with a nil Cost
// (the deterministic-only path) still renders a parseable zero cost line in both
// audiences.
func TestRenderSummary_NoCostStillEmitsZeroLine(t *testing.T) {
r := fixedReport()
r.Cost = nil
for _, aud := range []Audience{AudienceAgent, AudienceHuman} {
got := RenderSummary(r, aud)
if !strings.Contains(got, "cost: in=0 out=0 cache_r=0 cache_w=0 usd=0.000000 elapsed=0.0s") {
t.Errorf("audience %d must emit a zero cost line when Cost is nil, got:\n%s", aud, got)
}
}
}