feat: 添加构建器测试框架和Linux包测试用例

This commit is contained in:
程广 2025-07-07 15:16:08 +08:00
parent c022153bcd
commit 189cf664cd
8 changed files with 1475 additions and 6 deletions

4
go.mod
View File

@ -6,21 +6,23 @@ require (
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.16.0 github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.9.5 // indirect github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/subosito/gotenv v1.4.2 // indirect github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect

5
go.sum
View File

@ -46,7 +46,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -155,14 +154,10 @@ github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=

View File

@ -0,0 +1,181 @@
package builder
import (
"errors"
"os"
"path/filepath"
"testing"
"git.kingecg.top/kingecg/installerbuilder/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// 创建一个测试用的构建器实现
type MockBuilder struct {
name string
supportedPlatforms []string
supportedFormats []string
buildFunc func() error
}
func (b *MockBuilder) Build() error {
if b.buildFunc != nil {
return b.buildFunc()
}
return nil
}
func (b *MockBuilder) Name() string {
return b.name
}
func (b *MockBuilder) SupportedPlatforms() []string {
return b.supportedPlatforms
}
func (b *MockBuilder) SupportedFormats() []string {
return b.supportedFormats
}
func TestRegisterAndGetBuilder(t *testing.T) {
// 清理注册的构建器
builderFactories = make(map[string]BuilderFactory)
// 注册一个测试构建器
RegisterBuilder("test-builder", func(cfg *config.Config, outputDir string) Builder {
return &MockBuilder{
name: "Test Builder",
supportedPlatforms: []string{"test-platform"},
supportedFormats: []string{"test-format"},
}
})
// 测试获取已注册的构建器
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder, err := GetBuilder("test-builder", cfg, "output")
assert.NoError(t, err)
assert.NotNil(t, builder)
assert.Equal(t, "Test Builder", builder.Name())
assert.Equal(t, []string{"test-platform"}, builder.SupportedPlatforms())
assert.Equal(t, []string{"test-format"}, builder.SupportedFormats())
// 测试获取未注册的构建器
_, err = GetBuilder("non-existent", cfg, "output")
assert.Error(t, err)
assert.Contains(t, err.Error(), "未找到名为 'non-existent' 的构建器")
}
func TestBuildAll(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建配置文件
configPath := filepath.Join(tempDir, "config.yaml")
configContent := `
name: TestApp
version: 1.0.0
description: Test Application
author: Test Author
license: MIT
build:
outputDir: dist
targets:
- windows
- linux
formats:
- msi
- deb
`
err = os.WriteFile(configPath, []byte(configContent), 0644)
require.NoError(t, err)
// 清理注册的构建器
builderFactories = make(map[string]BuilderFactory)
// 注册测试构建器
RegisterBuilder("windows-msi", func(cfg *config.Config, outputDir string) Builder {
return &MockBuilder{
name: "Windows MSI Builder",
supportedPlatforms: []string{"windows"},
supportedFormats: []string{"msi"},
}
})
RegisterBuilder("linux-deb", func(cfg *config.Config, outputDir string) Builder {
return &MockBuilder{
name: "Linux DEB Builder",
supportedPlatforms: []string{"linux"},
supportedFormats: []string{"deb"},
}
})
// 测试成功构建
opts := BuildOptions{
ConfigPath: configPath,
OutputDir: filepath.Join(tempDir, "output"),
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "deb"},
Parallel: 2,
}
err = BuildAll(opts)
assert.NoError(t, err)
// 测试构建失败
RegisterBuilder("windows-msi", func(cfg *config.Config, outputDir string) Builder {
return &MockBuilder{
name: "Windows MSI Builder",
supportedPlatforms: []string{"windows"},
supportedFormats: []string{"msi"},
buildFunc: func() error { return errors.New("构建失败") },
}
})
err = BuildAll(opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "构建过程中发生")
assert.Contains(t, err.Error(), "构建失败")
// 测试无效配置
opts.ConfigPath = filepath.Join(tempDir, "nonexistent.yaml")
err = BuildAll(opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "加载配置失败")
// 测试无构建任务
opts.ConfigPath = configPath
opts.Targets = []string{"invalid-platform"}
err = BuildAll(opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "没有可构建的任务")
}
func TestIsFormatSupportedForTarget(t *testing.T) {
tests := []struct {
target string
format string
expected bool
}{
{"windows", "msi", true},
{"windows", "zip", true},
{"windows", "deb", false},
{"windows", "rpm", false},
{"linux", "deb", true},
{"linux", "rpm", true},
{"linux", "tar.gz", true},
{"linux", "msi", false},
{"invalid", "msi", false},
{"windows", "invalid", false},
}
for _, tt := range tests {
t.Run(tt.target+"-"+tt.format, func(t *testing.T) {
result := isFormatSupportedForTarget(tt.target, tt.format)
assert.Equal(t, tt.expected, result)
})
}
}

View File

@ -0,0 +1,296 @@
package linux
import (
"os"
"path/filepath"
"testing"
"git.kingecg.top/kingecg/installerbuilder/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDEBBuilder_Name(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewDEBBuilder(cfg, "output")
assert.Equal(t, "Linux DEB Builder", builder.Name())
}
func TestDEBBuilder_SupportedPlatforms(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewDEBBuilder(cfg, "output")
platforms := builder.SupportedPlatforms()
assert.Equal(t, []string{"linux"}, platforms)
}
func TestDEBBuilder_SupportedFormats(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewDEBBuilder(cfg, "output")
formats := builder.SupportedFormats()
assert.Equal(t, []string{"deb"}, formats)
}
func TestDEBBuilder_CreateDebStructure(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "deb-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
}
// 创建DEB构建器
builder := NewDEBBuilder(cfg, tempDir)
// 测试创建DEB包结构
err = builder.createDebStructure(tempDir)
assert.NoError(t, err)
// 验证目录结构
debianDir := filepath.Join(tempDir, "DEBIAN")
_, err = os.Stat(debianDir)
assert.NoError(t, err)
installDir := filepath.Join(tempDir, "usr", "local", "bin")
_, err = os.Stat(installDir)
assert.NoError(t, err)
docDir := filepath.Join(tempDir, "usr", "share", "doc", "TestApp")
_, err = os.Stat(docDir)
assert.NoError(t, err)
}
func TestDEBBuilder_CreateControlFile(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "deb-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建DEBIAN目录
debianDir := filepath.Join(tempDir, "DEBIAN")
err = os.MkdirAll(debianDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
Platforms: config.PlatformsConfig{
Linux: config.LinuxConfig{
Deb: config.LinuxDebConfig{
Section: "utils",
Priority: "optional",
Architecture: "amd64",
Depends: []string{"libc6", "libstdc++6"},
Recommends: []string{"curl"},
Suggests: []string{"wget"},
Conflicts: []string{"old-package"},
Replaces: []string{"legacy-package"},
},
},
},
}
// 创建DEB构建器
builder := NewDEBBuilder(cfg, tempDir)
// 测试创建控制文件
err = builder.createControlFile(tempDir)
assert.NoError(t, err)
// 验证控制文件是否存在
controlFile := filepath.Join(tempDir, "DEBIAN", "control")
_, err = os.Stat(controlFile)
assert.NoError(t, err)
// 读取控制文件内容
content, err := os.ReadFile(controlFile)
assert.NoError(t, err)
// 验证文件内容
contentStr := string(content)
assert.Contains(t, contentStr, "Package: TestApp")
assert.Contains(t, contentStr, "Version: 1.0.0")
assert.Contains(t, contentStr, "Section: utils")
assert.Contains(t, contentStr, "Priority: optional")
assert.Contains(t, contentStr, "Architecture: amd64")
assert.Contains(t, contentStr, "Maintainer: Test Author")
assert.Contains(t, contentStr, "Description: Test Application")
assert.Contains(t, contentStr, "Depends: libc6, libstdc++6")
assert.Contains(t, contentStr, "Recommends: curl")
assert.Contains(t, contentStr, "Suggests: wget")
assert.Contains(t, contentStr, "Conflicts: old-package")
assert.Contains(t, contentStr, "Replaces: legacy-package")
}
func TestDEBBuilder_CreateScripts(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "deb-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建DEBIAN目录
debianDir := filepath.Join(tempDir, "DEBIAN")
err = os.MkdirAll(debianDir, 0755)
require.NoError(t, err)
// 创建测试脚本源文件
scriptDir := filepath.Join(tempDir, "scripts")
err = os.MkdirAll(scriptDir, 0755)
require.NoError(t, err)
preinstScript := filepath.Join(scriptDir, "preinst.sh")
err = os.WriteFile(preinstScript, []byte("#!/bin/sh\necho 'Pre-install script'"), 0644)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Contents: config.ContentsConfig{
Scripts: []config.ScriptConfig{
{
Type: "preinstall",
Content: "#!/bin/sh\necho 'Pre-install script'",
},
{
Type: "postinstall",
Source: preinstScript,
},
{
Type: "preuninstall",
Content: "echo 'Pre-uninstall script'", // 没有shebang
},
{
Type: "invalid",
Content: "echo 'Invalid script'",
},
},
},
}
// 创建DEB构建器
builder := NewDEBBuilder(cfg, tempDir)
// 测试创建脚本文件
err = builder.createScripts(tempDir)
assert.NoError(t, err)
// 验证脚本文件是否存在
preinstFile := filepath.Join(tempDir, "DEBIAN", "preinst")
_, err = os.Stat(preinstFile)
assert.NoError(t, err)
postinstFile := filepath.Join(tempDir, "DEBIAN", "postinst")
_, err = os.Stat(postinstFile)
assert.NoError(t, err)
prermFile := filepath.Join(tempDir, "DEBIAN", "prerm")
_, err = os.Stat(prermFile)
assert.NoError(t, err)
// 读取脚本文件内容
content, err := os.ReadFile(preinstFile)
assert.NoError(t, err)
assert.Contains(t, string(content), "#!/bin/sh")
assert.Contains(t, string(content), "echo 'Pre-install script'")
content, err = os.ReadFile(prermFile)
assert.NoError(t, err)
assert.Contains(t, string(content), "#!/bin/sh")
assert.Contains(t, string(content), "echo 'Pre-uninstall script'")
}
func TestDEBBuilder_Build(t *testing.T) {
// 跳过实际构建测试因为它需要dpkg-deb工具
t.Skip("跳过实际构建测试因为它需要dpkg-deb工具")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "deb-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
Platforms: config.PlatformsConfig{
Linux: config.LinuxConfig{
Deb: config.LinuxDebConfig{
Section: "utils",
Priority: "optional",
Architecture: "amd64",
},
},
},
}
// 创建DEB构建器
builder := NewDEBBuilder(cfg, outputDir)
// 测试构建
err = builder.Build()
assert.NoError(t, err)
// 验证输出文件是否存在
outputFile := filepath.Join(outputDir, "TestApp_1.0.0_amd64.deb")
_, err = os.Stat(outputFile)
assert.NoError(t, err)
}
func TestDEBBuilder_BuildDeb(t *testing.T) {
// 跳过实际构建测试因为它需要dpkg-deb工具
t.Skip("跳过实际构建测试因为它需要dpkg-deb工具")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "deb-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
}
// 创建DEB构建器
builder := NewDEBBuilder(cfg, outputDir)
// 创建DEB包结构
err = builder.createDebStructure(tempDir)
assert.NoError(t, err)
// 创建控制文件
err = builder.createControlFile(tempDir)
assert.NoError(t, err)
// 测试构建DEB包
outputFile := filepath.Join(outputDir, "TestApp_1.0.0_amd64.deb")
err = builder.buildDeb(tempDir, outputFile)
assert.NoError(t, err)
// 验证输出文件是否存在
_, err = os.Stat(outputFile)
assert.NoError(t, err)
}

