package app import ( "context" "errors" "testing" "time" "github.com/geo-platform/tenant-api/internal/ops/domain" "github.com/stretchr/testify/require" ) func TestNormalizeDesktopClientReleaseInputOSS(t *testing.T) { t.Parallel() customURL := "https://download.example.com/client.dmg" ossKey := "desktop/stable/client.dmg" got, err := normalizeDesktopClientReleaseInput(DesktopClientReleaseInput{ Platform: " Darwin ", Arch: "", Channel: "", Version: "1.2.3", DownloadSource: "oss", OSSObjectKey: &ossKey, CustomDownloadURL: &customURL, Enabled: true, }) require.NoError(t, err) require.Equal(t, "darwin", got.Platform) require.Equal(t, "universal", got.Arch) require.Equal(t, "stable", got.Channel) require.Equal(t, DesktopClientDownloadSourceOSS, got.DownloadSource) require.NotNil(t, got.OSSObjectKey) require.Equal(t, "desktop/stable/client.dmg", *got.OSSObjectKey) require.Nil(t, got.CustomDownloadURL) } func TestNormalizeDesktopClientReleaseInputCustom(t *testing.T) { t.Parallel() customURL := "https://download.example.com/client.exe" ossKey := "desktop/stable/client.exe" got, err := normalizeDesktopClientReleaseInput(DesktopClientReleaseInput{ Platform: "win32", Arch: "x64", Channel: "stable", Version: "1.2.3", DownloadSource: "custom", OSSObjectKey: &ossKey, CustomDownloadURL: &customURL, Enabled: true, }) require.NoError(t, err) require.Equal(t, DesktopClientDownloadSourceCustom, got.DownloadSource) require.Nil(t, got.OSSObjectKey) require.NotNil(t, got.CustomDownloadURL) require.Equal(t, customURL, *got.CustomDownloadURL) } func TestNormalizeDesktopClientReleaseInputRejectsInvalidDownloadTargets(t *testing.T) { t.Parallel() invalidOSSKey := "../client.dmg" _, err := normalizeDesktopClientReleaseInput(DesktopClientReleaseInput{ Platform: "darwin", Version: "1.2.3", DownloadSource: "oss", OSSObjectKey: &invalidOSSKey, }) require.Error(t, err) invalidURL := "javascript:alert(1)" _, err = normalizeDesktopClientReleaseInput(DesktopClientReleaseInput{ Platform: "win32", Version: "1.2.3", DownloadSource: "custom", CustomDownloadURL: &invalidURL, }) require.Error(t, err) } func TestDesktopClientReleaseVersionPolicy(t *testing.T) { t.Parallel() require.Positive(t, compareVersionStrings("1.2.10", "1.2.9")) require.Negative(t, compareVersionStrings("1.2.9", "1.2.10")) minVersion := "1.2.0" require.True(t, minSupportedVersionRequiresUpdate(&minVersion, "1.1.9")) require.False(t, minSupportedVersionRequiresUpdate(&minVersion, "1.2.0")) } func TestNormalizeDesktopClientReleaseUploadInput(t *testing.T) { t.Parallel() target, fileName, err := normalizeDesktopClientReleaseUploadInput(DesktopClientReleaseUploadInput{ Platform: "darwin", Arch: "arm64", Channel: "stable", Version: "1.2.3", FileName: "省心推-1.2.3-arm64.dmg", Content: []byte("package"), }) require.NoError(t, err) require.Equal(t, "darwin", target.Platform) require.Equal(t, "arm64", target.Arch) require.Equal(t, "stable", target.Channel) require.Equal(t, "省心推-1.2.3-arm64.dmg", fileName) } func TestNormalizeDesktopClientReleaseUploadInputDecodesFileName(t *testing.T) { t.Parallel() _, fileName, err := normalizeDesktopClientReleaseUploadInput(DesktopClientReleaseUploadInput{ Platform: "darwin", Arch: "arm64", Channel: "stable", Version: "1.2.3", FileName: "%E7%9C%81%E5%BF%83%E6%8E%A8-mac-0.1.19.zip", Content: []byte("package"), }) require.NoError(t, err) require.Equal(t, "省心推-mac-0.1.19.zip", fileName) } func TestNormalizeDesktopClientReleaseUploadInputRejectsInvalidFile(t *testing.T) { t.Parallel() _, _, err := normalizeDesktopClientReleaseUploadInput(DesktopClientReleaseUploadInput{ Platform: "darwin", Arch: "arm64", Channel: "stable", Version: "1.2.3", FileName: "client.txt", Content: []byte("package"), }) require.Error(t, err) } func TestBuildDesktopClientUpdaterFeedYAMLUsesSignedURL(t *testing.T) { t.Parallel() fileName := "省心推-mac-1.2.3.zip" sha256 := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" size := int64(1024) publishedAt := time.Date(2026, 5, 25, 10, 30, 0, 0, time.UTC) content, err := buildDesktopClientUpdaterFeedYAML(&domain.DesktopClientRelease{ Platform: "darwin", Version: "1.2.3", PublishedAt: &publishedAt, UpdatedAt: publishedAt, }, desktopClientReleaseAsset{ FileName: &fileName, FileSizeBytes: &size, SHA256: &sha256, }, "https://oss.example.com/desktop/%E7%9C%81%E5%BF%83%E6%8E%A8.zip?Expires=123&Signature=abc") require.NoError(t, err) text := string(content) require.Contains(t, text, "version: 1.2.3") require.Contains(t, text, "sha2: "+sha256) require.Contains(t, text, "size: 1024") require.Contains(t, text, "Expires=123") require.Contains(t, text, "Signature=abc") require.NotContains(t, text, "%25E7") } func TestValidateDesktopClientUpdaterPackage(t *testing.T) { t.Parallel() zipName := "省心推-mac-1.2.3.zip" require.NoError(t, validateDesktopClientUpdaterPackage("darwin", &zipName, "")) dmgName := "省心推-mac-1.2.3.dmg" require.Error(t, validateDesktopClientUpdaterPackage("darwin", &dmgName, "")) exeURL := "https://download.example.com/%E7%9C%81%E5%BF%83%E6%8E%A8%20Setup%201.2.3.exe" require.NoError(t, validateDesktopClientUpdaterPackage("win32", nil, exeURL)) } func TestBuildDesktopClientReleaseObjectKeyReusesContentAddress(t *testing.T) { t.Parallel() sum := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" first := buildDesktopClientReleaseObjectKey(sum, "省心推 Setup 1.2.3.exe") second := buildDesktopClientReleaseObjectKey(sum, "renamed.exe") require.Equal(t, first, second) require.Equal(t, "desktop-client/releases/blobs/"+sum+".exe", first) } func TestResolveDownloadURLResultUsesSignedDownloadURL(t *testing.T) { t.Parallel() objectKey := "desktop-client/releases/blobs/client.zip" storage := &desktopReleaseDownloadURLStorage{} svc := &DesktopClientReleaseService{storage: storage} result, err := svc.resolveDownloadURLResult(&domain.DesktopClientRelease{ DownloadSource: DesktopClientDownloadSourceOSS, OSSObjectKey: &objectKey, }) require.NoError(t, err) require.Equal(t, "https://oss.example.test/signed/client.zip?Expires=123", result.DownloadURL) require.Equal(t, []string{objectKey}, storage.downloadURLCalls) require.Empty(t, storage.publicURLCalls) } type desktopReleaseDownloadURLStorage struct { publicURLCalls []string downloadURLCalls []string } func (s *desktopReleaseDownloadURLStorage) Validate() error { return nil } func (s *desktopReleaseDownloadURLStorage) PutBytes(context.Context, string, []byte, string) error { return nil } func (s *desktopReleaseDownloadURLStorage) GetBytes(context.Context, string) ([]byte, error) { return nil, errors.New("not implemented") } func (s *desktopReleaseDownloadURLStorage) Exists(context.Context, string) (bool, error) { return true, nil } func (s *desktopReleaseDownloadURLStorage) Delete(context.Context, string) error { return nil } func (s *desktopReleaseDownloadURLStorage) PublicURL(objectKey string) (string, error) { s.publicURLCalls = append(s.publicURLCalls, objectKey) return "", errors.New("public url should not be used for private release downloads") } func (s *desktopReleaseDownloadURLStorage) DownloadURL(objectKey string) (string, error) { s.downloadURLCalls = append(s.downloadURLCalls, objectKey) return "https://oss.example.test/signed/client.zip?Expires=123", nil }