diff --git a/frontend/src/components/DriverManagerModal.tsx b/frontend/src/components/DriverManagerModal.tsx index df5b0cd..6dc3df9 100644 --- a/frontend/src/components/DriverManagerModal.tsx +++ b/frontend/src/components/DriverManagerModal.tsx @@ -1067,29 +1067,35 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG const options = versionMap[row.type] || []; const selectedKey = selectedVersionMap[row.type]; const selectOptions = buildVersionSelectOptions(options); + const mongoHint = row.type === 'mongodb' + ? '当前仅支持 MongoDB 1.17.x 和 2.x;更老 1.x 暂不提供安装。' + : ''; return ( - 0 ? '选择驱动版本' : '点击展开加载版本'} + value={selectedKey} + options={selectOptions as any} + onOpenChange={(open) => { + if (open && options.length === 0 && !versionLoadingMap[row.type]) { + void loadVersionOptions(row, true); + return; + } + if (open && selectedKey) { + void loadVersionPackageSize(row, selectedKey); + } + }} + onChange={(value) => { + setSelectedVersionMap((prev) => ({ ...prev, [row.type]: value })); + void loadVersionPackageSize(row, value); + }} + /> + {mongoHint ? {mongoHint} : null} + ); }, }, diff --git a/internal/app/methods_driver.go b/internal/app/methods_driver.go index 1024014..e8873f7 100644 --- a/internal/app/methods_driver.go +++ b/internal/app/methods_driver.go @@ -543,7 +543,10 @@ func (a *App) GetDriverVersionPackageSize(driverType string, version string) con if normalizedVersion == "" { return connection.QueryResult{Success: false, Message: "版本号为空"} } - assetName := optionalDriverReleaseAssetName(normalizedType) + if err := validateDriverSelectedVersion(definition, normalizedVersion); err != nil { + return connection.QueryResult{Success: false, Message: err.Error()} + } + assetName := optionalDriverReleaseAssetNameForVersion(normalizedType, normalizedVersion) if strings.TrimSpace(assetName) == "" { return connection.QueryResult{Success: false, Message: "驱动资产名称为空"} } @@ -554,14 +557,15 @@ func (a *App) GetDriverVersionPackageSize(driverType string, version string) con if sizeByAsset, err := loadReleaseAssetSizesCached("tag:"+tag, func() (*githubRelease, error) { return fetchReleaseByTag(tag) }); err == nil { - sizeBytes = resolveOptionalDriverAssetSize(sizeByAsset, normalizedType) + sizeBytes = resolveOptionalDriverAssetSizeForVersion(sizeByAsset, normalizedType, normalizedVersion) if sizeBytes > 0 { sizeSource = "tag" } } - if sizeBytes <= 0 { + allowLatestFallback := sameDriverVersion(normalizedVersion, definition.PinnedVersion) || sameDriverVersion(normalizedVersion, latestDriverVersionMap[normalizedType]) + if sizeBytes <= 0 && allowLatestFallback { if sizeByAsset, err := loadReleaseAssetSizesCached("latest", fetchLatestReleaseForDriverAssets); err == nil { - sizeBytes = resolveOptionalDriverAssetSize(sizeByAsset, normalizedType) + sizeBytes = resolveOptionalDriverAssetSizeForVersion(sizeByAsset, normalizedType, normalizedVersion) if sizeBytes > 0 { sizeSource = "latest" } @@ -816,6 +820,9 @@ func (a *App) DownloadDriverPackage(driverType string, version string, downloadU urlText = fmt.Sprintf("builtin://activate/%s", optionalDriverPublicTypeName(definition.Type)) } selectedVersion := resolveDriverInstallVersion(version, urlText, definition) + if err := validateDriverSelectedVersion(definition, selectedVersion); err != nil { + return connection.QueryResult{Success: false, Message: err.Error()} + } resolvedDir, err := resolveDriverDownloadDirectory(downloadDir) if err != nil { @@ -1424,6 +1431,11 @@ func resolveDriverVersionOptions(definition driverDefinition, repositoryURL stri if versionText == "" && urlText == "" { return } + if versionText != "" { + if err := validateDriverSelectedVersion(definition, versionText); err != nil { + return + } + } versionKey := normalizeVersion(versionText) key := "" if versionKey != "" { @@ -1550,6 +1562,16 @@ func resolveVersionedDriverOption(definition driverDefinition, version string, s if versionText == "" { return "", "", false } + if err := validateDriverSelectedVersion(definition, versionText); err != nil { + return "", "", false + } + + if publishedURL, ok := resolvePublishedDriverDownloadURL(definition, versionText); ok { + return versionText, publishedURL, true + } + if !optionalDriverSourceBuildAvailable(definition, versionText) { + return "", "", false + } urlText := strings.TrimSpace(definition.DefaultDownloadURL) if urlText == "" && effectiveDriverEngine(definition) == driverEngineGo { @@ -1580,6 +1602,97 @@ func sameDriverVersion(left, right string) bool { return a != "" && a == b } +func validateDriverSelectedVersion(definition driverDefinition, version string) error { + driverType := normalizeDriverType(definition.Type) + versionText := normalizeVersion(strings.TrimSpace(version)) + if driverType == "" || versionText == "" { + return nil + } + + switch driverType { + case "mongodb": + if strings.HasPrefix(versionText, "2.") { + return nil + } + if strings.HasPrefix(versionText, "1.17.") { + return nil + } + return fmt.Errorf("MongoDB 版本 %s 当前不受支持;仅支持 1.17.x 和 2.x", versionText) + default: + return nil + } +} + +func shouldRestrictToExplicitVersionArtifact(definition driverDefinition, selectedVersion string) bool { + versionText := normalizeVersion(strings.TrimSpace(selectedVersion)) + if versionText == "" { + return false + } + return !sameDriverVersion(versionText, definition.PinnedVersion) +} + +func optionalDriverSourceBuildAvailable(definition driverDefinition, selectedVersion string) bool { + driverType := normalizeDriverType(definition.Type) + if driverType == "" || !db.IsOptionalGoDriver(driverType) { + return false + } + if _, err := optionalDriverBuildTag(driverType, selectedVersion); err != nil { + return false + } + if _, err := exec.LookPath("go"); err != nil { + return false + } + if _, err := locateProjectRootForAgentBuild(); err != nil { + return false + } + return true +} + +func resolvePublishedDriverDownloadURL(definition driverDefinition, version string) (string, bool) { + driverType := normalizeDriverType(definition.Type) + versionText := normalizeVersion(strings.TrimSpace(version)) + if driverType == "" || versionText == "" { + return "", false + } + + tag := "v" + versionText + assetName, ok := resolvePublishedDriverReleaseAssetName(driverType, versionText, tag) + if !ok { + return "", false + } + return fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", updateRepo, tag, assetName), true +} + +func resolvePublishedDriverReleaseAssetName(driverType string, version string, tag string) (string, bool) { + assetNames := optionalDriverReleaseAssetNamesForVersion(driverType, version) + if len(assetNames) == 0 { + return "", false + } + + cacheKey := "tag:" + strings.TrimSpace(tag) + if sizeByAsset, ok := readReleaseAssetSizesFromCache(cacheKey); ok { + for _, assetName := range assetNames { + if sizeByAsset[assetName] > 0 { + return assetName, true + } + } + return "", false + } + + sizeByAsset, err := loadReleaseAssetSizesCached(cacheKey, func() (*githubRelease, error) { + return fetchReleaseByTag(tag) + }) + if err != nil { + return "", false + } + for _, assetName := range assetNames { + if sizeByAsset[assetName] > 0 { + return assetName, true + } + } + return "", false +} + func resolveDriverVersionPackageSizeBytes(definition driverDefinition, option driverVersionOptionItem) int64 { driverType := normalizeDriverType(definition.Type) if driverType == "" || definition.BuiltIn { @@ -1593,20 +1706,20 @@ func resolveDriverVersionPackageSizeBytes(definition driverDefinition, option dr if version == "" { return 0 } - assetName := optionalDriverReleaseAssetName(driverType) - if strings.TrimSpace(assetName) == "" { + assetNames := optionalDriverReleaseAssetNamesForVersion(driverType, version) + if len(assetNames) == 0 { return 0 } tag := "v" + version if sizeByAsset, ok := readReleaseAssetSizesFromCache("tag:" + tag); ok { - return resolveOptionalDriverAssetSize(sizeByAsset, driverType) + return resolveOptionalDriverAssetSizeForVersion(sizeByAsset, driverType, version) } // 下拉版本列表要求快速返回:仅复用已有缓存,不在这里触发网络请求。 if strings.EqualFold(strings.TrimSpace(option.Source), "latest") { if sizeByAsset, ok := readReleaseAssetSizesFromCache("latest"); ok { - return resolveOptionalDriverAssetSize(sizeByAsset, driverType) + return resolveOptionalDriverAssetSizeForVersion(sizeByAsset, driverType, version) } } return 0 @@ -1906,19 +2019,23 @@ func resolveDriverVersionOptionsFromReleases(definition driverDefinition) []driv return nil } - assetName := optionalDriverReleaseAssetName(driverType) - assetNames := optionalDriverReleaseAssetNames(driverType) result := make([]driverVersionOptionItem, 0, len(releases)) for _, release := range releases { if release.Prerelease { continue } tag := strings.TrimSpace(release.TagName) - if tag == "" || !releaseContainsAnyAsset(release, assetNames) { + version := normalizeVersion(tag) + if tag == "" || version == "" { + continue + } + assetName := optionalDriverReleaseAssetNameForVersion(driverType, version) + assetNames := optionalDriverReleaseAssetNamesForVersion(driverType, version) + if !releaseContainsAnyAsset(release, assetNames) { continue } result = append(result, driverVersionOptionItem{ - Version: normalizeVersion(tag), + Version: version, DownloadURL: fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", updateRepo, tag, assetName), Source: "release", }) @@ -2791,9 +2908,10 @@ func installOptionalDriverAgentFromLocalZip(zipPath string, definition driverDef func ensureOptionalDriverAgentBinary(a *App, definition driverDefinition, executablePath string, downloadURL string, selectedVersion string) (string, string, error) { driverType := normalizeDriverType(definition.Type) displayName := resolveDriverDisplayName(definition) - forceSourceBuild := shouldForceSourceBuildForVersion(driverType, selectedVersion) + forceSourceBuild := shouldForceSourceBuildForResolvedDownload(driverType, selectedVersion, downloadURL) preferSourceBuildBeforeDownload := shouldPreferSourceBuildBeforeDownload(driverType, selectedVersion) skipReuseCandidate := shouldSkipReusableAgentCandidate(driverType, selectedVersion) + restrictToExplicitArtifact := shouldRestrictToExplicitVersionArtifact(definition, selectedVersion) info, err := os.Stat(executablePath) if err == nil && !info.IsDir() { @@ -2851,7 +2969,7 @@ func ensureOptionalDriverAgentBinary(a *App, definition driverDefinition, execut } if !forceSourceBuild { - downloadURLs := resolveOptionalDriverAgentDownloadURLs(definition, downloadURL) + downloadURLs := resolveOptionalDriverAgentDownloadURLs(definition, downloadURL, selectedVersion) if len(downloadURLs) > 0 { for _, candidateURL := range downloadURLs { if a != nil { @@ -2865,7 +2983,7 @@ func ensureOptionalDriverAgentBinary(a *App, definition driverDefinition, execut } } bundleURLs := resolveOptionalDriverBundleDownloadURLs() - if len(bundleURLs) > 0 { + if !restrictToExplicitArtifact && len(bundleURLs) > 0 { for _, bundleURL := range bundleURLs { if a != nil { a.emitDriverDownloadProgress(driverType, "downloading", 20, 100, fmt.Sprintf("从驱动总包提取 %s 代理", displayName)) @@ -3108,6 +3226,23 @@ func shouldForceSourceBuildForVersion(driverType string, selectedVersion string) return resolveMongoDriverMajorFromVersion(selectedVersion) == 1 } +func shouldForceSourceBuildForResolvedDownload(driverType string, selectedVersion string, downloadURL string) bool { + if !shouldForceSourceBuildForVersion(driverType, selectedVersion) { + return false + } + + parsed, err := url.Parse(strings.TrimSpace(downloadURL)) + if err != nil || parsed == nil { + return true + } + switch strings.ToLower(strings.TrimSpace(parsed.Scheme)) { + case "http", "https": + return false + default: + return true + } +} + func shouldPreferSourceBuildBeforeDownload(driverType string, selectedVersion string) bool { _ = selectedVersion switch normalizeDriverType(driverType) { @@ -3224,11 +3359,80 @@ func optionalDriverReleaseAssetNameForType(typeName string, goos string, goarch return name } -func optionalDriverExecutableBaseNames(driverType string) []string { +func optionalDriverNameStemCandidates(driverType string, selectedVersion string) []string { + candidates := make([]string, 0, 3) + seen := make(map[string]struct{}, 3) + appendStem := func(stem string) { + trimmed := strings.TrimSpace(stem) + if trimmed == "" { + return + } + if _, ok := seen[trimmed]; ok { + return + } + seen[trimmed] = struct{}{} + candidates = append(candidates, trimmed) + } + + base := fmt.Sprintf("%s-driver-agent", optionalDriverPublicTypeName(driverType)) + if normalizeDriverType(driverType) == "mongodb" { + switch resolveMongoDriverMajorFromVersion(selectedVersion) { + case 1: + appendStem(base + "-v1") + appendStem(base) + case 2: + appendStem(base) + appendStem(base + "-v2") + default: + appendStem(base) + } + return candidates + } + + appendStem(base) + return candidates +} + +func optionalDriverExecutableBaseNamesForVersion(driverType string, selectedVersion string) []string { names := make([]string, 0, 2) seen := make(map[string]struct{}, 2) - appendName := func(typeName string) { - name := optionalDriverExecutableBaseNameForType(typeName) + appendName := func(stem string) { + name := strings.TrimSpace(stem) + if strings.TrimSpace(name) == "" { + return + } + if stdRuntime.GOOS == "windows" { + name += ".exe" + } + if _, ok := seen[name]; ok { + return + } + seen[name] = struct{}{} + names = append(names, name) + } + + for _, stem := range optionalDriverNameStemCandidates(driverType, selectedVersion) { + appendName(stem) + } + return names +} + +func optionalDriverExecutableBaseNames(driverType string) []string { + return optionalDriverExecutableBaseNamesForVersion(driverType, "") +} + +func optionalDriverReleaseAssetNamesForVersion(driverType string, selectedVersion string) []string { + names := make([]string, 0, 2) + seen := make(map[string]struct{}, 2) + appendName := func(stem string) { + trimmedStem := strings.TrimSpace(stem) + if trimmedStem == "" { + return + } + name := fmt.Sprintf("%s-%s-%s", trimmedStem, stdRuntime.GOOS, stdRuntime.GOARCH) + if strings.EqualFold(stdRuntime.GOOS, "windows") { + name += ".exe" + } if strings.TrimSpace(name) == "" { return } @@ -3239,27 +3443,14 @@ func optionalDriverExecutableBaseNames(driverType string) []string { names = append(names, name) } - appendName(optionalDriverPublicTypeName(driverType)) + for _, stem := range optionalDriverNameStemCandidates(driverType, selectedVersion) { + appendName(stem) + } return names } func optionalDriverReleaseAssetNames(driverType string) []string { - names := make([]string, 0, 2) - seen := make(map[string]struct{}, 2) - appendName := func(typeName string) { - name := optionalDriverReleaseAssetNameForType(typeName, stdRuntime.GOOS, stdRuntime.GOARCH) - if strings.TrimSpace(name) == "" { - return - } - if _, ok := seen[name]; ok { - return - } - seen[name] = struct{}{} - names = append(names, name) - } - - appendName(optionalDriverPublicTypeName(driverType)) - return names + return optionalDriverReleaseAssetNamesForVersion(driverType, "") } func optionalDriverExecutableBaseName(driverType string) string { @@ -3278,6 +3469,14 @@ func optionalDriverReleaseAssetName(driverType string) string { return names[0] } +func optionalDriverReleaseAssetNameForVersion(driverType string, selectedVersion string) string { + names := optionalDriverReleaseAssetNamesForVersion(driverType, selectedVersion) + if len(names) == 0 { + return optionalDriverReleaseAssetNameForType("", stdRuntime.GOOS, stdRuntime.GOARCH) + } + return names[0] +} + func optionalDriverBundlePlatformDir(goos string) string { switch strings.ToLower(strings.TrimSpace(goos)) { case "windows": @@ -3328,6 +3527,19 @@ func resolveOptionalDriverAssetSize(sizeByAsset map[string]int64, driverType str return 0 } +func resolveOptionalDriverAssetSizeForVersion(sizeByAsset map[string]int64, driverType string, version string) int64 { + if len(sizeByAsset) == 0 { + return 0 + } + for _, assetName := range optionalDriverReleaseAssetNamesForVersion(driverType, version) { + sizeBytes := sizeByAsset[assetName] + if sizeBytes > 0 { + return sizeBytes + } + } + return 0 +} + func resolveOptionalDriverBundleDownloadURLs() []string { candidates := make([]string, 0, 2) seen := make(map[string]struct{}, 2) @@ -3351,7 +3563,7 @@ func resolveOptionalDriverBundleDownloadURLs() []string { return candidates } -func resolveOptionalDriverAgentDownloadURLs(definition driverDefinition, rawURL string) []string { +func resolveOptionalDriverAgentDownloadURLs(definition driverDefinition, rawURL string, selectedVersion string) []string { driverType := normalizeDriverType(definition.Type) candidates := make([]string, 0, 3) seen := make(map[string]struct{}, 3) @@ -3373,6 +3585,9 @@ func resolveOptionalDriverAgentDownloadURLs(definition driverDefinition, rawURL appendURL(parsed.String()) } } + if shouldRestrictToExplicitVersionArtifact(definition, selectedVersion) { + return candidates + } assetNames := optionalDriverReleaseAssetNames(driverType) currentVersion := normalizeVersion(getCurrentVersion()) diff --git a/internal/app/methods_driver_version_test.go b/internal/app/methods_driver_version_test.go new file mode 100644 index 0000000..5fcfe34 --- /dev/null +++ b/internal/app/methods_driver_version_test.go @@ -0,0 +1,222 @@ +package app + +import ( + "fmt" + "os" + "runtime" + "strings" + "testing" + "time" +) + +func TestResolveVersionedDriverOptionUsesPublishedMongoV1Release(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + version := "1.17.4" + assetName := mongoVersionedReleaseAssetName(1) + seedReleaseAssetSizeCache(t, "tag:v"+version, map[string]int64{ + assetName: 24 << 20, + }) + chdirTemp(t) + + gotVersion, gotURL, ok := resolveVersionedDriverOption(definition, version, "history") + if !ok { + t.Fatal("expected published mongodb v1 option to remain available") + } + if gotVersion != version { + t.Fatalf("expected version %q, got %q", version, gotVersion) + } + + wantURL := fmt.Sprintf("https://github.com/%s/releases/download/v%s/%s", updateRepo, version, assetName) + if gotURL != wantURL { + t.Fatalf("expected published release URL %q, got %q", wantURL, gotURL) + } +} + +func TestDriverVersionSupportRangeForMongoDB(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + if err := validateDriverSelectedVersion(definition, "1.17.4"); err != nil { + t.Fatalf("expected 1.17.4 to stay supported, got %v", err) + } + if err := validateDriverSelectedVersion(definition, "2.5.0"); err != nil { + t.Fatalf("expected 2.5.0 to stay supported, got %v", err) + } + if err := validateDriverSelectedVersion(definition, "1.16.1"); err == nil { + t.Fatal("expected 1.16.1 to be rejected by MongoDB support range") + } +} + +func TestResolveVersionedDriverOptionSkipsMongoV1WithoutPublishedReleaseOrSourceBuild(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + version := "1.17.4" + seedReleaseAssetSizeCache(t, "tag:v"+version, map[string]int64{}) + chdirTemp(t) + + _, _, ok = resolveVersionedDriverOption(definition, version, "history") + if ok { + t.Fatal("expected unpublished mongodb v1 option to be filtered out when source build is unavailable") + } +} + +func TestResolveVersionedDriverOptionRejectsUnsupportedMongoV1Range(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + seedReleaseAssetSizeCache(t, "tag:v1.16.1", map[string]int64{ + mongoVersionedReleaseAssetName(1): 24 << 20, + }) + + _, _, ok = resolveVersionedDriverOption(definition, "1.16.1", "history") + if ok { + t.Fatal("expected MongoDB 1.16.1 to be hidden from the selectable version list") + } +} + +func TestResolveDriverVersionPackageSizeBytesReadsMongoV1VersionedAsset(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + version := "1.17.4" + assetName := mongoVersionedReleaseAssetName(1) + const wantSize int64 = 31 << 20 + seedReleaseAssetSizeCache(t, "tag:v"+version, map[string]int64{ + assetName: wantSize, + }) + + got := resolveDriverVersionPackageSizeBytes(definition, driverVersionOptionItem{ + Version: version, + Source: "history", + }) + if got != wantSize { + t.Fatalf("expected size %d, got %d", wantSize, got) + } +} + +func TestResolveOptionalDriverAgentDownloadURLsDoesNotFallbackForHistoricalVersion(t *testing.T) { + definition, ok := resolveDriverDefinition("mongodb") + if !ok { + t.Fatal("expected mongodb driver definition") + } + + explicitURL := fmt.Sprintf("https://github.com/Syngnat/GoNavi/releases/download/v1.17.4/%s", mongoVersionedReleaseAssetName(1)) + urls := resolveOptionalDriverAgentDownloadURLs( + definition, + explicitURL, + "1.17.4", + ) + if len(urls) != 1 { + t.Fatalf("expected only explicit historical URL, got %d candidates: %v", len(urls), urls) + } + if urls[0] != explicitURL { + t.Fatalf("unexpected historical URL candidate: %v", urls) + } +} + +func TestDownloadDriverPackageRejectsUnsupportedMongoVersion(t *testing.T) { + app := &App{} + + result := app.DownloadDriverPackage("mongodb", "1.16.1", "builtin://activate/mongodb?channel=history&version=1.16.1", t.TempDir()) + if result.Success { + t.Fatal("expected unsupported MongoDB 1.16.1 install to be rejected") + } + if !strings.Contains(result.Message, "仅支持 1.17.x 和 2.x") { + t.Fatalf("expected support-range error, got %q", result.Message) + } +} + +func TestShouldForceSourceBuildForResolvedDownload(t *testing.T) { + if !shouldForceSourceBuildForResolvedDownload("mongodb", "1.17.4", "builtin://activate/mongodb?channel=history&version=1.17.4") { + t.Fatal("expected mongodb v1 builtin install to keep source build mode") + } + + explicitURL := fmt.Sprintf("https://github.com/%s/releases/download/v1.17.4/%s", updateRepo, mongoVersionedReleaseAssetName(1)) + if shouldForceSourceBuildForResolvedDownload("mongodb", "1.17.4", explicitURL) { + t.Fatal("expected mongodb v1 published asset install to skip forced source build") + } + + if shouldForceSourceBuildForResolvedDownload("mongodb", "2.5.0", "builtin://activate/mongodb?channel=latest&version=2.5.0") { + t.Fatal("expected mongodb v2 install not to force source build") + } +} + +func seedReleaseAssetSizeCache(t *testing.T, cacheKey string, sizeByKey map[string]int64) { + t.Helper() + + driverReleaseSizeMu.Lock() + original := cloneReleaseAssetSizeCache(driverReleaseSizeMap) + driverReleaseSizeMap[cacheKey] = driverReleaseAssetSizeCacheEntry{ + LoadedAt: time.Now(), + SizeByKey: cloneInt64Map(sizeByKey), + } + driverReleaseSizeMu.Unlock() + + t.Cleanup(func() { + driverReleaseSizeMu.Lock() + driverReleaseSizeMap = original + driverReleaseSizeMu.Unlock() + }) +} + +func cloneReleaseAssetSizeCache(src map[string]driverReleaseAssetSizeCacheEntry) map[string]driverReleaseAssetSizeCacheEntry { + cloned := make(map[string]driverReleaseAssetSizeCacheEntry, len(src)) + for key, value := range src { + cloned[key] = driverReleaseAssetSizeCacheEntry{ + LoadedAt: value.LoadedAt, + SizeByKey: cloneInt64Map(value.SizeByKey), + Err: value.Err, + } + } + return cloned +} + +func cloneInt64Map(src map[string]int64) map[string]int64 { + if len(src) == 0 { + return map[string]int64{} + } + cloned := make(map[string]int64, len(src)) + for key, value := range src { + cloned[key] = value + } + return cloned +} + +func chdirTemp(t *testing.T) { + t.Helper() + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd failed: %v", err) + } + tempDir := t.TempDir() + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("chdir temp failed: %v", err) + } + t.Cleanup(func() { + if err := os.Chdir(wd); err != nil { + t.Fatalf("restore cwd failed: %v", err) + } + }) +} + +func mongoVersionedReleaseAssetName(major int) string { + name := fmt.Sprintf("mongodb-driver-agent-v%d-%s-%s", major, runtime.GOOS, runtime.GOARCH) + if runtime.GOOS == "windows" { + return name + ".exe" + } + return name +}