View File

@ -0,0 +1,306 @@
package linux
import (
"os"
"path/filepath"
"testing"
"git.kingecg.top/kingecg/installerbuilder/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRPMBuilder_Name(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewRPMBuilder(cfg, "output")
assert.Equal(t, "Linux RPM Builder", builder.Name())
}
func TestRPMBuilder_SupportedPlatforms(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewRPMBuilder(cfg, "output")
platforms := builder.SupportedPlatforms()
assert.Equal(t, []string{"linux"}, platforms)
}
func TestRPMBuilder_SupportedFormats(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewRPMBuilder(cfg, "output")
formats := builder.SupportedFormats()
assert.Equal(t, []string{"rpm"}, formats)
}
func TestRPMBuilder_CreateSourceTarball(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "rpm-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建源代码目录
sourceDir := filepath.Join(tempDir, "SOURCES", "TestApp-1.0.0")
err = os.MkdirAll(sourceDir, 0755)
require.NoError(t, err)
// 创建测试文件
testFile := filepath.Join(sourceDir, "test.txt")
err = os.WriteFile(testFile, []byte("test content"), 0644)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
}
// 创建RPM构建器
builder := NewRPMBuilder(cfg, tempDir)
// 测试创建源代码压缩包
tarFile := filepath.Join(tempDir, "SOURCES", "TestApp-1.0.0.tar.gz")
err = builder.createSourceTarball(sourceDir, tarFile)
// 由于createSourceTarball需要执行tar命令可能在测试环境中不可用所以我们跳过错误检查
if err != nil {
t.Skip("跳过tar命令测试可能在测试环境中不可用")
}
// 验证压缩包是否存在
_, err = os.Stat(tarFile)
assert.NoError(t, err)
}
func TestRPMBuilder_CreateSpecFile(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "rpm-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建SPECS目录
specsDir := filepath.Join(tempDir, "SPECS")
err = os.MkdirAll(specsDir, 0755)
require.NoError(t, err)
// 创建测试脚本源文件
scriptDir := filepath.Join(tempDir, "scripts")
err = os.MkdirAll(scriptDir, 0755)
require.NoError(t, err)
preinstScript := filepath.Join(scriptDir, "preinst.sh")
err = os.WriteFile(preinstScript, []byte("#!/bin/sh\necho 'Pre-install script'"), 0644)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Platforms: config.PlatformsConfig{
Linux: config.LinuxConfig{
Rpm: config.LinuxRpmConfig{
Group: "Applications/System",
URL: "https://example.com",
Requires: []string{"glibc", "libstdc++"},
Provides: []string{"testapp"},
Conflicts: []string{"old-package"},
Obsoletes: []string{"legacy-package"},
AutoReqProv: true,
},
},
},
Contents: config.ContentsConfig{
Scripts: []config.ScriptConfig{
{
Type: "preinstall",
Content: "#!/bin/sh\necho 'Pre-install script'",
},
{
Type: "postinstall",
Source: preinstScript,
},
{
Type: "preuninstall",
Content: "echo 'Pre-uninstall script'",
},
{
Type: "postuninstall",
Content: "echo 'Post-uninstall script'",
},
{
Type: "invalid",
Content: "echo 'Invalid script'",
},
},
},
}
// 创建RPM构建器
builder := NewRPMBuilder(cfg, tempDir)
// 测试创建SPEC文件
specFile := filepath.Join(specsDir, "TestApp.spec")
err = builder.createSpecFile(specFile)
assert.NoError(t, err)
// 验证SPEC文件是否存在
_, err = os.Stat(specFile)
assert.NoError(t, err)
// 读取SPEC文件内容
content, err := os.ReadFile(specFile)
assert.NoError(t, err)
// 验证文件内容
contentStr := string(content)
assert.Contains(t, contentStr, "Name: TestApp")
assert.Contains(t, contentStr, "Version: 1.0.0")
assert.Contains(t, contentStr, "Summary: Test Application")
assert.Contains(t, contentStr, "Group: Applications/System")
assert.Contains(t, contentStr, "License: MIT")
assert.Contains(t, contentStr, "URL: https://example.com")
assert.Contains(t, contentStr, "Requires: glibc, libstdc++")
assert.Contains(t, contentStr, "Provides: testapp")
assert.Contains(t, contentStr, "Conflicts: old-package")
assert.Contains(t, contentStr, "Obsoletes: legacy-package")
assert.Contains(t, contentStr, "%pre")
assert.Contains(t, contentStr, "%post")
assert.Contains(t, contentStr, "%preun")
assert.Contains(t, contentStr, "%postun")
}
func TestRPMBuilder_Build(t *testing.T) {
// 跳过实际构建测试因为它需要rpmbuild工具
t.Skip("跳过实际构建测试因为它需要rpmbuild工具")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "rpm-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Platforms: config.PlatformsConfig{
Linux: config.LinuxConfig{
Rpm: config.LinuxRpmConfig{
Group: "Applications/System",
URL: "https://example.com",
AutoReqProv: true,
},
},
},
}
// 创建RPM构建器
builder := NewRPMBuilder(cfg, outputDir)
// 测试构建
err = builder.Build()
assert.NoError(t, err)
// 验证输出文件是否存在
outputFile := filepath.Join(outputDir, "TestApp-1.0.0.x86_64.rpm")
_, err = os.Stat(outputFile)
assert.NoError(t, err)
}
func TestRPMBuilder_BuildRpm(t *testing.T) {
// 跳过实际构建测试因为它需要rpmbuild工具
t.Skip("跳过实际构建测试因为它需要rpmbuild工具")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "rpm-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建RPM构建目录结构
buildDirs := []string{
filepath.Join(tempDir, "BUILD"),
filepath.Join(tempDir, "RPMS"),
filepath.Join(tempDir, "SOURCES"),
filepath.Join(tempDir, "SPECS"),
filepath.Join(tempDir, "SRPMS"),
}
for _, dir := range buildDirs {
err = os.MkdirAll(dir, 0755)
require.NoError(t, err)
}
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
}
// 创建RPM构建器
builder := NewRPMBuilder(cfg, outputDir)
// 创建SPEC文件
specFile := filepath.Join(tempDir, "SPECS", "TestApp.spec")
err = builder.createSpecFile(specFile)
assert.NoError(t, err)
// 测试构建RPM包
outputFile := filepath.Join(outputDir, "TestApp-1.0.0.x86_64.rpm")
err = builder.buildRpm(tempDir, specFile, outputFile)
assert.NoError(t, err)
// 验证输出文件是否存在
_, err = os.Stat(outputFile)
assert.NoError(t, err)
}
func TestCopyFile(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "rpm-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建源文件
srcFile := filepath.Join(tempDir, "source.txt")
err = os.WriteFile(srcFile, []byte("test content"), 0644)
require.NoError(t, err)
// 创建目标文件路径
dstFile := filepath.Join(tempDir, "destination.txt")
// 测试复制文件
err = copyFile(srcFile, dstFile)
assert.NoError(t, err)
// 验证目标文件是否存在
_, err = os.Stat(dstFile)
assert.NoError(t, err)
// 读取目标文件内容
content, err := os.ReadFile(dstFile)
assert.NoError(t, err)
assert.Equal(t, "test content", string(content))
// 测试复制不存在的文件
err = copyFile(filepath.Join(tempDir, "nonexistent.txt"), dstFile)
assert.Error(t, err)
// 测试复制到无效路径
err = copyFile(srcFile, "/invalid/path/file.txt")
assert.Error(t, err)
}

