Skip to content

Commit e431726

Browse files
authored
internal/generator: Refactor unit tests to be table driven (#4)
Signed-off-by: timflannagan <[email protected]>
1 parent 97a5671 commit e431726

File tree

1 file changed

+174
-61
lines changed

1 file changed

+174
-61
lines changed

internal/generator/generator_test.go

Lines changed: 174 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -15,78 +15,191 @@ import (
1515
"github.com/migueleliasweb/go-github-mock/src/mock"
1616
)
1717

18-
// TestGenerateChangelog_Mocked tests the changelog-generator logic
18+
// TestGenerateChangelog tests the changelog-generator logic
1919
// by mocking GitHub API calls via go-github-mock.
20-
func TestGenerateChangelog_Mocked(t *testing.T) {
21-
// Prepare mock transport
22-
mockedHTTPClient := mock.NewMockedHTTPClient(
23-
// 1. Mock Git.GetCommit for startSHA
24-
mock.WithRequestMatchHandler(
25-
mock.GetReposGitCommitsByOwnerByRepoByCommitSha,
26-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27-
vars := mux.Vars(r)
28-
if vars["commit_sha"] == "start-sha" {
29-
json.NewEncoder(w).Encode(github.Commit{
30-
Committer: &github.CommitAuthor{
31-
Date: &github.Timestamp{Time: time.Date(2025, 4, 1, 12, 0, 0, 0, time.UTC)},
32-
},
33-
})
34-
return
35-
}
36-
if vars["commit_sha"] == "end-sha" {
37-
json.NewEncoder(w).Encode(github.Commit{
38-
Committer: &github.CommitAuthor{
39-
Date: &github.Timestamp{Time: time.Date(2025, 5, 1, 12, 0, 0, 0, time.UTC)},
40-
},
41-
})
42-
return
43-
}
44-
w.WriteHeader(http.StatusNotFound)
45-
}),
46-
),
47-
// 3. Mock Search.Issues for merged PRs
48-
mock.WithRequestMatch(
49-
mock.GetSearchIssues,
50-
github.IssuesSearchResult{
51-
Issues: []*github.Issue{
52-
{Number: github.Int(42)},
53-
},
20+
func TestGenerateChangelog(t *testing.T) {
21+
tt := []struct {
22+
name string
23+
owner string
24+
repo string
25+
startSHA string
26+
startSHADate time.Time
27+
endSHA string
28+
endSHADate time.Time
29+
issuesForSearch []*github.Issue
30+
pullRequests []*github.PullRequest
31+
expectedChangelog string
32+
expectError bool
33+
}{
34+
{
35+
name: "Valid/Feature PR with release-note",
36+
owner: "foo",
37+
repo: "bar",
38+
startSHA: "start-sha",
39+
startSHADate: time.Date(2025, 4, 1, 12, 0, 0, 0, time.UTC),
40+
endSHA: "end-sha",
41+
endSHADate: time.Date(2025, 5, 1, 12, 0, 0, 0, time.UTC),
42+
issuesForSearch: []*github.Issue{
43+
{Number: github.Ptr(42)},
5444
},
55-
),
56-
// 4. Mock PullRequests.Get for PR #42
57-
mock.WithRequestMatch(
58-
mock.GetReposPullsByOwnerByRepoByPullNumber,
59-
github.PullRequest{
45+
pullRequests: []*github.PullRequest{{
6046
Number: github.Ptr(42),
6147
Title: github.Ptr("Add new feature"),
62-
Body: github.Ptr("```release-note\nMy note for PR42\n```"),
63-
Labels: []*github.Label{
64-
{Name: github.Ptr("kind/new-feature")},
48+
Body: github.Ptr("```release-note\\nMy note for PR42\\n```"),
49+
Labels: []*github.Label{{
50+
Name: github.Ptr("kind/new-feature"),
51+
}},
52+
}},
53+
expectedChangelog: `
54+
## 🚀 Features
55+
56+
- Add new feature (#42)
57+
`,
58+
expectError: false,
59+
},
60+
{
61+
name: "Valid/No PRs with release-note content",
62+
owner: "foo",
63+
repo: "bar",
64+
startSHA: "start-sha",
65+
startSHADate: time.Date(2025, 4, 1, 12, 0, 0, 0, time.UTC),
66+
endSHA: "end-sha",
67+
endSHADate: time.Date(2025, 5, 1, 12, 0, 0, 0, time.UTC),
68+
issuesForSearch: []*github.Issue{
69+
{Number: github.Ptr(43)},
70+
},
71+
pullRequests: []*github.PullRequest{{
72+
Number: github.Ptr(43),
73+
Title: github.Ptr("Refactor code"),
74+
Body: github.Ptr("No release note here."),
75+
Labels: []*github.Label{{
76+
Name: github.Ptr("irrelevant-label"),
77+
}},
78+
}},
79+
expectedChangelog: "",
80+
expectError: false,
81+
},
82+
{
83+
name: "Valid/Multiple PRs with different kinds",
84+
owner: "foo",
85+
repo: "bar",
86+
startSHA: "start-sha",
87+
startSHADate: time.Date(2025, 4, 1, 12, 0, 0, 0, time.UTC),
88+
endSHA: "end-sha",
89+
endSHADate: time.Date(2025, 5, 1, 12, 0, 0, 0, time.UTC),
90+
issuesForSearch: []*github.Issue{
91+
{Number: github.Ptr(42)},
92+
{Number: github.Ptr(43)},
93+
},
94+
pullRequests: []*github.PullRequest{
95+
{
96+
Number: github.Ptr(42),
97+
Title: github.Ptr("Add new feature"),
98+
Body: github.Ptr("```release-note\\ASDF\\n```"),
99+
Labels: []*github.Label{{
100+
Name: github.Ptr("kind/"),
101+
}},
102+
},
103+
{
104+
Number: github.Ptr(43),
105+
Title: github.Ptr("Fix bug"),
106+
Body: github.Ptr("```release-note\\nFixed a bug\\n```"),
107+
Labels: []*github.Label{{
108+
Name: github.Ptr("kind/bug"),
109+
}},
65110
},
66111
},
67-
),
68-
)
112+
expectedChangelog: `
113+
## 🚀 Features
69114
70-
// Create GitHub client with mock transport
71-
ghClient := github.NewClient(mockedHTTPClient)
115+
- Add new feature (#42)
72116
73-
// Create changelog generator
74-
generator := generator.New(ghClient, "foo", "bar")
117+
## 🐛 Bug Fixes
75118
76-
// Generate changelog
77-
changelog, err := generator.Generate(context.Background(), "start-sha", "end-sha")
78-
if err != nil {
79-
t.Fatalf("Generate failed: %v", err)
119+
- Fix bug (#43)
120+
`,
121+
expectError: false,
122+
},
80123
}
81124

82-
// Debug output
83-
fmt.Printf("Generated changelog:\n%s\n", changelog)
125+
for _, tc := range tt {
126+
t.Run(tc.name, func(t *testing.T) {
127+
currentMockHandlers := []mock.MockBackendOption{
128+
mockGetCommitHandler(tc.startSHA, tc.startSHADate, tc.endSHA, tc.endSHADate),
129+
mockSearchIssuesHandler(tc.issuesForSearch),
130+
}
131+
if len(tc.pullRequests) > 0 {
132+
currentMockHandlers = append(currentMockHandlers, mockGetPullRequestsHandler(tc.pullRequests))
133+
}
134+
mockedHTTPClient := mock.NewMockedHTTPClient(currentMockHandlers...)
84135

85-
// Verify changelog content
86-
if !strings.Contains(changelog, "🚀 Features") {
87-
t.Error("changelog missing Features section")
88-
}
89-
if !strings.Contains(changelog, "#42") {
90-
t.Error("changelog missing PR #42")
136+
g := generator.New(github.NewClient(mockedHTTPClient), tc.owner, tc.repo)
137+
changelog, err := g.Generate(context.Background(), tc.startSHA, tc.endSHA)
138+
switch {
139+
case tc.expectError && err == nil:
140+
t.Fatalf("Expected an error, but got nil")
141+
case !tc.expectError && err != nil:
142+
t.Fatalf("Expected no error, but got: %v", err)
143+
default:
144+
if strings.TrimSpace(changelog) != strings.TrimSpace(tc.expectedChangelog) {
145+
t.Fatalf("Generated changelog does not match expected changelog:\nwant: %s\ngot: %s", tc.expectedChangelog, changelog)
146+
}
147+
}
148+
})
91149
}
92150
}
151+
152+
// mockGetCommitHandler creates a mock handler for the GetCommit API call.
153+
func mockGetCommitHandler(startSHA string, startDate time.Time, endSHA string, endDate time.Time) mock.MockBackendOption {
154+
return mock.WithRequestMatchHandler(
155+
mock.GetReposGitCommitsByOwnerByRepoByCommitSha,
156+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
157+
vars := mux.Vars(r)
158+
commitSHA := vars["commit_sha"]
159+
var commitDate time.Time
160+
switch commitSHA {
161+
case startSHA:
162+
commitDate = startDate
163+
case endSHA:
164+
commitDate = endDate
165+
default:
166+
w.WriteHeader(http.StatusNotFound)
167+
return
168+
}
169+
json.NewEncoder(w).Encode(github.Commit{
170+
Committer: &github.CommitAuthor{
171+
Date: &github.Timestamp{Time: commitDate},
172+
},
173+
})
174+
}),
175+
)
176+
}
177+
178+
// mockSearchIssuesHandler creates a mock handler for the SearchIssues API call.
179+
func mockSearchIssuesHandler(issues []*github.Issue) mock.MockBackendOption {
180+
return mock.WithRequestMatch(
181+
mock.GetSearchIssues,
182+
github.IssuesSearchResult{
183+
Issues: issues,
184+
},
185+
)
186+
}
187+
188+
// mockGetPullRequestsHandler creates a mock handler for GetPullRequests API calls.
189+
// It mocks responses for each PR in the provided slice based on its number.
190+
func mockGetPullRequestsHandler(prs []*github.PullRequest) mock.MockBackendOption {
191+
return mock.WithRequestMatchHandler(
192+
mock.GetReposPullsByOwnerByRepoByPullNumber,
193+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
194+
vars := mux.Vars(r)
195+
prNumberStr := vars["pull_number"]
196+
for _, pr := range prs {
197+
if pr.Number != nil && fmt.Sprintf("%d", *pr.Number) == prNumberStr {
198+
json.NewEncoder(w).Encode(pr)
199+
return
200+
}
201+
}
202+
w.WriteHeader(http.StatusNotFound)
203+
}),
204+
)
205+
}

0 commit comments

Comments
 (0)