aboutsummaryrefslogtreecommitdiff
path: root/languages/go/tests/coverage_summary_test.go
blob: 6b2fb4469cd3116a40ffb07d4f9fc65c4aa47ada (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Black-box tests for the Go bundle coverage-summary script.
//
// The script ships into a project's .claude/scripts/ and runs via `go run`, so
// these tests exercise the real CLI rather than importing it: build a throwaway
// module (go.mod + source files + a cover.out profile), run the script against
// it, and assert on stdout. Keeping the test in its own module here means the
// shipped script dir stays test-free.
//
// Normal / Boundary / Error coverage at the behavior level: missing-file
// detection, the file-weighted number, all-tracked, ignoring _test.go, and a
// missing-report error.
package gocovtest

import (
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

// scriptPath resolves coverage-summary.go relative to this test file.
func scriptPath(t *testing.T) string {
	t.Helper()
	_, thisFile, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("cannot locate test file")
	}
	p := filepath.Join(filepath.Dir(thisFile), "..", "claude", "scripts", "coverage-summary.go")
	abs, err := filepath.Abs(p)
	if err != nil {
		t.Fatal(err)
	}
	return abs
}

// fixture writes a go.mod, the given source files, and a cover.out under a temp
// module root. sources maps relpath -> contents; profile is the raw cover.out.
func fixture(t *testing.T, sources map[string]string, profile string) string {
	t.Helper()
	root := t.TempDir()
	write := func(rel, body string) {
		full := filepath.Join(root, rel)
		if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(full, []byte(body), 0o644); err != nil {
			t.Fatal(err)
		}
	}
	write("go.mod", "module example.com/m\n\ngo 1.26\n")
	for rel, body := range sources {
		write(rel, body)
	}
	write("cover.out", profile)
	return root
}

// run executes the script against a fixture root, returning combined output + err.
func run(t *testing.T, root, sourceDir string) (string, error) {
	t.Helper()
	cmd := exec.Command("go", "run", scriptPath(t),
		filepath.Join(root, "cover.out"), filepath.Join(root, sourceDir), root)
	out, err := cmd.CombinedOutput()
	return string(out), err
}

func TestMissingFileSurfacedAndCountedZero(t *testing.T) {
	root := fixture(t,
		map[string]string{
			"calc/calc.go":     "package calc\n\nfunc Add(a, b int) int { return a + b }\n",
			"calc/untested.go": "package calc\n\nfunc Mul(a, b int) int { return a * b }\n",
		},
		// calc.go fully covered (2/2 stmts); untested.go absent from the profile.
		"mode: set\nexample.com/m/calc/calc.go:3.40,3.56 2 1\n",
	)
	out, err := run(t, root, ".")
	if err != nil {
		t.Fatalf("script failed: %v\n%s", err, out)
	}
	if !strings.Contains(out, "calc/untested.go") {
		t.Errorf("missing file not surfaced:\n%s", out)
	}
	if !strings.Contains(out, "0%") {
		t.Errorf("missing-as-0%% note absent:\n%s", out)
	}
	// calc.go = 100%, untested.go missing = 0% -> 50.0%
	if !strings.Contains(out, "50.0%") {
		t.Errorf("expected project number 50.0%%:\n%s", out)
	}
}

func TestAllTrackedNoMissing(t *testing.T) {
	root := fixture(t,
		map[string]string{"calc/calc.go": "package calc\n\nfunc Add(a, b int) int { return a + b }\n"},
		"mode: set\nexample.com/m/calc/calc.go:3.40,3.56 2 1\n",
	)
	out, err := run(t, root, ".")
	if err != nil {
		t.Fatalf("script failed: %v\n%s", err, out)
	}
	if !strings.Contains(out, "Not in coverage report: 0 file") {
		t.Errorf("expected zero missing files:\n%s", out)
	}
	if !strings.Contains(out, "100.0%") {
		t.Errorf("expected 100.0%% project number:\n%s", out)
	}
}

func TestTestFilesAreNotCountedAsSource(t *testing.T) {
	root := fixture(t,
		map[string]string{
			"calc/calc.go":      "package calc\n\nfunc Add(a, b int) int { return a + b }\n",
			"calc/calc_test.go": "package calc\n\nimport \"testing\"\n\nfunc TestX(t *testing.T) {}\n",
		},
		"mode: set\nexample.com/m/calc/calc.go:3.40,3.56 2 1\n",
	)
	out, err := run(t, root, ".")
	if err != nil {
		t.Fatalf("script failed: %v\n%s", err, out)
	}
	if strings.Contains(out, "calc_test.go") {
		t.Errorf("_test.go wrongly counted as source:\n%s", out)
	}
	if !strings.Contains(out, "100.0%") {
		t.Errorf("expected 100.0%% (test file excluded):\n%s", out)
	}
}

func TestMissingReportErrors(t *testing.T) {
	root := fixture(t,
		map[string]string{"calc/calc.go": "package calc\n"},
		"mode: set\n",
	)
	cmd := exec.Command("go", "run", scriptPath(t),
		filepath.Join(root, "does-not-exist.out"), root, root)
	out, err := cmd.CombinedOutput()
	if err == nil {
		t.Errorf("expected non-zero exit for missing report; output:\n%s", out)
	}
}