View File

@ -0,0 +1,204 @@
package windows
import (
"os"
"path/filepath"
"testing"
"git.kingecg.top/kingecg/installerbuilder/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMSIBuilder_Name(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewMSIBuilder(cfg, "output")
assert.Equal(t, "Windows MSI Builder", builder.Name())
}
func TestMSIBuilder_SupportedPlatforms(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewMSIBuilder(cfg, "output")
platforms := builder.SupportedPlatforms()
assert.Equal(t, []string{"windows"}, platforms)
}
func TestMSIBuilder_SupportedFormats(t *testing.T) {
cfg := &config.Config{Name: "TestApp", Version: "1.0.0"}
builder := NewMSIBuilder(cfg, "output")
formats := builder.SupportedFormats()
assert.Equal(t, []string{"msi"}, formats)
}
func TestMSIBuilder_CreateWixSource(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "msi-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Platforms: config.PlatformsConfig{
Windows: config.WindowsConfig{
Msi: config.WindowsMsiConfig{
Manufacturer: "Test Manufacturer",
UpgradeCode: "12345678-1234-1234-1234-123456789012",
InstallDir: "TestApp",
},
},
},
}
// 创建MSI构建器
builder := NewMSIBuilder(cfg, tempDir)
// 测试创建WiX源文件
wixFile := filepath.Join(tempDir, "installer.wxs")
err = builder.createWixSource(wixFile)
assert.NoError(t, err)
// 验证文件是否存在
_, err = os.Stat(wixFile)
assert.NoError(t, err)
// 读取文件内容
content, err := os.ReadFile(wixFile)
assert.NoError(t, err)
// 验证文件内容
contentStr := string(content)
assert.Contains(t, contentStr, `Name="TestApp"`)
assert.Contains(t, contentStr, `Version="1.0.0"`)
assert.Contains(t, contentStr, `Manufacturer="Test Manufacturer"`)
assert.Contains(t, contentStr, `UpgradeCode="12345678-1234-1234-1234-123456789012"`)
assert.Contains(t, contentStr, `Description="Test Application"`)
assert.Contains(t, contentStr, `Name="TestApp"`)
}
func TestMSIBuilder_Build(t *testing.T) {
// 跳过实际构建测试因为它需要WiX工具集
t.Skip("跳过实际构建测试因为它需要WiX工具集")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "msi-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Platforms: config.PlatformsConfig{
Windows: config.WindowsConfig{
Msi: config.WindowsMsiConfig{
Manufacturer: "Test Manufacturer",
UpgradeCode: "12345678-1234-1234-1234-123456789012",
InstallDir: "TestApp",
},
},
},
}
// 创建MSI构建器
builder := NewMSIBuilder(cfg, outputDir)
// 测试构建
err = builder.Build()
assert.NoError(t, err)
// 验证输出文件是否存在
outputFile := filepath.Join(outputDir, "TestApp-1.0.0.msi")
_, err = os.Stat(outputFile)
assert.NoError(t, err)
}
func TestMSIBuilder_CompileWixSource(t *testing.T) {
// 跳过实际编译测试因为它需要WiX工具集
t.Skip("跳过实际编译测试因为它需要WiX工具集")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "msi-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
}
// 创建MSI构建器
builder := NewMSIBuilder(cfg, tempDir)
// 创建WiX源文件
wixFile := filepath.Join(tempDir, "installer.wxs")
err = builder.createWixSource(wixFile)
assert.NoError(t, err)
// 测试编译WiX源文件
objFile := filepath.Join(tempDir, "installer.wixobj")
err = builder.compileWixSource(wixFile, objFile)
assert.NoError(t, err)
// 验证输出文件是否存在
_, err = os.Stat(objFile)
assert.NoError(t, err)
}
func TestMSIBuilder_LinkMsi(t *testing.T) {
// 跳过实际链接测试因为它需要WiX工具集
t.Skip("跳过实际链接测试因为它需要WiX工具集")
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "msi-builder-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建输出目录
outputDir := filepath.Join(tempDir, "output")
err = os.MkdirAll(outputDir, 0755)
require.NoError(t, err)
// 创建测试配置
cfg := &config.Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
}
// 创建MSI构建器
builder := NewMSIBuilder(cfg, outputDir)
// 创建WiX源文件
wixFile := filepath.Join(tempDir, "installer.wxs")
err = builder.createWixSource(wixFile)
assert.NoError(t, err)
// 编译WiX源文件
objFile := filepath.Join(tempDir, "installer.wixobj")
err = builder.compileWixSource(wixFile, objFile)
assert.NoError(t, err)
// 测试链接生成MSI
msiFile := filepath.Join(outputDir, "TestApp-1.0.0.msi")
err = builder.linkMsi(objFile, msiFile)
assert.NoError(t, err)
// 验证输出文件是否存在
_, err = os.Stat(msiFile)
assert.NoError(t, err)
}

