Compare commits
2 Commits
3ea4af158e
...
4c5a49d685
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c5a49d685 | ||
|
|
87059d21fd |
120
cmd/vociferate/main_test.go
Normal file
120
cmd/vociferate/main_test.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMainRecommendPrintsTag(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n")
|
||||||
|
writeFile(t, filepath.Join(root, "changelog.md"), "# Changelog\n\n## [Unreleased]\n\n### Added\n\n- Feature.\n\n## [1.1.6] - 2017-12-20\n")
|
||||||
|
|
||||||
|
stdout, stderr, code := runMain(t, "--recommend", "--root", root)
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got %d (stderr: %s)", code, stderr)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(stdout) != "v1.2.0" {
|
||||||
|
t.Fatalf("unexpected recommended tag: %q", strings.TrimSpace(stdout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMainUsageExitWhenRequiredFlagsMissing(t *testing.T) {
|
||||||
|
_, stderr, code := runMain(t)
|
||||||
|
if code != 2 {
|
||||||
|
t.Fatalf("expected exit 2, got %d", code)
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr, "usage: vociferate") {
|
||||||
|
t.Fatalf("expected usage text in stderr, got: %s", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMainPrepareUpdatesFiles(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
writeFile(t, filepath.Join(root, ".git", "config"), "[remote \"origin\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n")
|
||||||
|
writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n")
|
||||||
|
writeFile(t, filepath.Join(root, "changelog.md"), "# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Patch note.\n\n## [1.1.6] - 2017-12-20\n")
|
||||||
|
|
||||||
|
_, stderr, code := runMain(t, "--version", "v1.1.7", "--date", "2026-03-20", "--root", root)
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("expected exit 0, got %d (stderr: %s)", code, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionBytes, err := os.ReadFile(filepath.Join(root, "release-version"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read release-version: %v", err)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(string(versionBytes)) != "1.1.7" {
|
||||||
|
t.Fatalf("unexpected version file value: %q", string(versionBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMainPrepareReturnsExitOneOnFailure(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
_, stderr, code := runMain(t, "--version", "v1.1.7", "--date", "2026-03-20", "--root", root)
|
||||||
|
if code != 1 {
|
||||||
|
t.Fatalf("expected exit 1, got %d", code)
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr, "prepare release") {
|
||||||
|
t.Fatalf("expected prepare error in stderr, got: %s", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelperProcess(t *testing.T) {
|
||||||
|
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := -1
|
||||||
|
for i, arg := range os.Args {
|
||||||
|
if arg == "--" {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if idx == -1 {
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := append([]string{"vociferate"}, os.Args[idx+1:]...)
|
||||||
|
os.Args = args
|
||||||
|
flag.CommandLine = flag.NewFlagSet(args[0], flag.ExitOnError)
|
||||||
|
|
||||||
|
main()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMain(t *testing.T, args ...string) (string, string, int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cmdArgs := append([]string{"-test.run=TestHelperProcess", "--"}, args...)
|
||||||
|
cmd := exec.Command(os.Args[0], cmdArgs...)
|
||||||
|
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||||
|
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
output := string(out)
|
||||||
|
if err == nil {
|
||||||
|
return output, "", 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
return "", output, exitErr.ExitCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("run helper process: %v", err)
|
||||||
|
return "", "", -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFile(t *testing.T, path, content string) {
|
||||||
|
t.Helper()
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||||
|
t.Fatalf("mkdir for %s: %v", path, err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||||
|
t.Fatalf("write file %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
123
internal/vociferate/vociferate_internal_test.go
Normal file
123
internal/vociferate/vociferate_internal_test.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package vociferate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNormalizeRepoURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
remoteURL string
|
||||||
|
wantURL string
|
||||||
|
wantOK bool
|
||||||
|
}{
|
||||||
|
{name: "https", remoteURL: "https://git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
|
||||||
|
{name: "http", remoteURL: "http://teapot:3000/aether/vociferate.git", wantURL: "http://teapot:3000/aether/vociferate", wantOK: true},
|
||||||
|
{name: "ssh with scheme", remoteURL: "ssh://git@git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
|
||||||
|
{name: "scp style", remoteURL: "git@git.hrafn.xyz:aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
|
||||||
|
{name: "empty", remoteURL: "", wantURL: "", wantOK: false},
|
||||||
|
{name: "unsupported scheme", remoteURL: "ftp://example.com/repo.git", wantURL: "", wantOK: false},
|
||||||
|
{name: "invalid ssh missing user", remoteURL: "ssh://git.hrafn.xyz/aether/vociferate.git", wantURL: "", wantOK: false},
|
||||||
|
{name: "invalid scp style", remoteURL: "git.hrafn.xyz:aether/vociferate.git", wantURL: "", wantOK: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
gotURL, gotOK := normalizeRepoURL(tt.remoteURL)
|
||||||
|
if gotOK != tt.wantOK {
|
||||||
|
t.Fatalf("normalizeRepoURL(%q) ok = %v, want %v", tt.remoteURL, gotOK, tt.wantOK)
|
||||||
|
}
|
||||||
|
if gotURL != tt.wantURL {
|
||||||
|
t.Fatalf("normalizeRepoURL(%q) url = %q, want %q", tt.remoteURL, gotURL, tt.wantURL)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseSemver(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want semver
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{name: "valid", input: "1.2.3", want: semver{major: 1, minor: 2, patch: 3}, wantErr: false},
|
||||||
|
{name: "missing part", input: "1.2", wantErr: true},
|
||||||
|
{name: "non numeric", input: "1.two.3", wantErr: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
got, err := parseSemver(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("parseSemver(%q) expected error", tt.input)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseSemver(%q) unexpected error: %v", tt.input, err)
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Fatalf("parseSemver(%q) = %+v, want %+v", tt.input, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOriginRemoteURLFromGitConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("origin exists", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
config := "[core]\n\trepositoryformatversion = 0\n[remote \"origin\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n"
|
||||||
|
url, ok := originRemoteURLFromGitConfig(config)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected origin url to be found")
|
||||||
|
}
|
||||||
|
if url != "git@git.hrafn.xyz:aether/vociferate.git" {
|
||||||
|
t.Fatalf("unexpected url: %q", url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("origin missing", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
config := "[core]\n\trepositoryformatversion = 0\n[remote \"upstream\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n"
|
||||||
|
_, ok := originRemoteURLFromGitConfig(config)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("expected origin url to be absent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeriveRepositoryURLFromGitConfigFallback(t *testing.T) {
|
||||||
|
t.Setenv("GITHUB_SERVER_URL", "")
|
||||||
|
t.Setenv("GITHUB_REPOSITORY", "")
|
||||||
|
|
||||||
|
root := t.TempDir()
|
||||||
|
configPath := filepath.Join(root, ".git", "config")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
|
||||||
|
t.Fatalf("mkdir .git: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(configPath, []byte("[remote \"origin\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n"), 0o644); err != nil {
|
||||||
|
t.Fatalf("write git config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
url, ok := deriveRepositoryURL(root)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected repository URL from git config")
|
||||||
|
}
|
||||||
|
if url != "https://git.hrafn.xyz/aether/vociferate" {
|
||||||
|
t.Fatalf("unexpected repository URL: %q", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,9 @@ runs:
|
|||||||
elif [[ "${GITHUB_REF_VALUE}" == refs/tags/* ]]; then
|
elif [[ "${GITHUB_REF_VALUE}" == refs/tags/* ]]; then
|
||||||
tag="${GITHUB_REF_VALUE#refs/tags/}"
|
tag="${GITHUB_REF_VALUE#refs/tags/}"
|
||||||
normalized="${tag#v}"
|
normalized="${tag#v}"
|
||||||
|
elif head_tag="$(git describe --exact-match --tags HEAD 2>/dev/null)" && [[ -n "$head_tag" ]]; then
|
||||||
|
tag="$head_tag"
|
||||||
|
normalized="${tag#v}"
|
||||||
else
|
else
|
||||||
echo "A version input is required when the workflow is not running from a tag push" >&2
|
echo "A version input is required when the workflow is not running from a tag push" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user