Add unit tests, integration test, CI workflow, and conventions
- gitea/client_test.go: mock HTTP tests for all API methods + error cases - llm/client_test.go: mock tests for completion, errors, timeouts - review/parser_test.go: JSON parsing, markdown fences, validation - review/formatter_test.go: markdown output, empty/multiple findings - review/prompt_test.go: system/user prompt construction - integration_test.go: full end-to-end flow (build tag: integration) - .gitea/workflows/ci.yml: test + vet + build on push, dual LLM review on PRs - CONVENTIONS.md: coding standards for self-review dogfooding - README.md: usage docs, env vars, architecture
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
package review
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseResponse_ValidJSON(t *testing.T) {
|
||||
input := `{
|
||||
"verdict": "APPROVE",
|
||||
"summary": "Looks good",
|
||||
"findings": [
|
||||
{"severity": "NIT", "file": "main.go", "line": 10, "finding": "Consider renaming"}
|
||||
],
|
||||
"recommendation": "Ship it"
|
||||
}`
|
||||
|
||||
result, err := ParseResponse(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result.Verdict != "APPROVE" {
|
||||
t.Errorf("expected verdict APPROVE, got %q", result.Verdict)
|
||||
}
|
||||
if result.Summary != "Looks good" {
|
||||
t.Errorf("expected summary %q, got %q", "Looks good", result.Summary)
|
||||
}
|
||||
if len(result.Findings) != 1 {
|
||||
t.Fatalf("expected 1 finding, got %d", len(result.Findings))
|
||||
}
|
||||
if result.Findings[0].Severity != "NIT" {
|
||||
t.Errorf("expected severity NIT, got %q", result.Findings[0].Severity)
|
||||
}
|
||||
if result.Findings[0].File != "main.go" {
|
||||
t.Errorf("expected file main.go, got %q", result.Findings[0].File)
|
||||
}
|
||||
if result.Findings[0].Line != 10 {
|
||||
t.Errorf("expected line 10, got %d", result.Findings[0].Line)
|
||||
}
|
||||
if result.Recommendation != "Ship it" {
|
||||
t.Errorf("expected recommendation %q, got %q", "Ship it", result.Recommendation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_MarkdownFences(t *testing.T) {
|
||||
input := "```json\n{\"verdict\": \"REQUEST_CHANGES\", \"summary\": \"Issues found\", \"findings\": [{\"severity\": \"MAJOR\", \"file\": \"a.go\", \"line\": 5, \"finding\": \"Bug\"}], \"recommendation\": \"Fix it\"}\n```"
|
||||
|
||||
result, err := ParseResponse(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result.Verdict != "REQUEST_CHANGES" {
|
||||
t.Errorf("expected verdict REQUEST_CHANGES, got %q", result.Verdict)
|
||||
}
|
||||
if len(result.Findings) != 1 {
|
||||
t.Fatalf("expected 1 finding, got %d", len(result.Findings))
|
||||
}
|
||||
if result.Findings[0].Severity != "MAJOR" {
|
||||
t.Errorf("expected severity MAJOR, got %q", result.Findings[0].Severity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_InvalidJSON(t *testing.T) {
|
||||
_, err := ParseResponse("this is not json")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_InvalidVerdict(t *testing.T) {
|
||||
input := `{"verdict": "MAYBE", "summary": "Hmm", "findings": [], "recommendation": "Dunno"}`
|
||||
_, err := ParseResponse(input)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid verdict, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_InvalidSeverity(t *testing.T) {
|
||||
input := `{"verdict": "APPROVE", "summary": "Ok", "findings": [{"severity": "CRITICAL", "file": "x.go", "line": 1, "finding": "bad"}], "recommendation": "Fix"}`
|
||||
_, err := ParseResponse(input)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid severity, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_EmptyFindings(t *testing.T) {
|
||||
input := `{"verdict": "APPROVE", "summary": "All good", "findings": [], "recommendation": "Merge"}`
|
||||
result, err := ParseResponse(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(result.Findings) != 0 {
|
||||
t.Errorf("expected 0 findings, got %d", len(result.Findings))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_MissingFields(t *testing.T) {
|
||||
// verdict is empty string — should fail validation
|
||||
input := `{"summary": "Ok", "findings": [], "recommendation": "Merge"}`
|
||||
_, err := ParseResponse(input)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing verdict, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_MarkdownFencesNoLang(t *testing.T) {
|
||||
input := "```\n{\"verdict\": \"APPROVE\", \"summary\": \"Fine\", \"findings\": [], \"recommendation\": \"Good\"}\n```"
|
||||
result, err := ParseResponse(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result.Verdict != "APPROVE" {
|
||||
t.Errorf("expected APPROVE, got %q", result.Verdict)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user