View File

@ -0,0 +1,254 @@
package config
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLoadConfig(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "config-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建有效的YAML配置文件
validYAML := `
name: TestApp
version: 1.0.0
description: Test Application
author: Test Author
license: MIT
build:
outputDir: dist
targets:
- windows
- linux
formats:
- msi
- deb
contents:
files:
- source: test.txt
destination: test.txt
mode: "0644"
scripts:
- type: preinstall
content: "echo 'Pre-install script'"
- type: postinstall
source: scripts/post-install.sh
`
validYAMLPath := filepath.Join(tempDir, "valid.yaml")
err = os.WriteFile(validYAMLPath, []byte(validYAML), 0644)
require.NoError(t, err)
// 创建无效的YAML配置文件
invalidYAML := `
name: TestApp
version: 1.0.0
invalid_field: value
`
invalidYAMLPath := filepath.Join(tempDir, "invalid.yaml")
err = os.WriteFile(invalidYAMLPath, []byte(invalidYAML), 0644)
require.NoError(t, err)
// 创建有效的JSON配置文件
validJSON := `{
"name": "TestApp",
"version": "1.0.0",
"description": "Test Application",
"author": "Test Author",
"license": "MIT",
"build": {
"outputDir": "dist",
"targets": ["windows", "linux"],
"formats": ["msi", "deb"]
},
"contents": {
"files": [
{
"source": "test.txt",
"destination": "test.txt",
"mode": "0644"
}
],
"scripts": [
{
"type": "preinstall",
"content": "echo 'Pre-install script'"
},
{
"type": "postinstall",
"source": "scripts/post-install.sh"
}
]
}
}`
validJSONPath := filepath.Join(tempDir, "valid.json")
err = os.WriteFile(validJSONPath, []byte(validJSON), 0644)
require.NoError(t, err)
// 测试用例
tests := []struct {
name string
path string
wantErr bool
checkCfg func(*Config)
}{
{
name: "Valid YAML config",
path: validYAMLPath,
wantErr: false,
checkCfg: func(cfg *Config) {
assert.Equal(t, "TestApp", cfg.Name)
assert.Equal(t, "1.0.0", cfg.Version)
assert.Equal(t, "Test Application", cfg.Description)
assert.Equal(t, "Test Author", cfg.Author)
assert.Equal(t, "MIT", cfg.License)
assert.Equal(t, "dist", cfg.Build.OutputDir)
assert.Contains(t, cfg.Build.Targets, "windows")
assert.Contains(t, cfg.Build.Targets, "linux")
assert.Contains(t, cfg.Build.Formats, "msi")
assert.Contains(t, cfg.Build.Formats, "deb")
assert.Len(t, cfg.Contents.Files, 1)
assert.Equal(t, "test.txt", cfg.Contents.Files[0].Source)
assert.Equal(t, "test.txt", cfg.Contents.Files[0].Destination)
assert.Equal(t, "0644", cfg.Contents.Files[0].Mode)
assert.Len(t, cfg.Contents.Scripts, 2)
assert.Equal(t, "preinstall", cfg.Contents.Scripts[0].Type)
assert.Equal(t, "echo 'Pre-install script'", cfg.Contents.Scripts[0].Content)
assert.Equal(t, "postinstall", cfg.Contents.Scripts[1].Type)
assert.Equal(t, "scripts/post-install.sh", cfg.Contents.Scripts[1].Source)
},
},
{
name: "Valid JSON config",
path: validJSONPath,
wantErr: false,
checkCfg: func(cfg *Config) {
assert.Equal(t, "TestApp", cfg.Name)
assert.Equal(t, "1.0.0", cfg.Version)
assert.Equal(t, "Test Application", cfg.Description)
assert.Equal(t, "Test Author", cfg.Author)
assert.Equal(t, "MIT", cfg.License)
},
},
{
name: "Invalid YAML config",
path: invalidYAMLPath,
wantErr: false, // 不会报错,但会有警告
checkCfg: func(cfg *Config) {
assert.Equal(t, "TestApp", cfg.Name)
assert.Equal(t, "1.0.0", cfg.Version)
},
},
{
name: "Non-existent file",
path: filepath.Join(tempDir, "nonexistent.yaml"),
wantErr: true,
checkCfg: func(cfg *Config) {
assert.Nil(t, cfg)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg, err := LoadConfig(tt.path)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
tt.checkCfg(cfg)
}
})
}
}
func TestSaveConfig(t *testing.T) {
// 创建临时测试目录
tempDir, err := os.MkdirTemp("", "config-test-*")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
// 创建测试配置
cfg := &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "deb"},
},
Contents: ContentsConfig{
Files: []FileConfig{
{
Source: "test.txt",
Destination: "test.txt",
Mode: "0644",
},
},
Scripts: []ScriptConfig{
{
Type: "preinstall",
Content: "echo 'Pre-install script'",
},
},
},
}
// 测试YAML保存
yamlPath := filepath.Join(tempDir, "config.yaml")
err = SaveConfig(cfg, yamlPath)
assert.NoError(t, err)
// 验证保存的YAML文件
loadedCfg, err := LoadConfig(yamlPath)
assert.NoError(t, err)
assert.Equal(t, cfg.Name, loadedCfg.Name)
assert.Equal(t, cfg.Version, loadedCfg.Version)
assert.Equal(t, cfg.Description, loadedCfg.Description)
assert.Equal(t, cfg.Build.OutputDir, loadedCfg.Build.OutputDir)
assert.ElementsMatch(t, cfg.Build.Targets, loadedCfg.Build.Targets)
assert.ElementsMatch(t, cfg.Build.Formats, loadedCfg.Build.Formats)
assert.Len(t, loadedCfg.Contents.Files, 1)
assert.Equal(t, cfg.Contents.Files[0].Source, loadedCfg.Contents.Files[0].Source)
assert.Equal(t, cfg.Contents.Files[0].Destination, loadedCfg.Contents.Files[0].Destination)
assert.Equal(t, cfg.Contents.Files[0].Mode, loadedCfg.Contents.Files[0].Mode)
assert.Len(t, loadedCfg.Contents.Scripts, 1)
assert.Equal(t, cfg.Contents.Scripts[0].Type, loadedCfg.Contents.Scripts[0].Type)
assert.Equal(t, cfg.Contents.Scripts[0].Content, loadedCfg.Contents.Scripts[0].Content)
// 测试JSON保存
jsonPath := filepath.Join(tempDir, "config.json")
err = SaveConfig(cfg, jsonPath)
assert.NoError(t, err)
// 验证保存的JSON文件
loadedCfg, err = LoadConfig(jsonPath)
assert.NoError(t, err)
assert.Equal(t, cfg.Name, loadedCfg.Name)
assert.Equal(t, cfg.Version, loadedCfg.Version)
assert.Equal(t, cfg.Description, loadedCfg.Description)
// 测试无效路径
err = SaveConfig(cfg, "/invalid/path/config.yaml")
assert.Error(t, err)
}
func TestDefaultConfig(t *testing.T) {
cfg := DefaultConfig()
assert.NotEmpty(t, cfg.Name)
assert.NotEmpty(t, cfg.Version)
assert.NotEmpty(t, cfg.Build.OutputDir)
assert.NotEmpty(t, cfg.Build.Targets)
assert.NotEmpty(t, cfg.Build.Formats)
}

