345 lines
9.1 KiB
Go
345 lines
9.1 KiB
Go
|
|
package publishers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"os"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/host-uk/core/pkg/build"
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_Name_Good(t *testing.T) {
|
||
|
|
t.Run("returns homebrew", func(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
assert.Equal(t, "homebrew", p.Name())
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_ParseConfig_Good(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
|
||
|
|
t.Run("uses defaults when no extended config", func(t *testing.T) {
|
||
|
|
pubCfg := PublisherConfig{Type: "homebrew"}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
cfg := p.parseConfig(pubCfg, relCfg)
|
||
|
|
|
||
|
|
assert.Empty(t, cfg.Tap)
|
||
|
|
assert.Empty(t, cfg.Formula)
|
||
|
|
assert.Nil(t, cfg.Official)
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("parses tap and formula from extended config", func(t *testing.T) {
|
||
|
|
pubCfg := PublisherConfig{
|
||
|
|
Type: "homebrew",
|
||
|
|
Extended: map[string]any{
|
||
|
|
"tap": "host-uk/homebrew-tap",
|
||
|
|
"formula": "myformula",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
cfg := p.parseConfig(pubCfg, relCfg)
|
||
|
|
|
||
|
|
assert.Equal(t, "host-uk/homebrew-tap", cfg.Tap)
|
||
|
|
assert.Equal(t, "myformula", cfg.Formula)
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("parses official config", func(t *testing.T) {
|
||
|
|
pubCfg := PublisherConfig{
|
||
|
|
Type: "homebrew",
|
||
|
|
Extended: map[string]any{
|
||
|
|
"official": map[string]any{
|
||
|
|
"enabled": true,
|
||
|
|
"output": "dist/brew",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
cfg := p.parseConfig(pubCfg, relCfg)
|
||
|
|
|
||
|
|
require.NotNil(t, cfg.Official)
|
||
|
|
assert.True(t, cfg.Official.Enabled)
|
||
|
|
assert.Equal(t, "dist/brew", cfg.Official.Output)
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("handles missing official fields", func(t *testing.T) {
|
||
|
|
pubCfg := PublisherConfig{
|
||
|
|
Type: "homebrew",
|
||
|
|
Extended: map[string]any{
|
||
|
|
"official": map[string]any{},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
cfg := p.parseConfig(pubCfg, relCfg)
|
||
|
|
|
||
|
|
require.NotNil(t, cfg.Official)
|
||
|
|
assert.False(t, cfg.Official.Enabled)
|
||
|
|
assert.Empty(t, cfg.Official.Output)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_ToFormulaClass_Good(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
input string
|
||
|
|
expected string
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "simple name",
|
||
|
|
input: "core",
|
||
|
|
expected: "Core",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "kebab case",
|
||
|
|
input: "my-cli-tool",
|
||
|
|
expected: "MyCliTool",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "already capitalised",
|
||
|
|
input: "CLI",
|
||
|
|
expected: "CLI",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "single letter",
|
||
|
|
input: "x",
|
||
|
|
expected: "X",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "multiple dashes",
|
||
|
|
input: "my-super-cool-app",
|
||
|
|
expected: "MySuperCoolApp",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, tc := range tests {
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
result := toFormulaClass(tc.input)
|
||
|
|
assert.Equal(t, tc.expected, result)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_BuildChecksumMap_Good(t *testing.T) {
|
||
|
|
t.Run("maps artifacts to checksums by platform", func(t *testing.T) {
|
||
|
|
artifacts := []build.Artifact{
|
||
|
|
{Path: "/dist/myapp-darwin-amd64.tar.gz", OS: "darwin", Arch: "amd64", Checksum: "abc123"},
|
||
|
|
{Path: "/dist/myapp-darwin-arm64.tar.gz", OS: "darwin", Arch: "arm64", Checksum: "def456"},
|
||
|
|
{Path: "/dist/myapp-linux-amd64.tar.gz", OS: "linux", Arch: "amd64", Checksum: "ghi789"},
|
||
|
|
{Path: "/dist/myapp-linux-arm64.tar.gz", OS: "linux", Arch: "arm64", Checksum: "jkl012"},
|
||
|
|
{Path: "/dist/myapp-windows-amd64.zip", OS: "windows", Arch: "amd64", Checksum: "mno345"},
|
||
|
|
{Path: "/dist/myapp-windows-arm64.zip", OS: "windows", Arch: "arm64", Checksum: "pqr678"},
|
||
|
|
}
|
||
|
|
|
||
|
|
checksums := buildChecksumMap(artifacts)
|
||
|
|
|
||
|
|
assert.Equal(t, "abc123", checksums.DarwinAmd64)
|
||
|
|
assert.Equal(t, "def456", checksums.DarwinArm64)
|
||
|
|
assert.Equal(t, "ghi789", checksums.LinuxAmd64)
|
||
|
|
assert.Equal(t, "jkl012", checksums.LinuxArm64)
|
||
|
|
assert.Equal(t, "mno345", checksums.WindowsAmd64)
|
||
|
|
assert.Equal(t, "pqr678", checksums.WindowsArm64)
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("handles empty artifacts", func(t *testing.T) {
|
||
|
|
checksums := buildChecksumMap([]build.Artifact{})
|
||
|
|
|
||
|
|
assert.Empty(t, checksums.DarwinAmd64)
|
||
|
|
assert.Empty(t, checksums.DarwinArm64)
|
||
|
|
assert.Empty(t, checksums.LinuxAmd64)
|
||
|
|
assert.Empty(t, checksums.LinuxArm64)
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("handles partial platform coverage", func(t *testing.T) {
|
||
|
|
artifacts := []build.Artifact{
|
||
|
|
{Path: "/dist/myapp-darwin-arm64.tar.gz", Checksum: "def456"},
|
||
|
|
{Path: "/dist/myapp-linux-amd64.tar.gz", Checksum: "ghi789"},
|
||
|
|
}
|
||
|
|
|
||
|
|
checksums := buildChecksumMap(artifacts)
|
||
|
|
|
||
|
|
assert.Empty(t, checksums.DarwinAmd64)
|
||
|
|
assert.Equal(t, "def456", checksums.DarwinArm64)
|
||
|
|
assert.Equal(t, "ghi789", checksums.LinuxAmd64)
|
||
|
|
assert.Empty(t, checksums.LinuxArm64)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_RenderTemplate_Good(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
|
||
|
|
t.Run("renders formula template with data", func(t *testing.T) {
|
||
|
|
data := homebrewTemplateData{
|
||
|
|
FormulaClass: "MyApp",
|
||
|
|
Description: "My awesome CLI",
|
||
|
|
Repository: "owner/myapp",
|
||
|
|
Version: "1.2.3",
|
||
|
|
License: "MIT",
|
||
|
|
BinaryName: "myapp",
|
||
|
|
Checksums: ChecksumMap{
|
||
|
|
DarwinAmd64: "abc123",
|
||
|
|
DarwinArm64: "def456",
|
||
|
|
LinuxAmd64: "ghi789",
|
||
|
|
LinuxArm64: "jkl012",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
result, err := p.renderTemplate("templates/homebrew/formula.rb.tmpl", data)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
assert.Contains(t, result, "class MyApp < Formula")
|
||
|
|
assert.Contains(t, result, `desc "My awesome CLI"`)
|
||
|
|
assert.Contains(t, result, `version "1.2.3"`)
|
||
|
|
assert.Contains(t, result, `license "MIT"`)
|
||
|
|
assert.Contains(t, result, "owner/myapp")
|
||
|
|
assert.Contains(t, result, "abc123")
|
||
|
|
assert.Contains(t, result, "def456")
|
||
|
|
assert.Contains(t, result, "ghi789")
|
||
|
|
assert.Contains(t, result, "jkl012")
|
||
|
|
assert.Contains(t, result, `bin.install "myapp"`)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_RenderTemplate_Bad(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
|
||
|
|
t.Run("returns error for non-existent template", func(t *testing.T) {
|
||
|
|
data := homebrewTemplateData{}
|
||
|
|
_, err := p.renderTemplate("templates/homebrew/nonexistent.tmpl", data)
|
||
|
|
assert.Error(t, err)
|
||
|
|
assert.Contains(t, err.Error(), "failed to read template")
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_DryRunPublish_Good(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
|
||
|
|
t.Run("outputs expected dry run information", func(t *testing.T) {
|
||
|
|
// Capture stdout
|
||
|
|
oldStdout := os.Stdout
|
||
|
|
r, w, _ := os.Pipe()
|
||
|
|
os.Stdout = w
|
||
|
|
|
||
|
|
data := homebrewTemplateData{
|
||
|
|
FormulaClass: "MyApp",
|
||
|
|
Description: "My CLI",
|
||
|
|
Repository: "owner/repo",
|
||
|
|
Version: "1.0.0",
|
||
|
|
License: "MIT",
|
||
|
|
BinaryName: "myapp",
|
||
|
|
Checksums: ChecksumMap{},
|
||
|
|
}
|
||
|
|
cfg := HomebrewConfig{
|
||
|
|
Tap: "owner/homebrew-tap",
|
||
|
|
}
|
||
|
|
|
||
|
|
err := p.dryRunPublish(data, cfg)
|
||
|
|
|
||
|
|
w.Close()
|
||
|
|
var buf bytes.Buffer
|
||
|
|
_, _ = buf.ReadFrom(r)
|
||
|
|
os.Stdout = oldStdout
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
output := buf.String()
|
||
|
|
|
||
|
|
assert.Contains(t, output, "DRY RUN: Homebrew Publish")
|
||
|
|
assert.Contains(t, output, "Formula: MyApp")
|
||
|
|
assert.Contains(t, output, "Version: 1.0.0")
|
||
|
|
assert.Contains(t, output, "Tap: owner/homebrew-tap")
|
||
|
|
assert.Contains(t, output, "Repository: owner/repo")
|
||
|
|
assert.Contains(t, output, "Would commit to tap: owner/homebrew-tap")
|
||
|
|
assert.Contains(t, output, "END DRY RUN")
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("shows official output path when enabled", func(t *testing.T) {
|
||
|
|
oldStdout := os.Stdout
|
||
|
|
r, w, _ := os.Pipe()
|
||
|
|
os.Stdout = w
|
||
|
|
|
||
|
|
data := homebrewTemplateData{
|
||
|
|
FormulaClass: "MyApp",
|
||
|
|
Version: "1.0.0",
|
||
|
|
BinaryName: "myapp",
|
||
|
|
Checksums: ChecksumMap{},
|
||
|
|
}
|
||
|
|
cfg := HomebrewConfig{
|
||
|
|
Official: &OfficialConfig{
|
||
|
|
Enabled: true,
|
||
|
|
Output: "custom/path",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
err := p.dryRunPublish(data, cfg)
|
||
|
|
|
||
|
|
w.Close()
|
||
|
|
var buf bytes.Buffer
|
||
|
|
_, _ = buf.ReadFrom(r)
|
||
|
|
os.Stdout = oldStdout
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
output := buf.String()
|
||
|
|
assert.Contains(t, output, "Would write files for official PR to: custom/path")
|
||
|
|
})
|
||
|
|
|
||
|
|
t.Run("uses default official output path when not specified", func(t *testing.T) {
|
||
|
|
oldStdout := os.Stdout
|
||
|
|
r, w, _ := os.Pipe()
|
||
|
|
os.Stdout = w
|
||
|
|
|
||
|
|
data := homebrewTemplateData{
|
||
|
|
FormulaClass: "MyApp",
|
||
|
|
Version: "1.0.0",
|
||
|
|
BinaryName: "myapp",
|
||
|
|
Checksums: ChecksumMap{},
|
||
|
|
}
|
||
|
|
cfg := HomebrewConfig{
|
||
|
|
Official: &OfficialConfig{
|
||
|
|
Enabled: true,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
err := p.dryRunPublish(data, cfg)
|
||
|
|
|
||
|
|
w.Close()
|
||
|
|
var buf bytes.Buffer
|
||
|
|
_, _ = buf.ReadFrom(r)
|
||
|
|
os.Stdout = oldStdout
|
||
|
|
|
||
|
|
require.NoError(t, err)
|
||
|
|
output := buf.String()
|
||
|
|
assert.Contains(t, output, "Would write files for official PR to: dist/homebrew")
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewPublisher_Publish_Bad(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
|
||
|
|
t.Run("fails when tap not configured and not official mode", func(t *testing.T) {
|
||
|
|
release := &Release{
|
||
|
|
Version: "v1.0.0",
|
||
|
|
ProjectDir: "/project",
|
||
|
|
}
|
||
|
|
pubCfg := PublisherConfig{Type: "homebrew"}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
|
||
|
|
err := p.Publish(nil, release, pubCfg, relCfg, false)
|
||
|
|
assert.Error(t, err)
|
||
|
|
assert.Contains(t, err.Error(), "tap is required")
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHomebrewConfig_Defaults_Good(t *testing.T) {
|
||
|
|
t.Run("has sensible defaults", func(t *testing.T) {
|
||
|
|
p := NewHomebrewPublisher()
|
||
|
|
pubCfg := PublisherConfig{Type: "homebrew"}
|
||
|
|
relCfg := &mockReleaseConfig{repository: "owner/repo"}
|
||
|
|
|
||
|
|
cfg := p.parseConfig(pubCfg, relCfg)
|
||
|
|
|
||
|
|
assert.Empty(t, cfg.Tap)
|
||
|
|
assert.Empty(t, cfg.Formula)
|
||
|
|
assert.Nil(t, cfg.Official)
|
||
|
|
})
|
||
|
|
}
|