changelog generator & diff tool
stormlightlabs.github.io/git-storm/
changelog
changeset
markdown
golang
git
1package main
2
3import (
4 "encoding/json"
5 "strings"
6 "testing"
7
8 "github.com/stormlightlabs/git-storm/internal/changelog"
9 "github.com/stormlightlabs/git-storm/internal/testutils"
10)
11
12func TestCreateReleaseTag(t *testing.T) {
13 repo := testutils.SetupTestRepo(t)
14 worktree, err := repo.Worktree()
15 if err != nil {
16 t.Fatalf("Failed to get worktree: %v", err)
17 }
18
19 repoPath := worktree.Filesystem.Root()
20 version := &changelog.Version{
21 Number: "1.0.0",
22 Date: "2024-01-15",
23 Sections: []changelog.Section{
24 {
25 Type: "added",
26 Entries: []string{
27 "New authentication system",
28 "User profile management",
29 },
30 },
31 {
32 Type: "fixed",
33 Entries: []string{
34 "Memory leak in database connection pool",
35 },
36 },
37 },
38 }
39
40 err = createReleaseTag(repoPath, "1.0.0", version)
41 if err != nil {
42 t.Fatalf("createReleaseTag() error = %v", err)
43 }
44
45 tagRef, err := repo.Tag("v1.0.0")
46 if err != nil {
47 t.Fatalf("Tag v1.0.0 should exist, got error: %v", err)
48 }
49
50 tagObj, err := repo.TagObject(tagRef.Hash())
51 if err != nil {
52 t.Fatalf("Tag should be annotated, got error: %v", err)
53 }
54
55 head, err := repo.Head()
56 if err != nil {
57 t.Fatalf("Failed to get HEAD: %v", err)
58 }
59
60 testutils.Expect.Equal(t, tagObj.Target, head.Hash(), "Tag should point to HEAD")
61
62 message := tagObj.Message
63 testutils.Expect.True(t, strings.Contains(message, "Release 1.0.0"), "Tag message should contain version")
64 testutils.Expect.True(t, strings.Contains(message, "Added:"), "Tag message should contain Added section")
65 testutils.Expect.True(t, strings.Contains(message, "Fixed:"), "Tag message should contain Fixed section")
66 testutils.Expect.True(t, strings.Contains(message, "New authentication system"), "Tag message should contain entry")
67 testutils.Expect.True(t, strings.Contains(message, "Memory leak"), "Tag message should contain entry")
68}
69
70func TestCreateReleaseTag_DuplicateTag(t *testing.T) {
71 repo := testutils.SetupTestRepo(t)
72 worktree, err := repo.Worktree()
73 if err != nil {
74 t.Fatalf("Failed to get worktree: %v", err)
75 }
76
77 repoPath := worktree.Filesystem.Root()
78 version := &changelog.Version{
79 Number: "1.0.0",
80 Date: "2024-01-15",
81 Sections: []changelog.Section{
82 {
83 Type: "added",
84 Entries: []string{"Feature 1"},
85 },
86 },
87 }
88
89 err = createReleaseTag(repoPath, "1.0.0", version)
90 if err != nil {
91 t.Fatalf("First createReleaseTag() error = %v", err)
92 }
93
94 err = createReleaseTag(repoPath, "1.0.0", version)
95 if err == nil {
96 t.Error("Expected error when creating duplicate tag, got nil")
97 }
98
99 testutils.Expect.True(t, strings.Contains(err.Error(), "already exists"), "Error should indicate tag already exists")
100}
101
102func TestCreateReleaseTag_TagNameFormat(t *testing.T) {
103 tests := []struct {
104 version string
105 expectedTag string
106 }{
107 {"1.0.0", "v1.0.0"},
108 {"2.5.3", "v2.5.3"},
109 {"0.1.0", "v0.1.0"},
110 }
111
112 for _, tt := range tests {
113 t.Run(tt.version, func(t *testing.T) {
114 repo := testutils.SetupTestRepo(t)
115 worktree, err := repo.Worktree()
116 if err != nil {
117 t.Fatalf("Failed to get worktree: %v", err)
118 }
119
120 repoPath := worktree.Filesystem.Root()
121 version := &changelog.Version{
122 Number: tt.version,
123 Date: "2024-01-15",
124 Sections: []changelog.Section{
125 {
126 Type: "added",
127 Entries: []string{"Feature"},
128 },
129 },
130 }
131
132 err = createReleaseTag(repoPath, tt.version, version)
133 if err != nil {
134 t.Fatalf("createReleaseTag() error = %v", err)
135 }
136
137 _, err = repo.Tag(tt.expectedTag)
138 if err != nil {
139 t.Errorf("Tag %s should exist, got error: %v", tt.expectedTag, err)
140 }
141 })
142 }
143}
144
145func TestBuildTagMessage(t *testing.T) {
146 version := &changelog.Version{
147 Number: "1.2.3",
148 Date: "2024-01-15",
149 Sections: []changelog.Section{
150 {
151 Type: "added",
152 Entries: []string{
153 "Feature A",
154 "Feature B",
155 },
156 },
157 {
158 Type: "changed",
159 Entries: []string{"Updated API"},
160 },
161 {
162 Type: "fixed",
163 Entries: []string{
164 "Bug 1",
165 "Bug 2",
166 },
167 },
168 },
169 }
170 message := buildTagMessage("1.2.3", version)
171
172 testutils.Expect.True(t, strings.HasPrefix(message, "Release 1.2.3\n\n"), "Message should start with release header")
173
174 testutils.Expect.True(t, strings.Contains(message, "Added:\n"), "Should contain Added section")
175 testutils.Expect.True(t, strings.Contains(message, "Changed:\n"), "Should contain Changed section")
176 testutils.Expect.True(t, strings.Contains(message, "Fixed:\n"), "Should contain Fixed section")
177
178 testutils.Expect.True(t, strings.Contains(message, "- Feature A\n"), "Should contain entry")
179 testutils.Expect.True(t, strings.Contains(message, "- Feature B\n"), "Should contain entry")
180 testutils.Expect.True(t, strings.Contains(message, "- Updated API\n"), "Should contain entry")
181 testutils.Expect.True(t, strings.Contains(message, "- Bug 1\n"), "Should contain entry")
182 testutils.Expect.True(t, strings.Contains(message, "- Bug 2\n"), "Should contain entry")
183
184 sections := strings.Split(message, "\n\n")
185 testutils.Expect.True(t, len(sections) >= 3, "Sections should be separated by blank lines")
186}
187
188func TestBuildTagMessage_EmptyVersion(t *testing.T) {
189 version := &changelog.Version{
190 Number: "1.0.0",
191 Date: "2024-01-15",
192 Sections: []changelog.Section{},
193 }
194 message := buildTagMessage("1.0.0", version)
195
196 testutils.Expect.True(t, strings.HasPrefix(message, "Release 1.0.0\n\n"), "Should still have release header even with no sections")
197}
198
199func TestResolveReleaseVersion(t *testing.T) {
200 existing := &changelog.Changelog{Versions: []changelog.Version{{Number: "Unreleased"}, {Number: "1.2.3"}}}
201
202 version, err := resolveReleaseVersion("", "minor", existing)
203 if err != nil {
204 t.Fatalf("resolveReleaseVersion returned error: %v", err)
205 }
206 if version != "1.3.0" {
207 t.Fatalf("expected 1.3.0, got %s", version)
208 }
209
210 version, err = resolveReleaseVersion("2.0.0", "", existing)
211 if err != nil {
212 t.Fatalf("resolveReleaseVersion returned error: %v", err)
213 }
214 if version != "2.0.0" {
215 t.Fatalf("expected 2.0.0, got %s", version)
216 }
217
218 if _, err := resolveReleaseVersion("2.0.0", "patch", existing); err == nil {
219 t.Fatal("expected error when both --version and --bump are set")
220 }
221
222 blankChangelog := &changelog.Changelog{}
223 version, err = resolveReleaseVersion("", "patch", blankChangelog)
224 if err != nil {
225 t.Fatalf("resolveReleaseVersion returned error: %v", err)
226 }
227 if version != "0.0.1" {
228 t.Fatalf("expected 0.0.1 for empty changelog, got %s", version)
229 }
230}
231
232func TestReleaseOutput_JSONStructure(t *testing.T) {
233 output := ReleaseOutput{
234 Version: "1.0.0",
235 Date: "2024-01-15",
236 EntriesCount: 3,
237 ChangelogPath: "CHANGELOG.md",
238 TagCreated: true,
239 TagName: "v1.0.0",
240 ChangesCleared: true,
241 DeletedCount: 3,
242 DryRun: false,
243 VersionData: &changelog.Version{
244 Number: "1.0.0",
245 Date: "2024-01-15",
246 Sections: []changelog.Section{
247 {
248 Type: "added",
249 Entries: []string{"Feature 1"},
250 },
251 },
252 },
253 }
254
255 jsonBytes, err := json.MarshalIndent(output, "", " ")
256 if err != nil {
257 t.Fatalf("Failed to marshal JSON: %v", err)
258 }
259
260 var unmarshaled ReleaseOutput
261 err = json.Unmarshal(jsonBytes, &unmarshaled)
262 if err != nil {
263 t.Fatalf("Failed to unmarshal JSON: %v", err)
264 }
265
266 testutils.Expect.Equal(t, unmarshaled.Version, "1.0.0")
267 testutils.Expect.Equal(t, unmarshaled.Date, "2024-01-15")
268 testutils.Expect.Equal(t, unmarshaled.EntriesCount, 3)
269 testutils.Expect.Equal(t, unmarshaled.TagCreated, true)
270 testutils.Expect.Equal(t, unmarshaled.TagName, "v1.0.0")
271 testutils.Expect.Equal(t, unmarshaled.ChangesCleared, true)
272 testutils.Expect.Equal(t, unmarshaled.DeletedCount, 3)
273}
274
275func TestReleaseOutput_DryRunJSON(t *testing.T) {
276 output := ReleaseOutput{
277 Version: "1.0.0",
278 Date: "2024-01-15",
279 EntriesCount: 2,
280 ChangelogPath: "CHANGELOG.md",
281 DryRun: true,
282 VersionData: &changelog.Version{
283 Number: "1.0.0",
284 Date: "2024-01-15",
285 Sections: []changelog.Section{
286 {
287 Type: "fixed",
288 Entries: []string{"Bug fix"},
289 },
290 },
291 },
292 }
293
294 jsonBytes, err := json.MarshalIndent(output, "", " ")
295 if err != nil {
296 t.Fatalf("Failed to marshal JSON: %v", err)
297 }
298
299 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"dry_run": true`))
300 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"tag_created": false`))
301 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"changes_cleared": false`))
302}