Skip to content

Commit 479f23d

Browse files
committed
Boost test coverage: root 82%, review 94%, output 97%
New test files: - describe_test.go (8 tests), improve_test.go (9), config_test.go (19) - incremental_test.go (4), rules_test.go (11) - internal/review/cwe_test.go (21), budget_test.go (16), reflect_test.go (18) - internal/output/sarif_test.go (11) Extended: prompt_test.go (+6), context_test.go (+5) Coverage: root 43→82%, review 25→94%, output 68→97%
1 parent a621c05 commit 479f23d

11 files changed

Lines changed: 2245 additions & 0 deletions

File tree

config_test.go

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
package sight
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestParseTOMLConfig_Basic(t *testing.T) {
10+
content := `
11+
model = "gpt-4"
12+
fail_on = "high"
13+
max_tokens = 8192
14+
git_context = true
15+
reflection = false
16+
parallel = true
17+
concerns = ["security", "bugs"]
18+
exclude = ["vendor/*", "*.min.js"]
19+
`
20+
cfg, err := parseTOMLConfig(content)
21+
if err != nil {
22+
t.Fatalf("parseTOMLConfig error: %v", err)
23+
}
24+
if cfg.Model != "gpt-4" {
25+
t.Errorf("expected model 'gpt-4', got %q", cfg.Model)
26+
}
27+
if cfg.FailOn != "high" {
28+
t.Errorf("expected fail_on 'high', got %q", cfg.FailOn)
29+
}
30+
if cfg.MaxTokens != 8192 {
31+
t.Errorf("expected max_tokens 8192, got %d", cfg.MaxTokens)
32+
}
33+
if cfg.GitContext == nil || !*cfg.GitContext {
34+
t.Error("expected git_context = true")
35+
}
36+
if cfg.Reflection == nil || *cfg.Reflection {
37+
t.Error("expected reflection = false")
38+
}
39+
if cfg.Parallel == nil || !*cfg.Parallel {
40+
t.Error("expected parallel = true")
41+
}
42+
if len(cfg.Concerns) != 2 {
43+
t.Fatalf("expected 2 concerns, got %d", len(cfg.Concerns))
44+
}
45+
if cfg.Concerns[0] != "security" || cfg.Concerns[1] != "bugs" {
46+
t.Errorf("unexpected concerns: %v", cfg.Concerns)
47+
}
48+
if len(cfg.Exclude) != 2 {
49+
t.Fatalf("expected 2 exclude patterns, got %d", len(cfg.Exclude))
50+
}
51+
}
52+
53+
func TestParseTOMLConfig_WithSections(t *testing.T) {
54+
content := `
55+
model = "claude-3"
56+
57+
[prompts]
58+
system = "You are a strict reviewer"
59+
review = "Check everything carefully"
60+
`
61+
cfg, err := parseTOMLConfig(content)
62+
if err != nil {
63+
t.Fatalf("parseTOMLConfig error: %v", err)
64+
}
65+
if cfg.Model != "claude-3" {
66+
t.Errorf("expected model 'claude-3', got %q", cfg.Model)
67+
}
68+
if cfg.Prompts["system"] != "You are a strict reviewer" {
69+
t.Errorf("expected system prompt, got %q", cfg.Prompts["system"])
70+
}
71+
if cfg.Prompts["review"] != "Check everything carefully" {
72+
t.Errorf("expected review prompt, got %q", cfg.Prompts["review"])
73+
}
74+
}
75+
76+
func TestParseTOMLConfig_Comments(t *testing.T) {
77+
content := `
78+
# This is a comment
79+
model = "gpt-4"
80+
# Another comment
81+
fail_on = "medium"
82+
`
83+
cfg, err := parseTOMLConfig(content)
84+
if err != nil {
85+
t.Fatalf("parseTOMLConfig error: %v", err)
86+
}
87+
if cfg.Model != "gpt-4" {
88+
t.Errorf("expected model 'gpt-4', got %q", cfg.Model)
89+
}
90+
if cfg.FailOn != "medium" {
91+
t.Errorf("expected fail_on 'medium', got %q", cfg.FailOn)
92+
}
93+
}
94+
95+
func TestParseTOMLConfig_Empty(t *testing.T) {
96+
cfg, err := parseTOMLConfig("")
97+
if err != nil {
98+
t.Fatalf("parseTOMLConfig error: %v", err)
99+
}
100+
if cfg.Model != "" {
101+
t.Errorf("expected empty model, got %q", cfg.Model)
102+
}
103+
if cfg.MaxTokens != 0 {
104+
t.Errorf("expected 0 max_tokens, got %d", cfg.MaxTokens)
105+
}
106+
}
107+
108+
func TestParseTOMLConfig_InvalidMaxTokens(t *testing.T) {
109+
content := `max_tokens = "not a number"`
110+
cfg, err := parseTOMLConfig(content)
111+
if err != nil {
112+
t.Fatalf("parseTOMLConfig error: %v", err)
113+
}
114+
// parseInt("not a number") returns 0, which is not > 0, so MaxTokens stays 0
115+
if cfg.MaxTokens != 0 {
116+
t.Errorf("expected 0 max_tokens for invalid input, got %d", cfg.MaxTokens)
117+
}
118+
}
119+
120+
func TestParseTOMLConfig_BooleanFalse(t *testing.T) {
121+
content := `
122+
git_context = false
123+
reflection = false
124+
parallel = false
125+
`
126+
cfg, err := parseTOMLConfig(content)
127+
if err != nil {
128+
t.Fatalf("parseTOMLConfig error: %v", err)
129+
}
130+
if cfg.GitContext == nil || *cfg.GitContext {
131+
t.Error("expected git_context = false")
132+
}
133+
if cfg.Reflection == nil || *cfg.Reflection {
134+
t.Error("expected reflection = false")
135+
}
136+
if cfg.Parallel == nil || *cfg.Parallel {
137+
t.Error("expected parallel = false")
138+
}
139+
}
140+
141+
func TestParseTOMLConfig_LineWithoutEquals(t *testing.T) {
142+
content := `
143+
model = "gpt-4"
144+
this line has no equals sign
145+
fail_on = "low"
146+
`
147+
cfg, err := parseTOMLConfig(content)
148+
if err != nil {
149+
t.Fatalf("parseTOMLConfig error: %v", err)
150+
}
151+
if cfg.Model != "gpt-4" {
152+
t.Errorf("expected model 'gpt-4', got %q", cfg.Model)
153+
}
154+
if cfg.FailOn != "low" {
155+
t.Errorf("expected fail_on 'low', got %q", cfg.FailOn)
156+
}
157+
}
158+
159+
func TestApplyFileConfig_Nil(t *testing.T) {
160+
opts := ApplyFileConfig(nil)
161+
if len(opts) != 0 {
162+
t.Errorf("expected 0 options for nil config, got %d", len(opts))
163+
}
164+
}
165+
166+
func TestApplyFileConfig_Full(t *testing.T) {
167+
tr := true
168+
fc := &FileConfig{
169+
Model: "gpt-4",
170+
Concerns: []string{"security"},
171+
FailOn: "high",
172+
MaxTokens: 4096,
173+
Exclude: []string{"vendor/*"},
174+
GitContext: &tr,
175+
Reflection: &tr,
176+
Parallel: &tr,
177+
}
178+
opts := ApplyFileConfig(fc)
179+
// Should produce 8 options (one for each non-zero field)
180+
if len(opts) != 8 {
181+
t.Errorf("expected 8 options, got %d", len(opts))
182+
}
183+
}
184+
185+
func TestApplyFileConfig_Partial(t *testing.T) {
186+
fc := &FileConfig{
187+
Model: "gpt-4",
188+
}
189+
opts := ApplyFileConfig(fc)
190+
if len(opts) != 1 {
191+
t.Errorf("expected 1 option, got %d", len(opts))
192+
}
193+
}
194+
195+
func TestApplyFileConfig_Empty(t *testing.T) {
196+
fc := &FileConfig{}
197+
opts := ApplyFileConfig(fc)
198+
if len(opts) != 0 {
199+
t.Errorf("expected 0 options for empty config, got %d", len(opts))
200+
}
201+
}
202+
203+
func TestFindConfigFile_Found(t *testing.T) {
204+
dir := t.TempDir()
205+
configPath := filepath.Join(dir, ".sight.toml")
206+
if err := os.WriteFile(configPath, []byte("model = \"gpt-4\""), 0644); err != nil {
207+
t.Fatal(err)
208+
}
209+
210+
found := findConfigFile(dir)
211+
if found != configPath {
212+
t.Errorf("expected %s, got %s", configPath, found)
213+
}
214+
}
215+
216+
func TestFindConfigFile_ParentDir(t *testing.T) {
217+
parent := t.TempDir()
218+
child := filepath.Join(parent, "sub", "dir")
219+
if err := os.MkdirAll(child, 0755); err != nil {
220+
t.Fatal(err)
221+
}
222+
configPath := filepath.Join(parent, ".sight.toml")
223+
if err := os.WriteFile(configPath, []byte("model = \"gpt-4\""), 0644); err != nil {
224+
t.Fatal(err)
225+
}
226+
227+
found := findConfigFile(child)
228+
if found != configPath {
229+
t.Errorf("expected %s, got %s", configPath, found)
230+
}
231+
}
232+
233+
func TestFindConfigFile_AlternateNames(t *testing.T) {
234+
dir := t.TempDir()
235+
configPath := filepath.Join(dir, "sight.toml")
236+
if err := os.WriteFile(configPath, []byte("model = \"gpt-4\""), 0644); err != nil {
237+
t.Fatal(err)
238+
}
239+
240+
found := findConfigFile(dir)
241+
if found != configPath {
242+
t.Errorf("expected %s, got %s", configPath, found)
243+
}
244+
}
245+
246+
func TestFindConfigFile_JSONFormat(t *testing.T) {
247+
dir := t.TempDir()
248+
configPath := filepath.Join(dir, ".sight.json")
249+
if err := os.WriteFile(configPath, []byte(`{"model": "gpt-4"}`), 0644); err != nil {
250+
t.Fatal(err)
251+
}
252+
253+
found := findConfigFile(dir)
254+
if found != configPath {
255+
t.Errorf("expected %s, got %s", configPath, found)
256+
}
257+
}
258+
259+
func TestFindConfigFile_NotFound(t *testing.T) {
260+
dir := t.TempDir()
261+
found := findConfigFile(dir)
262+
if found != "" {
263+
t.Errorf("expected empty string, got %s", found)
264+
}
265+
}
266+
267+
func TestFindConfigFile_PrioritizesToml(t *testing.T) {
268+
dir := t.TempDir()
269+
os.WriteFile(filepath.Join(dir, ".sight.toml"), []byte("model = \"a\""), 0644)
270+
os.WriteFile(filepath.Join(dir, ".sight.json"), []byte(`{"model": "b"}`), 0644)
271+
272+
found := findConfigFile(dir)
273+
expected := filepath.Join(dir, ".sight.toml")
274+
if found != expected {
275+
t.Errorf("expected .sight.toml to have priority, got %s", found)
276+
}
277+
}
278+
279+
func TestLoadConfigFile_NoFile(t *testing.T) {
280+
dir := t.TempDir()
281+
cfg, err := LoadConfigFile(dir)
282+
if err != nil {
283+
t.Fatalf("unexpected error: %v", err)
284+
}
285+
if cfg != nil {
286+
t.Error("expected nil config when no file found")
287+
}
288+
}
289+
290+
func TestLoadConfigFile_ValidFile(t *testing.T) {
291+
dir := t.TempDir()
292+
content := `
293+
model = "gpt-4"
294+
fail_on = "high"
295+
max_tokens = 4096
296+
`
297+
if err := os.WriteFile(filepath.Join(dir, ".sight.toml"), []byte(content), 0644); err != nil {
298+
t.Fatal(err)
299+
}
300+
301+
cfg, err := LoadConfigFile(dir)
302+
if err != nil {
303+
t.Fatalf("LoadConfigFile error: %v", err)
304+
}
305+
if cfg == nil {
306+
t.Fatal("expected non-nil config")
307+
}
308+
if cfg.Model != "gpt-4" {
309+
t.Errorf("expected model 'gpt-4', got %q", cfg.Model)
310+
}
311+
}
312+
313+
func TestParseTOMLArray(t *testing.T) {
314+
tests := []struct {
315+
input string
316+
expected []string
317+
}{
318+
{`["a", "b", "c"]`, []string{"a", "b", "c"}},
319+
{`[a, b]`, []string{"a", "b"}},
320+
{`["single"]`, []string{"single"}},
321+
{`[]`, nil},
322+
}
323+
324+
for _, tc := range tests {
325+
result := parseTOMLArray(tc.input)
326+
if len(result) != len(tc.expected) {
327+
t.Errorf("parseTOMLArray(%q): expected %d items, got %d", tc.input, len(tc.expected), len(result))
328+
continue
329+
}
330+
for i := range result {
331+
if result[i] != tc.expected[i] {
332+
t.Errorf("parseTOMLArray(%q)[%d]: expected %q, got %q", tc.input, i, tc.expected[i], result[i])
333+
}
334+
}
335+
}
336+
}
337+
338+
func TestParseInt(t *testing.T) {
339+
tests := []struct {
340+
input string
341+
expected int
342+
}{
343+
{"123", 123},
344+
{"0", 0},
345+
{"4096", 4096},
346+
{"abc", 0},
347+
{"12abc", 12},
348+
{"", 0},
349+
}
350+
351+
for _, tc := range tests {
352+
result := parseInt(tc.input)
353+
if result != tc.expected {
354+
t.Errorf("parseInt(%q): expected %d, got %d", tc.input, tc.expected, result)
355+
}
356+
}
357+
}

0 commit comments

Comments
 (0)