package projectversion import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParse(t *testing.T) { t.Run("valid", func(t *testing.T) { version, err := Parse("1.2.3") require.NoError(t, err) assert.Equal(t, Version{Major: 1, Minor: 2, Patch: 3}, version) assert.Equal(t, "1.2.3", version.String()) }) t.Run("invalid", func(t *testing.T) { invalidValues := []string{"", "1.2", "1.2.3.4", "v1.2.3", "01.2.3", "1.02.3"} for _, tc := range invalidValues { _, err := Parse(tc) assert.Error(t, err, "%q 应校验失败", tc) } }) } func TestUpdatePackageJSONVersion(t *testing.T) { content := "{\n \"name\": \"frontend\",\n \"version\": \"0.0.0\"\n}\n" updated, err := UpdatePackageJSONVersion(content, "1.2.3") require.NoError(t, err) assert.Contains(t, updated, `"version": "1.2.3"`) version, err := ReadPackageJSONVersion(updated) require.NoError(t, err) assert.Equal(t, "1.2.3", version) } func TestUpsertEnvVar(t *testing.T) { updated := UpsertEnvVar("VITE_API_BASE=/api\n", "VITE_APP_VERSION", "1.2.3") assert.Contains(t, updated, "VITE_API_BASE=/api\n") assert.Contains(t, updated, "VITE_APP_VERSION=1.2.3\n") updated = UpsertEnvVar(updated, "VITE_APP_VERSION", "2.0.0") value, ok := ReadEnvVar(updated, "VITE_APP_VERSION") assert.True(t, ok) assert.Equal(t, "2.0.0", value) assert.Equal(t, 1, strings.Count(updated, "VITE_APP_VERSION=")) } func TestSyncAndCheck(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "VERSION"), []byte("1.2.3\n"), 0o600)) require.NoError(t, os.MkdirAll(filepath.Join(root, "frontend"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", "package.json"), []byte("{\n \"name\": \"frontend\",\n \"version\": \"0.0.0\"\n}\n"), 0o600)) require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.production"), []byte("VITE_API_BASE=/api\n"), 0o600)) require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.development"), []byte("VITE_API_BASE=\n"), 0o600)) require.NoError(t, os.WriteFile(filepath.Join(root, "frontend", ".env.desktop"), []byte("VITE_API_BASE=\n"), 0o600)) require.NoError(t, Sync(root)) require.NoError(t, Check(root)) packageJSONContent, err := os.ReadFile(filepath.Join(root, "frontend", "package.json")) require.NoError(t, err) assert.Contains(t, string(packageJSONContent), `"version": "1.2.3"`) for _, relPath := range frontendVersionFiles { content, readErr := os.ReadFile(filepath.Join(root, relPath)) require.NoError(t, readErr) assert.Contains(t, string(content), "VITE_APP_VERSION=1.2.3\n") } } func TestVerifyTag(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "VERSION"), []byte("1.2.3\n"), 0o600)) require.NoError(t, VerifyTag(root, "v1.2.3")) assert.Error(t, VerifyTag(root, "1.2.3")) assert.Error(t, VerifyTag(root, "v1.2.4")) } func TestAssetNames(t *testing.T) { testCases := []struct { name string component string platform string arch string format string want string }{ {"server linux amd64", "server", "linux", "amd64", "tar.gz", "nex-server_1.2.3_linux_amd64.tar.gz"}, {"server linux arm64", "server", "linux", "arm64", "tar.gz", "nex-server_1.2.3_linux_arm64.tar.gz"}, {"server macos amd64", "server", "macos", "amd64", "tar.gz", "nex-server_1.2.3_macos_amd64.tar.gz"}, {"server macos arm64", "server", "macos", "arm64", "tar.gz", "nex-server_1.2.3_macos_arm64.tar.gz"}, {"server macos universal", "server", "macos", "universal", "tar.gz", "nex-server_1.2.3_macos_universal.tar.gz"}, {"server windows amd64", "server", "windows", "amd64", "zip", "nex-server_1.2.3_windows_amd64.zip"}, {"web", "web", "", "", "tar.gz", "nex-web_1.2.3.tar.gz"}, {"desktop linux amd64 tar", "desktop", "linux", "amd64", "tar.gz", "nex-desktop_1.2.3_linux_amd64.tar.gz"}, {"desktop linux amd64 appimage", "desktop", "linux", "amd64", "AppImage", "nex-desktop_1.2.3_linux_amd64.AppImage"}, {"desktop linux amd64 deb", "desktop", "linux", "amd64", "deb", "nex-desktop_1.2.3_linux_amd64.deb"}, {"desktop linux amd64 rpm", "desktop", "linux", "amd64", "rpm", "nex-desktop_1.2.3_linux_amd64.rpm"}, {"desktop linux arm64 tar", "desktop", "linux", "arm64", "tar.gz", "nex-desktop_1.2.3_linux_arm64.tar.gz"}, {"desktop linux arm64 appimage", "desktop", "linux", "arm64", "AppImage", "nex-desktop_1.2.3_linux_arm64.AppImage"}, {"desktop linux arm64 deb", "desktop", "linux", "arm64", "deb", "nex-desktop_1.2.3_linux_arm64.deb"}, {"desktop linux arm64 rpm", "desktop", "linux", "arm64", "rpm", "nex-desktop_1.2.3_linux_arm64.rpm"}, {"desktop macos zip", "desktop", "macos", "universal", "zip", "nex-desktop_1.2.3_macos_universal.zip"}, {"desktop macos dmg", "desktop", "macos", "universal", "dmg", "nex-desktop_1.2.3_macos_universal.dmg"}, {"desktop windows amd64", "desktop", "windows", "amd64", "zip", "nex-desktop_1.2.3_windows_amd64.zip"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := ReleaseAssetName("1.2.3", tc.component, tc.platform, tc.arch, tc.format) require.NoError(t, err) assert.Equal(t, tc.want, got) }) } invalidCases := []struct { name string component string platform string arch string format string }{ {"invalid version", "server", "linux", "amd64", "tar.gz"}, {"invalid component", "mobile", "linux", "amd64", "tar.gz"}, {"darwin platform", "server", "darwin", "arm64", "tar.gz"}, {"server unsupported format", "server", "linux", "amd64", "zip"}, {"server unsupported arch", "server", "windows", "universal", "zip"}, {"web with platform", "web", "linux", "amd64", "tar.gz"}, {"web unsupported format", "web", "", "", "zip"}, {"desktop unsupported platform", "desktop", "ios", "arm64", "zip"}, {"desktop unsupported format", "desktop", "macos", "universal", "tar.gz"}, } for _, tc := range invalidCases { t.Run(tc.name, func(t *testing.T) { version := "1.2.3" if tc.name == "invalid version" { version = "1.2" } _, err := ReleaseAssetName(version, tc.component, tc.platform, tc.arch, tc.format) assert.Error(t, err) }) } } func TestDesktopInfoPlist(t *testing.T) { plist, err := DesktopInfoPlist("1.2.3", "13.0") require.NoError(t, err) assert.Contains(t, plist, "CFBundleShortVersionString\n 1.2.3") assert.Contains(t, plist, "CFBundleVersion\n 1.2.3") assert.Contains(t, plist, "LSMinimumSystemVersion\n 13.0") _, err = DesktopInfoPlist("1.2", "13.0") assert.Error(t, err) _, err = DesktopInfoPlist("1.2.3", "") assert.Error(t, err) } func TestLess(t *testing.T) { assert.True(t, Version{1, 0, 0}.Less(Version{2, 0, 0})) assert.True(t, Version{1, 1, 0}.Less(Version{1, 2, 0})) assert.True(t, Version{1, 0, 1}.Less(Version{1, 0, 2})) assert.False(t, Version{2, 0, 0}.Less(Version{1, 0, 0})) assert.False(t, Version{1, 0, 0}.Less(Version{1, 0, 0})) } func TestBump(t *testing.T) { setupRoot := func(t *testing.T) string { t.Helper() root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "VERSION"), []byte("0.1.0\n"), 0o600)) return root } t.Run("major", func(t *testing.T) { root := setupRoot(t) v, err := Bump(root, "major") require.NoError(t, err) assert.Equal(t, Version{1, 0, 0}, v) read, readErr := ReadString(root) require.NoError(t, readErr) assert.Equal(t, "1.0.0", read) }) t.Run("minor", func(t *testing.T) { root := setupRoot(t) v, err := Bump(root, "minor") require.NoError(t, err) assert.Equal(t, Version{0, 2, 0}, v) read, readErr := ReadString(root) require.NoError(t, readErr) assert.Equal(t, "0.2.0", read) }) t.Run("patch", func(t *testing.T) { root := setupRoot(t) v, err := Bump(root, "patch") require.NoError(t, err) assert.Equal(t, Version{0, 1, 1}, v) read, readErr := ReadString(root) require.NoError(t, readErr) assert.Equal(t, "0.1.1", read) }) t.Run("specific version", func(t *testing.T) { root := setupRoot(t) v, err := Bump(root, "1.0.0") require.NoError(t, err) assert.Equal(t, Version{1, 0, 0}, v) }) t.Run("same version as current", func(t *testing.T) { root := setupRoot(t) v, err := Bump(root, "0.1.0") require.NoError(t, err) assert.Equal(t, Version{0, 1, 0}, v) }) t.Run("invalid argument", func(t *testing.T) { root := setupRoot(t) _, err := Bump(root, "invalid") assert.Error(t, err) }) } func TestCheckNoRegression(t *testing.T) { t.Run("greater than existing tag", func(t *testing.T) { err := CheckNoRegression(Version{0, 2, 0}, []string{"v0.1.0"}) assert.NoError(t, err) }) t.Run("equal to existing tag", func(t *testing.T) { err := CheckNoRegression(Version{0, 1, 0}, []string{"v0.1.0"}) assert.Error(t, err) }) t.Run("less than existing tag", func(t *testing.T) { err := CheckNoRegression(Version{0, 1, 5}, []string{"v0.2.0"}) assert.Error(t, err) }) t.Run("no tags", func(t *testing.T) { err := CheckNoRegression(Version{0, 1, 0}, nil) assert.NoError(t, err) }) t.Run("skips non-semver tags", func(t *testing.T) { err := CheckNoRegression(Version{0, 2, 0}, []string{"v0.1.0", "some-other-tag"}) assert.NoError(t, err) }) t.Run("picks max tag", func(t *testing.T) { err := CheckNoRegression(Version{0, 1, 5}, []string{"v0.1.0", "v0.2.0", "v0.0.5"}) assert.Error(t, err) }) }