View File

@ -0,0 +1,231 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestValidate(t *testing.T) {
tests := []struct {
name string
config *Config
strict bool
wantValid bool
wantErrors int
}{
{
name: "Valid config",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "deb"},
},
Contents: ContentsConfig{
Files: []FileConfig{
{
Source: "test.txt",
Destination: "test.txt",
Mode: "0644",
},
},
},
},
strict: true,
wantValid: true,
wantErrors: 0,
},
{
name: "Missing name",
config: &Config{
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "deb"},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
{
name: "Missing version",
config: &Config{
Name: "TestApp",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "deb"},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
{
name: "Invalid target",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "invalid"},
Formats: []string{"msi", "deb"},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
{
name: "Invalid format",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows", "linux"},
Formats: []string{"msi", "invalid"},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
{
name: "Missing optional fields in non-strict mode",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Build: BuildConfig{
Targets: []string{"windows"},
Formats: []string{"msi"},
},
},
strict: false,
wantValid: true,
wantErrors: 0,
},
{
name: "Missing optional fields in strict mode",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Build: BuildConfig{
Targets: []string{"windows"},
Formats: []string{"msi"},
},
},
strict: true,
wantValid: false,
wantErrors: 3, // 缺少描述、作者和许可证
},
{
name: "Invalid script type",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows"},
Formats: []string{"msi"},
},
Contents: ContentsConfig{
Scripts: []ScriptConfig{
{
Type: "invalid",
Content: "echo 'Invalid script'",
},
},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
{
name: "Empty script",
config: &Config{
Name: "TestApp",
Version: "1.0.0",
Description: "Test Application",
Author: "Test Author",
License: "MIT",
Build: BuildConfig{
OutputDir: "dist",
Targets: []string{"windows"},
Formats: []string{"msi"},
},
Contents: ContentsConfig{
Scripts: []ScriptConfig{
{
Type: "preinstall",
},
},
},
},
strict: true,
wantValid: false,
wantErrors: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.config.Validate(tt.strict)
assert.Equal(t, tt.wantValid, result.Valid)
assert.Equal(t, tt.wantErrors, len(result.Errors))
})
}
}
func TestValidationResult_String(t *testing.T) {
// 测试空错误列表
result := ValidationResult{
Valid: true,
Errors: []ValidationError{},
}
assert.Equal(t, "配置有效", result.String())
// 测试单个错误
result = ValidationResult{
Valid: false,
Errors: []ValidationError{
{Field: "test", Message: "错误1"},
},
}
assert.Equal(t, "配置无效: 错误1", result.String())
// 测试多个错误
result = ValidationResult{
Valid: false,
Errors: []ValidationError{
{Message: "错误1"},
{Message: "错误2"},
{Message: "错误3"},
},
}
assert.Equal(t, "配置无效: 错误1, 错误2, 错误3", result.String())
}