Coverage improvements: - pkg/build: 89.4% - pkg/release: 86.7% (from 36.7%) - pkg/container: 85.7% - pkg/php: 62.1% (from 26%) - pkg/devops: 56.7% (from 33.1%) - pkg/release/publishers: 54.7% Also: - Add GEMINI.md for Gemini agent guidance - Update .gitignore to exclude coverage files - Remove stray core.go at root - Add core go cov command for coverage reports Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
695 lines
19 KiB
Go
695 lines
19 KiB
Go
package release
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestParseConventionalCommit_Good(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected *ConventionalCommit
|
|
}{
|
|
{
|
|
name: "feat without scope",
|
|
input: "abc1234 feat: add new feature",
|
|
expected: &ConventionalCommit{
|
|
Type: "feat",
|
|
Scope: "",
|
|
Description: "add new feature",
|
|
Hash: "abc1234",
|
|
Breaking: false,
|
|
},
|
|
},
|
|
{
|
|
name: "fix with scope",
|
|
input: "def5678 fix(auth): resolve login issue",
|
|
expected: &ConventionalCommit{
|
|
Type: "fix",
|
|
Scope: "auth",
|
|
Description: "resolve login issue",
|
|
Hash: "def5678",
|
|
Breaking: false,
|
|
},
|
|
},
|
|
{
|
|
name: "breaking change with exclamation",
|
|
input: "ghi9012 feat!: breaking API change",
|
|
expected: &ConventionalCommit{
|
|
Type: "feat",
|
|
Scope: "",
|
|
Description: "breaking API change",
|
|
Hash: "ghi9012",
|
|
Breaking: true,
|
|
},
|
|
},
|
|
{
|
|
name: "breaking change with scope",
|
|
input: "jkl3456 fix(api)!: remove deprecated endpoint",
|
|
expected: &ConventionalCommit{
|
|
Type: "fix",
|
|
Scope: "api",
|
|
Description: "remove deprecated endpoint",
|
|
Hash: "jkl3456",
|
|
Breaking: true,
|
|
},
|
|
},
|
|
{
|
|
name: "perf type",
|
|
input: "mno7890 perf: optimize database queries",
|
|
expected: &ConventionalCommit{
|
|
Type: "perf",
|
|
Scope: "",
|
|
Description: "optimize database queries",
|
|
Hash: "mno7890",
|
|
Breaking: false,
|
|
},
|
|
},
|
|
{
|
|
name: "chore type",
|
|
input: "pqr1234 chore: update dependencies",
|
|
expected: &ConventionalCommit{
|
|
Type: "chore",
|
|
Scope: "",
|
|
Description: "update dependencies",
|
|
Hash: "pqr1234",
|
|
Breaking: false,
|
|
},
|
|
},
|
|
{
|
|
name: "uppercase type normalizes to lowercase",
|
|
input: "stu5678 FEAT: uppercase type",
|
|
expected: &ConventionalCommit{
|
|
Type: "feat",
|
|
Scope: "",
|
|
Description: "uppercase type",
|
|
Hash: "stu5678",
|
|
Breaking: false,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := parseConventionalCommit(tc.input)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, tc.expected.Type, result.Type)
|
|
assert.Equal(t, tc.expected.Scope, result.Scope)
|
|
assert.Equal(t, tc.expected.Description, result.Description)
|
|
assert.Equal(t, tc.expected.Hash, result.Hash)
|
|
assert.Equal(t, tc.expected.Breaking, result.Breaking)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseConventionalCommit_Bad(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
}{
|
|
{
|
|
name: "non-conventional commit",
|
|
input: "abc1234 Update README",
|
|
},
|
|
{
|
|
name: "missing colon",
|
|
input: "def5678 feat add feature",
|
|
},
|
|
{
|
|
name: "empty subject",
|
|
input: "ghi9012",
|
|
},
|
|
{
|
|
name: "just hash",
|
|
input: "abc1234",
|
|
},
|
|
{
|
|
name: "merge commit",
|
|
input: "abc1234 Merge pull request #123",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := parseConventionalCommit(tc.input)
|
|
assert.Nil(t, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormatChangelog_Good(t *testing.T) {
|
|
t.Run("formats commits by type", func(t *testing.T) {
|
|
commits := []ConventionalCommit{
|
|
{Type: "feat", Description: "add feature A", Hash: "abc1234"},
|
|
{Type: "fix", Description: "fix bug B", Hash: "def5678"},
|
|
{Type: "feat", Description: "add feature C", Hash: "ghi9012"},
|
|
}
|
|
|
|
result := formatChangelog(commits, "v1.0.0")
|
|
|
|
assert.Contains(t, result, "## v1.0.0")
|
|
assert.Contains(t, result, "### Features")
|
|
assert.Contains(t, result, "### Bug Fixes")
|
|
assert.Contains(t, result, "- add feature A (abc1234)")
|
|
assert.Contains(t, result, "- fix bug B (def5678)")
|
|
assert.Contains(t, result, "- add feature C (ghi9012)")
|
|
})
|
|
|
|
t.Run("includes scope in output", func(t *testing.T) {
|
|
commits := []ConventionalCommit{
|
|
{Type: "feat", Scope: "api", Description: "add endpoint", Hash: "abc1234"},
|
|
}
|
|
|
|
result := formatChangelog(commits, "v1.0.0")
|
|
|
|
assert.Contains(t, result, "**api**: add endpoint")
|
|
})
|
|
|
|
t.Run("breaking changes first", func(t *testing.T) {
|
|
commits := []ConventionalCommit{
|
|
{Type: "feat", Description: "normal feature", Hash: "abc1234"},
|
|
{Type: "feat", Description: "breaking feature", Hash: "def5678", Breaking: true},
|
|
}
|
|
|
|
result := formatChangelog(commits, "v1.0.0")
|
|
|
|
assert.Contains(t, result, "### BREAKING CHANGES")
|
|
// Breaking changes section should appear before Features
|
|
breakingPos := indexOf(result, "BREAKING CHANGES")
|
|
featuresPos := indexOf(result, "Features")
|
|
assert.Less(t, breakingPos, featuresPos)
|
|
})
|
|
|
|
t.Run("empty commits returns minimal changelog", func(t *testing.T) {
|
|
result := formatChangelog([]ConventionalCommit{}, "v1.0.0")
|
|
|
|
assert.Contains(t, result, "## v1.0.0")
|
|
assert.Contains(t, result, "No notable changes")
|
|
})
|
|
}
|
|
|
|
func TestParseCommitType_Good(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
}{
|
|
{"feat: add feature", "feat"},
|
|
{"fix(scope): fix bug", "fix"},
|
|
{"perf!: breaking perf", "perf"},
|
|
{"chore: update deps", "chore"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.input, func(t *testing.T) {
|
|
result := ParseCommitType(tc.input)
|
|
assert.Equal(t, tc.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCommitType_Bad(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
}{
|
|
{"not a conventional commit"},
|
|
{"Update README"},
|
|
{"Merge branch 'main'"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.input, func(t *testing.T) {
|
|
result := ParseCommitType(tc.input)
|
|
assert.Empty(t, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateWithConfig_ConfigValues(t *testing.T) {
|
|
t.Run("config filters are parsed correctly", func(t *testing.T) {
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat", "fix"},
|
|
Exclude: []string{"chore", "docs"},
|
|
}
|
|
|
|
// Verify the config values
|
|
assert.Contains(t, cfg.Include, "feat")
|
|
assert.Contains(t, cfg.Include, "fix")
|
|
assert.Contains(t, cfg.Exclude, "chore")
|
|
assert.Contains(t, cfg.Exclude, "docs")
|
|
})
|
|
}
|
|
|
|
// indexOf returns the position of a substring in a string, or -1 if not found.
|
|
func indexOf(s, substr string) int {
|
|
for i := 0; i+len(substr) <= len(s); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// setupChangelogGitRepo creates a temporary directory with an initialized git repository.
|
|
func setupChangelogGitRepo(t *testing.T) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
|
|
// Initialize git repo
|
|
cmd := exec.Command("git", "init")
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
|
|
// Configure git user for commits
|
|
cmd = exec.Command("git", "config", "user.email", "test@example.com")
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
|
|
cmd = exec.Command("git", "config", "user.name", "Test User")
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
|
|
return dir
|
|
}
|
|
|
|
// createChangelogCommit creates a commit in the given directory.
|
|
func createChangelogCommit(t *testing.T, dir, message string) {
|
|
t.Helper()
|
|
|
|
// Create or modify a file
|
|
filePath := filepath.Join(dir, "changelog_test.txt")
|
|
content, _ := os.ReadFile(filePath)
|
|
content = append(content, []byte(message+"\n")...)
|
|
require.NoError(t, os.WriteFile(filePath, content, 0644))
|
|
|
|
// Stage and commit
|
|
cmd := exec.Command("git", "add", ".")
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
|
|
cmd = exec.Command("git", "commit", "-m", message)
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
}
|
|
|
|
// createChangelogTag creates a tag in the given directory.
|
|
func createChangelogTag(t *testing.T, dir, tag string) {
|
|
t.Helper()
|
|
cmd := exec.Command("git", "tag", tag)
|
|
cmd.Dir = dir
|
|
require.NoError(t, cmd.Run())
|
|
}
|
|
|
|
func TestGenerate_Good(t *testing.T) {
|
|
t.Run("generates changelog from commits", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: add new feature")
|
|
createChangelogCommit(t, dir, "fix: resolve bug")
|
|
|
|
changelog, err := Generate(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "## HEAD")
|
|
assert.Contains(t, changelog, "### Features")
|
|
assert.Contains(t, changelog, "add new feature")
|
|
assert.Contains(t, changelog, "### Bug Fixes")
|
|
assert.Contains(t, changelog, "resolve bug")
|
|
})
|
|
|
|
t.Run("generates changelog between tags", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: initial feature")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
createChangelogCommit(t, dir, "fix: bug fix")
|
|
createChangelogTag(t, dir, "v1.1.0")
|
|
|
|
changelog, err := Generate(dir, "v1.0.0", "v1.1.0")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "## v1.1.0")
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.Contains(t, changelog, "bug fix")
|
|
// Should NOT contain the initial feature
|
|
assert.NotContains(t, changelog, "initial feature")
|
|
})
|
|
|
|
t.Run("handles empty changelog when no conventional commits", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "Update README")
|
|
createChangelogCommit(t, dir, "Merge branch main")
|
|
|
|
changelog, err := Generate(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "No notable changes")
|
|
})
|
|
|
|
t.Run("uses previous tag when fromRef is empty", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: old feature")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
|
|
changelog, err := Generate(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.NotContains(t, changelog, "old feature")
|
|
})
|
|
|
|
t.Run("includes breaking changes", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat!: breaking API change")
|
|
createChangelogCommit(t, dir, "feat: normal feature")
|
|
|
|
changelog, err := Generate(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "### BREAKING CHANGES")
|
|
assert.Contains(t, changelog, "breaking API change")
|
|
})
|
|
|
|
t.Run("includes scope in output", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat(api): add endpoint")
|
|
|
|
changelog, err := Generate(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "**api**:")
|
|
})
|
|
}
|
|
|
|
func TestGenerate_Bad(t *testing.T) {
|
|
t.Run("returns error for non-git directory", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
_, err := Generate(dir, "", "HEAD")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGenerateWithConfig_Good(t *testing.T) {
|
|
t.Run("filters commits by include list", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
createChangelogCommit(t, dir, "fix: bug fix")
|
|
createChangelogCommit(t, dir, "chore: update deps")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
changelog, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.NotContains(t, changelog, "bug fix")
|
|
assert.NotContains(t, changelog, "update deps")
|
|
})
|
|
|
|
t.Run("filters commits by exclude list", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
createChangelogCommit(t, dir, "fix: bug fix")
|
|
createChangelogCommit(t, dir, "chore: update deps")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Exclude: []string{"chore"},
|
|
}
|
|
|
|
changelog, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.Contains(t, changelog, "bug fix")
|
|
assert.NotContains(t, changelog, "update deps")
|
|
})
|
|
|
|
t.Run("combines include and exclude filters", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
createChangelogCommit(t, dir, "fix: bug fix")
|
|
createChangelogCommit(t, dir, "perf: performance")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat", "fix", "perf"},
|
|
Exclude: []string{"perf"},
|
|
}
|
|
|
|
changelog, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.Contains(t, changelog, "bug fix")
|
|
assert.NotContains(t, changelog, "performance")
|
|
})
|
|
}
|
|
|
|
func TestGetCommits_Good(t *testing.T) {
|
|
t.Run("returns all commits when fromRef is empty", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
createChangelogCommit(t, dir, "feat: second")
|
|
createChangelogCommit(t, dir, "feat: third")
|
|
|
|
commits, err := getCommits(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, commits, 3)
|
|
})
|
|
|
|
t.Run("returns commits between refs", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: second")
|
|
createChangelogCommit(t, dir, "feat: third")
|
|
|
|
commits, err := getCommits(dir, "v1.0.0", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, commits, 2)
|
|
})
|
|
|
|
t.Run("excludes merge commits", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: regular commit")
|
|
// Merge commits are excluded by --no-merges flag
|
|
// We can verify by checking the count matches expected
|
|
|
|
commits, err := getCommits(dir, "", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, commits, 1)
|
|
assert.Contains(t, commits[0], "regular commit")
|
|
})
|
|
|
|
t.Run("returns empty slice for no commits in range", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: only commit")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
|
|
commits, err := getCommits(dir, "v1.0.0", "HEAD")
|
|
require.NoError(t, err)
|
|
|
|
assert.Empty(t, commits)
|
|
})
|
|
}
|
|
|
|
func TestGetCommits_Bad(t *testing.T) {
|
|
t.Run("returns error for invalid ref", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: commit")
|
|
|
|
_, err := getCommits(dir, "nonexistent-tag", "HEAD")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("returns error for non-git directory", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
_, err := getCommits(dir, "", "HEAD")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGetPreviousTag_Good(t *testing.T) {
|
|
t.Run("returns previous tag", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: second")
|
|
createChangelogTag(t, dir, "v1.1.0")
|
|
|
|
tag, err := getPreviousTag(dir, "v1.1.0")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "v1.0.0", tag)
|
|
})
|
|
|
|
t.Run("returns tag before HEAD", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: second")
|
|
|
|
tag, err := getPreviousTag(dir, "HEAD")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "v1.0.0", tag)
|
|
})
|
|
}
|
|
|
|
func TestGetPreviousTag_Bad(t *testing.T) {
|
|
t.Run("returns error when no previous tag exists", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
|
|
// v1.0.0^ has no tag before it
|
|
_, err := getPreviousTag(dir, "v1.0.0")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("returns error for invalid ref", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: commit")
|
|
|
|
_, err := getPreviousTag(dir, "nonexistent")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestFormatCommitLine_Good(t *testing.T) {
|
|
t.Run("formats commit without scope", func(t *testing.T) {
|
|
commit := ConventionalCommit{
|
|
Type: "feat",
|
|
Description: "add feature",
|
|
Hash: "abc1234",
|
|
}
|
|
|
|
result := formatCommitLine(commit)
|
|
assert.Equal(t, "- add feature (abc1234)\n", result)
|
|
})
|
|
|
|
t.Run("formats commit with scope", func(t *testing.T) {
|
|
commit := ConventionalCommit{
|
|
Type: "fix",
|
|
Scope: "api",
|
|
Description: "fix bug",
|
|
Hash: "def5678",
|
|
}
|
|
|
|
result := formatCommitLine(commit)
|
|
assert.Equal(t, "- **api**: fix bug (def5678)\n", result)
|
|
})
|
|
}
|
|
|
|
func TestFormatChangelog_Ugly(t *testing.T) {
|
|
t.Run("handles custom commit type not in order", func(t *testing.T) {
|
|
commits := []ConventionalCommit{
|
|
{Type: "custom", Description: "custom type", Hash: "abc1234"},
|
|
}
|
|
|
|
result := formatChangelog(commits, "v1.0.0")
|
|
|
|
assert.Contains(t, result, "### Custom")
|
|
assert.Contains(t, result, "custom type")
|
|
})
|
|
|
|
t.Run("handles multiple custom commit types", func(t *testing.T) {
|
|
commits := []ConventionalCommit{
|
|
{Type: "alpha", Description: "alpha feature", Hash: "abc1234"},
|
|
{Type: "beta", Description: "beta feature", Hash: "def5678"},
|
|
}
|
|
|
|
result := formatChangelog(commits, "v1.0.0")
|
|
|
|
// Should be sorted alphabetically for custom types
|
|
assert.Contains(t, result, "### Alpha")
|
|
assert.Contains(t, result, "### Beta")
|
|
})
|
|
}
|
|
|
|
func TestGenerateWithConfig_Bad(t *testing.T) {
|
|
t.Run("returns error for non-git directory", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
_, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestGenerateWithConfig_EdgeCases(t *testing.T) {
|
|
t.Run("uses HEAD when toRef is empty", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
// Pass empty toRef
|
|
changelog, err := GenerateWithConfig(dir, "", "", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "## HEAD")
|
|
})
|
|
|
|
t.Run("handles previous tag lookup failure gracefully", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: first")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
// No tags exist, should still work
|
|
changelog, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "first")
|
|
})
|
|
|
|
t.Run("uses explicit fromRef when provided", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: old feature")
|
|
createChangelogTag(t, dir, "v1.0.0")
|
|
createChangelogCommit(t, dir, "feat: new feature")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
// Use explicit fromRef
|
|
changelog, err := GenerateWithConfig(dir, "v1.0.0", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "new feature")
|
|
assert.NotContains(t, changelog, "old feature")
|
|
})
|
|
|
|
t.Run("skips non-conventional commits", func(t *testing.T) {
|
|
dir := setupChangelogGitRepo(t)
|
|
createChangelogCommit(t, dir, "feat: conventional commit")
|
|
createChangelogCommit(t, dir, "Update README")
|
|
|
|
cfg := &ChangelogConfig{
|
|
Include: []string{"feat"},
|
|
}
|
|
|
|
changelog, err := GenerateWithConfig(dir, "", "HEAD", cfg)
|
|
require.NoError(t, err)
|
|
|
|
assert.Contains(t, changelog, "conventional commit")
|
|
assert.NotContains(t, changelog, "Update README")
|
|
})
|
|
}
|