mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-27 17:02:04 +08:00
2081 lines
72 KiB
Go
2081 lines
72 KiB
Go
package app
|
||
|
||
import (
|
||
"archive/zip"
|
||
"encoding/json"
|
||
"fmt"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strings"
|
||
"sync"
|
||
"sync/atomic"
|
||
"testing"
|
||
"time"
|
||
|
||
"GoNavi-Wails/internal/db"
|
||
"GoNavi-Wails/shared/i18n"
|
||
)
|
||
|
||
func TestDriverStatusItemJSONIncludesStableReasonCode(t *testing.T) {
|
||
item := driverStatusItem{
|
||
Type: "clickhouse",
|
||
Name: "ClickHouse",
|
||
ReasonCode: driverStatusReasonSlimBuildMissingDriver,
|
||
}
|
||
|
||
payload, err := json.Marshal(item)
|
||
if err != nil {
|
||
t.Fatalf("marshal driver status item: %v", err)
|
||
}
|
||
|
||
if !strings.Contains(string(payload), `"reasonCode":"slim_build_missing_driver"`) {
|
||
t.Fatalf("expected stable reasonCode in payload, got %s", string(payload))
|
||
}
|
||
}
|
||
|
||
func TestDriverNetworkProbeItemJSONIncludesStableProbeCode(t *testing.T) {
|
||
item := driverNetworkProbeItem{
|
||
ProbeCode: driverNetworkProbeCodeGitHubRelease,
|
||
Name: "GitHub driver release",
|
||
URL: "https://github.com/example/release",
|
||
}
|
||
|
||
payload, err := json.Marshal(item)
|
||
if err != nil {
|
||
t.Fatalf("marshal driver network probe item: %v", err)
|
||
}
|
||
|
||
if !strings.Contains(string(payload), `"probeCode":"github_release"`) {
|
||
t.Fatalf("expected stable probeCode in payload, got %s", string(payload))
|
||
}
|
||
}
|
||
|
||
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", driverReleaseRepo, version, assetName)
|
||
if gotURL != wantURL {
|
||
t.Fatalf("expected published release URL %q, got %q", wantURL, gotURL)
|
||
}
|
||
}
|
||
|
||
func TestMongoDBDefaultDriverVersionUsesLegacyCompatibleLine(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mongodb")
|
||
if !ok {
|
||
t.Fatal("expected mongodb driver definition")
|
||
}
|
||
|
||
if definition.PinnedVersion != "1.17.9" {
|
||
t.Fatalf("expected MongoDB default driver version 1.17.9, got %q", definition.PinnedVersion)
|
||
}
|
||
if got := resolveDriverInstallVersion("", definition.DefaultDownloadURL, definition); got != "1.17.9" {
|
||
t.Fatalf("expected builtin MongoDB install to resolve version 1.17.9, got %q", got)
|
||
}
|
||
}
|
||
|
||
func TestCurrentDriverReleaseTagUsesDevLatestForDevBuild(t *testing.T) {
|
||
originalVersion := AppVersion
|
||
AppVersion = "dev-abc1234"
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
if got := currentDriverReleaseTag(); got != driverReleaseDevTag {
|
||
t.Fatalf("expected dev driver release tag %q, got %q", driverReleaseDevTag, got)
|
||
}
|
||
}
|
||
|
||
func TestCurrentDriverReleaseTagUsesDevLatestForLocalTestBuild(t *testing.T) {
|
||
originalVersion := AppVersion
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
for _, version := range []string{"0.0.1-test", "0.7.9-dev", "0.7.9-local", "0.7.9-SNAPSHOT"} {
|
||
AppVersion = version
|
||
if got := currentDriverReleaseTag(); got != driverReleaseDevTag {
|
||
t.Fatalf("expected %s to use dev driver release tag %q, got %q", version, driverReleaseDevTag, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestCurrentDriverReleaseTagUsesVersionedReleaseForStableBuild(t *testing.T) {
|
||
originalVersion := AppVersion
|
||
AppVersion = "0.7.9"
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
if got := currentDriverReleaseTag(); got != "v0.7.9" {
|
||
t.Fatalf("expected stable driver release tag v0.7.9, got %q", got)
|
||
}
|
||
}
|
||
|
||
func TestResolveOptionalDriverBundleDownloadURLsUsesDriverReleaseRepo(t *testing.T) {
|
||
originalVersion := AppVersion
|
||
AppVersion = "0.7.4"
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
urls := resolveOptionalDriverBundleDownloadURLs()
|
||
wantTagged := driverReleaseDownloadURL("v0.7.4", optionalDriverBundleAssetName)
|
||
wantLatest := driverReleaseLatestDownloadURL(optionalDriverBundleAssetName)
|
||
if len(urls) < 2 {
|
||
t.Fatalf("expected at least tagged and latest bundle URLs, got %v", urls)
|
||
}
|
||
foundTagged := false
|
||
foundLatest := false
|
||
for _, candidate := range urls {
|
||
if candidate == wantTagged {
|
||
foundTagged = true
|
||
}
|
||
if candidate == wantLatest {
|
||
foundLatest = true
|
||
}
|
||
}
|
||
if !foundTagged || !foundLatest {
|
||
t.Fatalf("expected bundle URLs to include tagged=%q and latest=%q, got %v", wantTagged, wantLatest, urls)
|
||
}
|
||
}
|
||
|
||
func TestDriverReleaseAssetAPIURLUsesReleaseAssetEndpoint(t *testing.T) {
|
||
asset := githubAsset{
|
||
Name: "kingbase-driver-agent-darwin-arm64",
|
||
BrowserDownloadURL: "https://github.com/Syngnat/GoNavi-DriverAgents/releases/download/dev-latest/kingbase-driver-agent-darwin-arm64",
|
||
URL: "https://api.github.com/repos/Syngnat/GoNavi-DriverAgents/releases/assets/123456",
|
||
Size: 18 << 20,
|
||
}
|
||
if got := driverReleaseAssetAPIURL(asset); got != "https://api.github.com/repos/Syngnat/GoNavi-DriverAgents/releases/assets/123456#kingbase-driver-agent-darwin-arm64" {
|
||
t.Fatalf("expected release asset API URL, got %q", got)
|
||
}
|
||
}
|
||
|
||
func TestOptionalDriverDownloadZipURLAcceptsAssetAPIFragment(t *testing.T) {
|
||
urlText := "https://api.github.com/repos/Syngnat/GoNavi-DriverAgents/releases/assets/123456#duckdb-driver.zip"
|
||
if !isOptionalDriverDownloadZipURL(urlText) {
|
||
t.Fatalf("expected asset API URL with zip fragment to be treated as zip download: %q", urlText)
|
||
}
|
||
}
|
||
|
||
func TestOptionalDriverAgentDownloadAndBuildErrorsUseI18nWrappers(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
|
||
functionNames := []string{
|
||
"ensureOptionalDriverAgentBinary",
|
||
"downloadOptionalDriverAgentBinary",
|
||
"downloadOptionalDriverAgentFromBundle",
|
||
"buildOptionalDriverAgentFromSource",
|
||
}
|
||
functionSource := ""
|
||
for _, name := range functionNames {
|
||
start := strings.Index(source, "func "+name)
|
||
if start < 0 {
|
||
t.Fatalf("methods_driver.go missing %s", name)
|
||
}
|
||
rest := source[start+len("func "+name):]
|
||
end := strings.Index(rest, "\nfunc ")
|
||
if end < 0 {
|
||
t.Fatalf("%s function boundary not found", name)
|
||
}
|
||
functionSource += rest[:end]
|
||
}
|
||
|
||
rawWrappers := []string{
|
||
`fmt.Errorf("下载地址为空")`,
|
||
`fmt.Errorf("下载失败:%w"`,
|
||
`fmt.Errorf("安装预编译驱动包失败:%w"`,
|
||
`fmt.Errorf("计算驱动代理摘要失败:%w"`,
|
||
`fmt.Errorf("%s 当前平台需要随包提供运行时依赖`,
|
||
`fmt.Errorf("驱动总包下载地址为空")`,
|
||
`fmt.Errorf("下载驱动总包失败:%w"`,
|
||
`fmt.Errorf("打开驱动总包失败:%w"`,
|
||
`fmt.Errorf("驱动总包内未找到 %s`,
|
||
`fmt.Errorf("读取驱动总包条目失败:%w"`,
|
||
`fmt.Errorf("当前环境未安装 Go`,
|
||
`fmt.Errorf("构建 %s 驱动代理超时`,
|
||
`fmt.Errorf("构建 %s 驱动代理失败`,
|
||
`fmt.Errorf("复制 %s 运行时依赖失败`,
|
||
`fmt.Errorf("设置 %s 驱动代理权限失败`,
|
||
`fmt.Errorf("计算 %s 驱动代理摘要失败`,
|
||
`fmt.Errorf("清理已安装 %s 驱动代理失败`,
|
||
`fmt.Errorf("%s 驱动代理路径被目录占用`,
|
||
`fmt.Errorf("创建 %s 驱动目录失败`,
|
||
`fmt.Errorf("复制预置 %s 驱动代理失败`,
|
||
`fmt.Errorf("计算预置 %s 驱动代理摘要失败`,
|
||
}
|
||
for _, rawWrapper := range rawWrappers {
|
||
if strings.Contains(functionSource, rawWrapper) {
|
||
t.Fatalf("optional driver agent flow still contains raw error wrapper %s", rawWrapper)
|
||
}
|
||
}
|
||
|
||
requiredKeys := []string{
|
||
"driver_manager.backend.error.download_url_empty",
|
||
"driver_manager.backend.error.download_failed",
|
||
"driver_manager.backend.error.bundle_url_empty",
|
||
"driver_manager.backend.error.bundle_download_failed",
|
||
"driver_manager.backend.error.open_bundle_failed",
|
||
"driver_manager.backend.error.read_bundle_entry_failed",
|
||
"driver_manager.backend.error.source_build_failed",
|
||
"driver_manager.backend.error.remove_installed_agent_failed",
|
||
"driver_manager.backend.error.agent_path_occupied_by_directory",
|
||
"driver_manager.backend.error.create_named_directory_failed",
|
||
"driver_manager.backend.error.copy_bundled_agent_failed",
|
||
"driver_manager.backend.error.bundled_agent_hash_failed",
|
||
}
|
||
for _, key := range requiredKeys {
|
||
if !strings.Contains(functionSource, key) {
|
||
t.Fatalf("optional driver agent flow does not reference i18n key %q", key)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestInstallOptionalDriverAgentPackageUsesLocalizedNamedHashFailure(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
|
||
start := strings.Index(source, "func installOptionalDriverAgentPackage")
|
||
if start < 0 {
|
||
t.Fatal("methods_driver.go missing installOptionalDriverAgentPackage")
|
||
}
|
||
rest := source[start+len("func installOptionalDriverAgentPackage"):]
|
||
end := strings.Index(rest, "\nfunc ")
|
||
if end < 0 {
|
||
t.Fatal("installOptionalDriverAgentPackage function boundary not found")
|
||
}
|
||
functionSource := rest[:end]
|
||
|
||
if strings.Contains(functionSource, `fmt.Errorf("计算 %s 驱动代理摘要失败:%w"`) {
|
||
t.Fatal("installOptionalDriverAgentPackage still contains raw driver agent hash wrapper")
|
||
}
|
||
if !strings.Contains(functionSource, `newLocalizedDriverBackendError("driver_manager.backend.error.named_agent_hash_failed"`) {
|
||
t.Fatal("installOptionalDriverAgentPackage missing localized named agent hash wrapper")
|
||
}
|
||
}
|
||
|
||
func TestEnsureOptionalDriverAgentBinaryUsesCurrentLanguageForPrebuiltPathErrors(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
executablePath := filepath.Join(t.TempDir(), optionalDriverExecutableBaseName("kingbase"))
|
||
if err := os.MkdirAll(executablePath, 0o755); err != nil {
|
||
t.Fatalf("create occupied executable directory failed: %v", err)
|
||
}
|
||
|
||
_, _, err := ensureOptionalDriverAgentBinary(
|
||
app,
|
||
driverDefinition{Type: "kingbase", Name: "Kingbase"},
|
||
executablePath,
|
||
"builtin://activate/kingbase",
|
||
"0.0.0-test",
|
||
)
|
||
if err == nil {
|
||
t.Fatal("expected occupied executable path to fail")
|
||
}
|
||
message := localizedDriverBackendErrorMessage(app, err)
|
||
if !strings.Contains(message, "Kingbase driver-agent path is occupied by a directory") {
|
||
t.Fatalf("expected English path occupied message, got %q", message)
|
||
}
|
||
if strings.Contains(message, "驱动代理路径被目录占用") {
|
||
t.Fatalf("expected localized wrapper instead of fixed Chinese, got %q", message)
|
||
}
|
||
if !strings.Contains(message, executablePath) {
|
||
t.Fatalf("expected raw executable path to stay in detail, got %q", message)
|
||
}
|
||
}
|
||
|
||
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 := driverReleaseDownloadURL("v1.17.4", 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 TestResolveOptionalDriverAgentDownloadURLsUsesMongoV1AssetForCompatibleDefault(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mongodb")
|
||
if !ok {
|
||
t.Fatal("expected mongodb driver definition")
|
||
}
|
||
|
||
originalVersion := AppVersion
|
||
AppVersion = "0.7.9"
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
assetName := mongoVersionedReleaseAssetName(1)
|
||
seedReleaseAssetSizeCache(t, "tag:v0.7.9", map[string]int64{
|
||
assetName: 24 << 20,
|
||
})
|
||
seedReleaseAssetSizeCache(t, "latest", map[string]int64{})
|
||
|
||
urls := resolveOptionalDriverAgentDownloadURLs(
|
||
definition,
|
||
"builtin://activate/mongodb",
|
||
"1.17.9",
|
||
)
|
||
want := driverReleaseDownloadURL("v0.7.9", assetName)
|
||
for _, got := range urls {
|
||
if got == want {
|
||
return
|
||
}
|
||
}
|
||
t.Fatalf("expected MongoDB v1 release asset %q in candidates, got %v", want, urls)
|
||
}
|
||
|
||
func TestResolveOptionalDriverAgentDownloadURLsDoesNotUseMongoV2BaseForCompatibleDefault(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mongodb")
|
||
if !ok {
|
||
t.Fatal("expected mongodb driver definition")
|
||
}
|
||
|
||
originalVersion := AppVersion
|
||
AppVersion = "0.7.9"
|
||
t.Cleanup(func() {
|
||
AppVersion = originalVersion
|
||
})
|
||
|
||
baseAssetName := optionalDriverReleaseAssetNameForType("mongodb", runtime.GOOS, runtime.GOARCH)
|
||
seedReleaseAssetSizeCache(t, "tag:v0.7.9", map[string]int64{
|
||
baseAssetName: 24 << 20,
|
||
})
|
||
seedReleaseAssetSizeCache(t, "latest", map[string]int64{
|
||
baseAssetName: 24 << 20,
|
||
})
|
||
|
||
urls := resolveOptionalDriverAgentDownloadURLs(
|
||
definition,
|
||
"builtin://activate/mongodb",
|
||
"1.17.9",
|
||
)
|
||
for _, got := range urls {
|
||
if strings.Contains(got, baseAssetName) {
|
||
t.Fatalf("expected MongoDB v1 install not to use ambiguous base asset %q, got %v", baseAssetName, urls)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestMongoDBVersionedAssetNamesDoNotFallbackToBaseForV1(t *testing.T) {
|
||
v1AssetName := mongoVersionedReleaseAssetName(1)
|
||
baseAssetName := optionalDriverReleaseAssetNameForType("mongodb", runtime.GOOS, runtime.GOARCH)
|
||
|
||
v1Names := optionalDriverReleaseAssetNamesForVersion("mongodb", "1.17.9")
|
||
if len(v1Names) != 1 || v1Names[0] != v1AssetName {
|
||
t.Fatalf("expected MongoDB v1 to use only %q, got %v", v1AssetName, v1Names)
|
||
}
|
||
for _, name := range v1Names {
|
||
if name == baseAssetName {
|
||
t.Fatalf("MongoDB v1 must not fallback to ambiguous base asset %q", baseAssetName)
|
||
}
|
||
}
|
||
|
||
v2Names := optionalDriverReleaseAssetNamesForVersion("mongodb", "2.5.0")
|
||
if len(v2Names) < 2 || v2Names[0] != mongoVersionedReleaseAssetName(2) || v2Names[1] != baseAssetName {
|
||
t.Fatalf("expected MongoDB v2 to prefer versioned asset then base compatibility asset, got %v", v2Names)
|
||
}
|
||
}
|
||
|
||
func TestResolveOptionalDriverAgentDownloadURLsSkipsBundleOnlyDamengAsset(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("dameng")
|
||
if !ok {
|
||
t.Fatal("expected dameng driver definition")
|
||
}
|
||
|
||
version := normalizeVersion(definition.PinnedVersion)
|
||
assetName := optionalDriverReleaseAssetNameForVersion("dameng", version)
|
||
seedReleaseAssetCacheEntry(t, "tag:v"+version, map[string]int64{
|
||
assetName: 23 << 20,
|
||
}, nil)
|
||
seedReleaseAssetCacheEntry(t, "latest", map[string]int64{
|
||
assetName: 23 << 20,
|
||
}, nil)
|
||
|
||
urls := resolveOptionalDriverAgentDownloadURLs(definition, "builtin://activate/dameng", version)
|
||
if len(urls) != 0 {
|
||
t.Fatalf("expected bundle-only dameng install to skip direct asset URLs, got %v", urls)
|
||
}
|
||
}
|
||
|
||
func TestShouldUseOptionalDriverBundleFallbackSkipsWhenDirectAssetExists(t *testing.T) {
|
||
if shouldUseOptionalDriverBundleFallback("sqlserver", false, 1) {
|
||
t.Fatal("expected published single-file driver asset to avoid 497MB bundle fallback")
|
||
}
|
||
}
|
||
|
||
func TestShouldUseOptionalDriverBundleFallbackKeepsBundleWhenDirectAssetMissing(t *testing.T) {
|
||
if !shouldUseOptionalDriverBundleFallback("dameng", false, 0) {
|
||
t.Fatal("expected missing single-file driver asset to keep bundle fallback")
|
||
}
|
||
if shouldUseOptionalDriverBundleFallback("dameng", true, 0) {
|
||
t.Fatal("expected explicit version artifact installs to skip bundle fallback")
|
||
}
|
||
}
|
||
|
||
func TestFormatOptionalDriverAttemptErrorRemovesDuplicatedSourcePrefix(t *testing.T) {
|
||
source := "https://github.com/Syngnat/GoNavi-DriverAgents/releases/download/dev-latest/kingbase-driver-agent-darwin-arm64"
|
||
err := fmt.Errorf("%s: kingbase 驱动代理 revision 不匹配(已安装:src-old,当前需要:src-new),请安装当前版本对应的 driver-agent", source)
|
||
|
||
got := formatOptionalDriverAttemptError(nil, source, err)
|
||
if strings.Count(got, source) != 1 {
|
||
t.Fatalf("expected source to appear once, got %q", got)
|
||
}
|
||
if !strings.Contains(got, "kingbase 驱动代理 revision 不匹配") {
|
||
t.Fatalf("expected revision mismatch detail, got %q", got)
|
||
}
|
||
}
|
||
|
||
func TestVerifyInstalledOptionalDriverAgentRevisionLocalizesMetadataUnavailable(t *testing.T) {
|
||
originalProbe := optionalDriverAgentMetadataProbe
|
||
t.Cleanup(func() {
|
||
optionalDriverAgentMetadataProbe = originalProbe
|
||
})
|
||
optionalDriverAgentMetadataProbe = func(driverType string, executablePath string) (db.OptionalDriverAgentMetadata, error) {
|
||
if driverType != "sqlserver" {
|
||
t.Fatalf("unexpected driver type %q", driverType)
|
||
}
|
||
if executablePath != `C:\raw\driver-agent.exe` {
|
||
t.Fatalf("unexpected executable path %q", executablePath)
|
||
}
|
||
return db.OptionalDriverAgentMetadata{}, fmt.Errorf("probe raw detail")
|
||
}
|
||
|
||
_, err := verifyInstalledOptionalDriverAgentRevision("sqlserver", `C:\raw\driver-agent.exe`)
|
||
if err == nil {
|
||
t.Fatal("expected metadata unavailable error")
|
||
}
|
||
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
got := localizedDriverBackendErrorMessage(app, err)
|
||
if !strings.Contains(got, "driver-agent version metadata is unavailable") {
|
||
t.Fatalf("expected English metadata wrapper, got %q", got)
|
||
}
|
||
if !strings.Contains(got, "probe raw detail") {
|
||
t.Fatalf("expected raw probe detail to pass through, got %q", got)
|
||
}
|
||
for _, forbidden := range []string{"驱动代理", "不可用", "请安装"} {
|
||
if strings.Contains(got, forbidden) {
|
||
t.Fatalf("expected no Chinese wrapper fragment %q in %q", forbidden, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestVerifyInstalledOptionalDriverAgentRevisionLocalizesRevisionMismatch(t *testing.T) {
|
||
expectedRevision := strings.TrimSpace(db.OptionalDriverAgentRevision("sqlserver"))
|
||
if expectedRevision == "" {
|
||
t.Fatal("expected sqlserver to define optional driver agent revision")
|
||
}
|
||
|
||
cases := []struct {
|
||
name string
|
||
revision string
|
||
wantActual string
|
||
}{
|
||
{name: "raw actual revision", revision: "src-old", wantActual: "src-old"},
|
||
{name: "empty actual revision", revision: "", wantActual: "empty"},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
originalProbe := optionalDriverAgentMetadataProbe
|
||
t.Cleanup(func() {
|
||
optionalDriverAgentMetadataProbe = originalProbe
|
||
})
|
||
optionalDriverAgentMetadataProbe = func(driverType string, executablePath string) (db.OptionalDriverAgentMetadata, error) {
|
||
return db.OptionalDriverAgentMetadata{
|
||
DriverType: driverType,
|
||
AgentRevision: tc.revision,
|
||
}, nil
|
||
}
|
||
|
||
_, err := verifyInstalledOptionalDriverAgentRevision("sqlserver", `C:\raw\driver-agent.exe`)
|
||
if err == nil {
|
||
t.Fatal("expected revision mismatch error")
|
||
}
|
||
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
got := localizedDriverBackendErrorMessage(app, err)
|
||
if !strings.Contains(got, "driver-agent revision does not match") {
|
||
t.Fatalf("expected English revision mismatch wrapper, got %q", got)
|
||
}
|
||
if !strings.Contains(got, "installed: "+tc.wantActual) {
|
||
t.Fatalf("expected installed revision %q to pass through, got %q", tc.wantActual, got)
|
||
}
|
||
if !strings.Contains(got, "required: "+expectedRevision) {
|
||
t.Fatalf("expected required revision %q to pass through, got %q", expectedRevision, got)
|
||
}
|
||
for _, forbidden := range []string{"驱动代理", "不匹配", "已安装", "当前需要", "请安装", "空"} {
|
||
if strings.Contains(got, forbidden) {
|
||
t.Fatalf("expected no Chinese wrapper fragment %q in %q", forbidden, got)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestVerifyInstalledOptionalDriverAgentRevisionUsesI18nWrappers(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
start := strings.Index(source, "func verifyInstalledOptionalDriverAgentRevision")
|
||
if start < 0 {
|
||
t.Fatal("methods_driver.go missing verifyInstalledOptionalDriverAgentRevision")
|
||
}
|
||
rest := source[start:]
|
||
end := strings.Index(rest[len("func verifyInstalledOptionalDriverAgentRevision"):], "\nfunc ")
|
||
if end < 0 {
|
||
t.Fatal("verifyInstalledOptionalDriverAgentRevision function boundary not found")
|
||
}
|
||
functionSource := rest[:len("func verifyInstalledOptionalDriverAgentRevision")+end]
|
||
|
||
for _, rawWrapper := range []string{
|
||
`驱动代理版本元数据不可用,请安装当前版本对应的 driver-agent`,
|
||
`驱动代理 revision 不匹配(已安装:`,
|
||
`actualLabel = "空"`,
|
||
} {
|
||
if strings.Contains(functionSource, rawWrapper) {
|
||
t.Fatalf("verifyInstalledOptionalDriverAgentRevision still contains raw wrapper %s", rawWrapper)
|
||
}
|
||
}
|
||
for _, key := range []string{
|
||
"driver_manager.backend.error.agent_metadata_unavailable",
|
||
"driver_manager.backend.error.agent_revision_mismatch",
|
||
"driver_manager.backend.error.agent_revision_mismatch_empty_actual",
|
||
} {
|
||
if !strings.Contains(functionSource, key) {
|
||
t.Fatalf("verifyInstalledOptionalDriverAgentRevision does not reference i18n key %q", key)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestAppendOptionalDriverAttemptErrorDeduplicatesIdenticalEntries(t *testing.T) {
|
||
source := "https://github.com/Syngnat/GoNavi-DriverAgents/releases/latest/download/GoNavi-DriverAgents.zip#MacOS/kingbase-driver-agent-darwin-arm64"
|
||
err := fmt.Errorf("kingbase 驱动代理 revision 不匹配(已安装:src-old,当前需要:src-new),请安装当前版本对应的 driver-agent")
|
||
|
||
entries := appendOptionalDriverAttemptError(nil, nil, source, err)
|
||
entries = appendOptionalDriverAttemptError(nil, entries, source, err)
|
||
if len(entries) != 1 {
|
||
t.Fatalf("expected duplicate driver attempt error to be collapsed, got %d entries: %v", len(entries), entries)
|
||
}
|
||
}
|
||
|
||
func TestResolveDriverInstallVersionUsesPinnedVersionForBuiltinActivateURL(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("sqlserver")
|
||
if !ok {
|
||
t.Fatal("expected sqlserver driver definition")
|
||
}
|
||
if normalizeVersion(definition.PinnedVersion) == "" {
|
||
t.Fatal("expected sqlserver default definition to include builtin manifest pinned version")
|
||
}
|
||
|
||
got := resolveDriverInstallVersion("", "builtin://activate/sqlserver", definition)
|
||
want := normalizeVersion(definition.PinnedVersion)
|
||
if got != want {
|
||
t.Fatalf("expected builtin activate URL to fall back to pinned version %q, got %q", want, got)
|
||
}
|
||
}
|
||
|
||
func TestBuiltinActivatePinnedVersionDoesNotRestrictBundleFallback(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("sqlserver")
|
||
if !ok {
|
||
t.Fatal("expected sqlserver driver definition")
|
||
}
|
||
|
||
selectedVersion := resolveDriverInstallVersion("", "builtin://activate/sqlserver", definition)
|
||
if shouldRestrictToExplicitVersionArtifact(definition, selectedVersion) {
|
||
t.Fatalf("expected builtin activate default version %q not to restrict bundle fallback", selectedVersion)
|
||
}
|
||
}
|
||
|
||
func TestIRISDriverDefinitionUsesOptionalAgent(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("iris")
|
||
if !ok {
|
||
t.Fatal("expected iris driver definition")
|
||
}
|
||
if definition.Name != "InterSystems IRIS" {
|
||
t.Fatalf("unexpected iris driver name: %q", definition.Name)
|
||
}
|
||
if driverGoModulePathMap["iris"] != "github.com/caretdev/go-irisnative" {
|
||
t.Fatalf("unexpected iris go module path: %q", driverGoModulePathMap["iris"])
|
||
}
|
||
if definition.PinnedVersion != "0.2.1" {
|
||
t.Fatalf("unexpected iris definition pinned version: %q", definition.PinnedVersion)
|
||
}
|
||
if definition.DefaultDownloadURL != "builtin://activate/iris" {
|
||
t.Fatalf("unexpected iris default download URL: %q", definition.DefaultDownloadURL)
|
||
}
|
||
if latestDriverVersionMap["iris"] != "0.2.1" {
|
||
t.Fatalf("unexpected iris pinned version: %q", latestDriverVersionMap["iris"])
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("iris", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve iris build tags failed: %v", err)
|
||
}
|
||
if tags != "gonavi_iris_driver" {
|
||
t.Fatalf("unexpected iris build tag: %q", tags)
|
||
}
|
||
}
|
||
|
||
func TestElasticsearchDriverDefinitionUsesOptionalAgent(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("elasticsearch")
|
||
if !ok {
|
||
t.Fatal("expected elasticsearch driver definition")
|
||
}
|
||
if definition.Name != "Elasticsearch" {
|
||
t.Fatalf("unexpected elasticsearch driver name: %q", definition.Name)
|
||
}
|
||
if definition.BuiltIn {
|
||
t.Fatal("expected elasticsearch to be an optional driver agent")
|
||
}
|
||
if driverGoModulePathMap["elasticsearch"] != "github.com/elastic/go-elasticsearch/v8" {
|
||
t.Fatalf("unexpected elasticsearch go module path: %q", driverGoModulePathMap["elasticsearch"])
|
||
}
|
||
if definition.PinnedVersion != "8.19.6" {
|
||
t.Fatalf("unexpected elasticsearch definition pinned version: %q", definition.PinnedVersion)
|
||
}
|
||
if definition.DefaultDownloadURL != "builtin://activate/elasticsearch" {
|
||
t.Fatalf("unexpected elasticsearch default download URL: %q", definition.DefaultDownloadURL)
|
||
}
|
||
if latestDriverVersionMap["elasticsearch"] != "8.19.6" {
|
||
t.Fatalf("unexpected elasticsearch pinned version: %q", latestDriverVersionMap["elasticsearch"])
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("elasticsearch", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve elasticsearch build tags failed: %v", err)
|
||
}
|
||
if tags != "gonavi_elasticsearch_driver" {
|
||
t.Fatalf("unexpected elasticsearch build tag: %q", tags)
|
||
}
|
||
}
|
||
|
||
func TestTrinoDriverDefinitionUsesOptionalAgent(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("trino")
|
||
if !ok {
|
||
t.Fatal("expected trino driver definition")
|
||
}
|
||
if definition.Name != "Trino" {
|
||
t.Fatalf("unexpected trino driver name: %q", definition.Name)
|
||
}
|
||
if definition.BuiltIn {
|
||
t.Fatal("expected trino to be an optional driver agent")
|
||
}
|
||
if driverGoModulePathMap["trino"] != "github.com/trinodb/trino-go-client" {
|
||
t.Fatalf("unexpected trino go module path: %q", driverGoModulePathMap["trino"])
|
||
}
|
||
if definition.PinnedVersion != "0.333.0" {
|
||
t.Fatalf("unexpected trino definition pinned version: %q", definition.PinnedVersion)
|
||
}
|
||
if definition.DefaultDownloadURL != "builtin://activate/trino" {
|
||
t.Fatalf("unexpected trino default download URL: %q", definition.DefaultDownloadURL)
|
||
}
|
||
if latestDriverVersionMap["trino"] != "0.333.0" {
|
||
t.Fatalf("unexpected trino pinned version: %q", latestDriverVersionMap["trino"])
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("trino", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve trino build tags failed: %v", err)
|
||
}
|
||
if tags != "gonavi_trino_driver" {
|
||
t.Fatalf("unexpected trino build tag: %q", tags)
|
||
}
|
||
}
|
||
|
||
func TestIoTDBDriverDefinitionUsesOptionalAgent(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("iotdb")
|
||
if !ok {
|
||
t.Fatal("expected iotdb driver definition")
|
||
}
|
||
if definition.Name != "Apache IoTDB" {
|
||
t.Fatalf("unexpected iotdb driver name: %q", definition.Name)
|
||
}
|
||
if definition.BuiltIn {
|
||
t.Fatal("expected iotdb to be an optional driver agent")
|
||
}
|
||
if driverGoModulePathMap["iotdb"] != "github.com/apache/iotdb-client-go" {
|
||
t.Fatalf("unexpected iotdb go module path: %q", driverGoModulePathMap["iotdb"])
|
||
}
|
||
if definition.PinnedVersion != "1.3.7" {
|
||
t.Fatalf("unexpected iotdb definition pinned version: %q", definition.PinnedVersion)
|
||
}
|
||
if definition.DefaultDownloadURL != "builtin://activate/iotdb" {
|
||
t.Fatalf("unexpected iotdb default download URL: %q", definition.DefaultDownloadURL)
|
||
}
|
||
if latestDriverVersionMap["iotdb"] != "1.3.7" {
|
||
t.Fatalf("unexpected iotdb pinned version: %q", latestDriverVersionMap["iotdb"])
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("iotdb", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve iotdb build tags failed: %v", err)
|
||
}
|
||
if tags != "gonavi_iotdb_driver" {
|
||
t.Fatalf("unexpected iotdb build tag: %q", tags)
|
||
}
|
||
}
|
||
|
||
func TestKafkaDriverDefinitionIsBuiltIn(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("apache-kafka")
|
||
if !ok {
|
||
t.Fatal("expected kafka driver definition")
|
||
}
|
||
if definition.Name != "Kafka" {
|
||
t.Fatalf("unexpected kafka driver name: %q", definition.Name)
|
||
}
|
||
if !definition.BuiltIn {
|
||
t.Fatal("expected kafka to be a built-in driver")
|
||
}
|
||
if definition.PinnedVersion != "" || definition.DefaultDownloadURL != "" {
|
||
t.Fatalf("expected kafka builtin definition to omit optional-agent metadata: %#v", definition)
|
||
}
|
||
}
|
||
|
||
func TestMQTTDriverDefinitionIsBuiltIn(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mqtts")
|
||
if !ok {
|
||
t.Fatal("expected mqtt driver definition")
|
||
}
|
||
if definition.Name != "MQTT" {
|
||
t.Fatalf("unexpected mqtt driver name: %q", definition.Name)
|
||
}
|
||
if !definition.BuiltIn {
|
||
t.Fatal("expected mqtt to be a built-in driver")
|
||
}
|
||
if definition.PinnedVersion != "" || definition.DefaultDownloadURL != "" {
|
||
t.Fatalf("expected mqtt builtin definition to omit optional-agent metadata: %#v", definition)
|
||
}
|
||
}
|
||
|
||
func TestRocketMQDriverDefinitionIsBuiltIn(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("rmq")
|
||
if !ok {
|
||
t.Fatal("expected rocketmq driver definition")
|
||
}
|
||
if definition.Name != "RocketMQ" {
|
||
t.Fatalf("unexpected rocketmq driver name: %q", definition.Name)
|
||
}
|
||
if !definition.BuiltIn {
|
||
t.Fatal("expected rocketmq to be a built-in driver")
|
||
}
|
||
if definition.PinnedVersion != "" || definition.DefaultDownloadURL != "" {
|
||
t.Fatalf("expected rocketmq builtin definition to omit optional-agent metadata: %#v", definition)
|
||
}
|
||
}
|
||
|
||
func TestRabbitMQDriverDefinitionIsBuiltIn(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("rabbit-mq")
|
||
if !ok {
|
||
t.Fatal("expected rabbitmq driver definition")
|
||
}
|
||
if definition.Name != "RabbitMQ" {
|
||
t.Fatalf("unexpected rabbitmq driver name: %q", definition.Name)
|
||
}
|
||
if !definition.BuiltIn {
|
||
t.Fatal("expected rabbitmq to be a built-in driver")
|
||
}
|
||
if definition.PinnedVersion != "" || definition.DefaultDownloadURL != "" {
|
||
t.Fatalf("expected rabbitmq builtin definition to omit optional-agent metadata: %#v", definition)
|
||
}
|
||
}
|
||
|
||
func TestGoldenDBDriverDefinitionIsBuiltIn(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("greatdb")
|
||
if !ok {
|
||
t.Fatal("expected goldendb driver definition")
|
||
}
|
||
if definition.Name != "GoldenDB" {
|
||
t.Fatalf("unexpected goldendb driver name: %q", definition.Name)
|
||
}
|
||
if !definition.BuiltIn {
|
||
t.Fatal("expected goldendb to be a built-in driver")
|
||
}
|
||
if definition.PinnedVersion != "" || definition.DefaultDownloadURL != "" {
|
||
t.Fatalf("expected goldendb builtin definition to omit optional metadata: %#v", definition)
|
||
}
|
||
if latestDriverVersionMap["goldendb"] != "1.9.3" {
|
||
t.Fatalf("unexpected goldendb pinned version: %q", latestDriverVersionMap["goldendb"])
|
||
}
|
||
if driverGoModulePathMap["goldendb"] != "github.com/go-sql-driver/mysql" {
|
||
t.Fatalf("unexpected goldendb go module path: %q", driverGoModulePathMap["goldendb"])
|
||
}
|
||
}
|
||
|
||
func TestGaussDBDriverDefinitionUsesOptionalAgent(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("gaussdb")
|
||
if !ok {
|
||
t.Fatal("expected gaussdb driver definition")
|
||
}
|
||
if definition.Name != "GaussDB" {
|
||
t.Fatalf("unexpected gaussdb driver name: %q", definition.Name)
|
||
}
|
||
if definition.BuiltIn {
|
||
t.Fatal("expected gaussdb to be an optional driver agent")
|
||
}
|
||
if driverGoModulePathMap["gaussdb"] != "github.com/HuaweiCloudDeveloper/gaussdb-go" {
|
||
t.Fatalf("unexpected gaussdb go module path: %q", driverGoModulePathMap["gaussdb"])
|
||
}
|
||
if definition.PinnedVersion != "v1.0.0-rc1" {
|
||
t.Fatalf("unexpected gaussdb definition pinned version: %q", definition.PinnedVersion)
|
||
}
|
||
if definition.DefaultDownloadURL != "builtin://activate/gaussdb" {
|
||
t.Fatalf("unexpected gaussdb default download URL: %q", definition.DefaultDownloadURL)
|
||
}
|
||
if latestDriverVersionMap["gaussdb"] != "v1.0.0-rc1" {
|
||
t.Fatalf("unexpected gaussdb pinned version: %q", latestDriverVersionMap["gaussdb"])
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("gaussdb", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve gaussdb build tags failed: %v", err)
|
||
}
|
||
if tags != "gonavi_gaussdb_driver" {
|
||
t.Fatalf("unexpected gaussdb build tag: %q", tags)
|
||
}
|
||
}
|
||
|
||
func TestBuildOptionalDriverInstallPlanMessagePrefersDirectThenBundle(t *testing.T) {
|
||
message := buildOptionalDriverInstallPlanMessage(zhCNDriverProgressText(t), "SQL Server", "1.9.6", false, false, false, false, 1, 2)
|
||
if !strings.Contains(message, "先尝试 1 个预编译直链") {
|
||
t.Fatalf("expected direct-download hint, got %q", message)
|
||
}
|
||
if !strings.Contains(message, "失败后转入 2 个驱动总包源") {
|
||
t.Fatalf("expected bundle fallback hint, got %q", message)
|
||
}
|
||
}
|
||
|
||
func TestBuildOptionalDriverFallbackProgressMessageReportsBundleFallback(t *testing.T) {
|
||
message := buildOptionalDriverFallbackProgressMessage(zhCNDriverProgressText(t), "SQL Server", 1, 2, false)
|
||
if !strings.Contains(message, "预编译直链未命中") {
|
||
t.Fatalf("expected direct miss hint, got %q", message)
|
||
}
|
||
if !strings.Contains(message, "转入驱动总包兜底") {
|
||
t.Fatalf("expected bundle fallback hint, got %q", message)
|
||
}
|
||
}
|
||
|
||
func TestOptionalDriverProgressNewCatalogKeysResolve(t *testing.T) {
|
||
text := zhCNDriverProgressText(t)
|
||
plan := buildOptionalDriverInstallPlanMessage(text, "SQL Server", "1.9.6", false, false, true, false, 1, 2)
|
||
if !strings.Contains(plan, "开发态使用本地源码构建") || strings.Contains(plan, "driver_manager.progress.plan.require_source_first") {
|
||
t.Fatalf("expected localized require-source-first plan message, got %q", plan)
|
||
}
|
||
|
||
prebuilt := text("driver_manager.progress.download_prebuilt_package", map[string]any{"name": "SQL Server"})
|
||
if !strings.Contains(prebuilt, "下载预编译 SQL Server 驱动包") {
|
||
t.Fatalf("expected localized prebuilt package progress, got %q", prebuilt)
|
||
}
|
||
|
||
waitBundle := text("driver_manager.progress.wait_bundle", map[string]any{"name": "SQL Server"})
|
||
if !strings.Contains(waitBundle, "等待 SQL Server 驱动总包下载完成") {
|
||
t.Fatalf("expected localized wait bundle progress, got %q", waitBundle)
|
||
}
|
||
}
|
||
|
||
func zhCNDriverProgressText(t *testing.T) func(string, map[string]any) string {
|
||
t.Helper()
|
||
localizer := newAppLocalizer()
|
||
if localizer == nil {
|
||
t.Fatal("expected app localizer")
|
||
}
|
||
localizer.SetLanguage(i18n.LanguageZhCN)
|
||
return localizer.T
|
||
}
|
||
|
||
func TestOptionalDriverAgentProgressMessagesUseLocalizedText(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
|
||
rawMessages := []string{
|
||
`"准备安装 %s 驱动代理`,
|
||
`"预编译直链未命中`,
|
||
`"直链不可用`,
|
||
`"发布资产未命中`,
|
||
`"优先使用本地源码构建 %s 驱动代理"`,
|
||
`"下载预编译 %s 驱动代理"`,
|
||
`"从驱动总包提取 %s 代理"`,
|
||
`"未命中预编译包,尝试开发态本地构建"`,
|
||
`"下载 %s 驱动总包"`,
|
||
`"等待 %s 驱动总包下载完成"`,
|
||
`"解压 %s 驱动代理"`,
|
||
}
|
||
for _, rawMessage := range rawMessages {
|
||
if strings.Contains(source, rawMessage) {
|
||
t.Fatalf("methods_driver.go still contains raw optional driver progress message %s", rawMessage)
|
||
}
|
||
}
|
||
|
||
keys := []string{
|
||
"driver_manager.progress.plan.direct_then_bundle",
|
||
"driver_manager.progress.plan.require_source_first",
|
||
"driver_manager.progress.fallback.direct_to_bundle",
|
||
"driver_manager.progress.source_build_preferred",
|
||
"driver_manager.progress.download_prebuilt_agent",
|
||
"driver_manager.progress.download_prebuilt_package",
|
||
"driver_manager.progress.extract_agent_from_bundle",
|
||
"driver_manager.progress.dev_build_fallback",
|
||
"driver_manager.progress.download_bundle",
|
||
"driver_manager.progress.wait_bundle",
|
||
"driver_manager.progress.unzip_agent",
|
||
}
|
||
for _, key := range keys {
|
||
if !strings.Contains(source, key) {
|
||
t.Fatalf("methods_driver.go does not reference optional driver progress i18n key %q", key)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestDuckDBWindowsBuildUsesDynamicLibraryTag(t *testing.T) {
|
||
if runtime.GOOS != "windows" || runtime.GOARCH != "amd64" {
|
||
t.Skip("DuckDB Windows dynamic library flow only applies on windows/amd64")
|
||
}
|
||
|
||
tags, err := optionalDriverBuildTags("duckdb", "")
|
||
if err != nil {
|
||
t.Fatalf("resolve DuckDB build tags failed: %v", err)
|
||
}
|
||
if !strings.Contains(tags, "gonavi_duckdb_driver") || !strings.Contains(tags, "duckdb_use_lib") {
|
||
t.Fatalf("expected DuckDB Windows build tags to include dynamic library tag, got %q", tags)
|
||
}
|
||
if !shouldPreferSourceBuildBeforeDownload("duckdb", "") {
|
||
t.Fatal("expected DuckDB Windows install to try local dynamic-library build before downloads")
|
||
}
|
||
if !shouldSkipReusableAgentCandidate("duckdb", "") {
|
||
t.Fatal("expected DuckDB Windows install to skip reusable static agent candidates")
|
||
}
|
||
seedReleaseAssetCacheEntry(t, "latest", map[string]int64{
|
||
duckDBWindowsDriverZipAssetName: 19 << 20,
|
||
}, map[string]int64{
|
||
duckDBWindowsDriverZipAssetName: 19 << 20,
|
||
})
|
||
legacyDirectURL := "https://example.com/duckdb-driver-agent-windows-amd64.exe"
|
||
urls := resolveOptionalDriverAgentDownloadURLs(driverDefinition{Type: "duckdb"}, legacyDirectURL, "")
|
||
if len(urls) < 2 {
|
||
t.Fatalf("expected DuckDB Windows install to keep dedicated zip ahead of legacy direct candidate, got %v", urls)
|
||
}
|
||
if urls[0] != driverReleaseLatestDownloadURL(duckDBWindowsDriverZipAssetName) {
|
||
t.Fatalf("expected DuckDB Windows dedicated zip candidate first, got %v", urls)
|
||
}
|
||
if urls[1] != legacyDirectURL {
|
||
t.Fatalf("expected DuckDB Windows to keep legacy direct candidate after dedicated zip, got %v", urls)
|
||
}
|
||
}
|
||
|
||
func TestDuckDBWindowsDynamicLibraryCGOLDFlagsIncludeSupportLibraries(t *testing.T) {
|
||
flags := duckDBWindowsDynamicLibraryCGOLDFlags(`C:\tmp\duckdb lib`)
|
||
for _, expected := range []string{
|
||
`-LC:/tmp/duckdb lib`,
|
||
"-lduckdb",
|
||
"-lstdc++",
|
||
"-lm",
|
||
"-lws2_32",
|
||
"-lwsock32",
|
||
"-lrstrtmgr",
|
||
} {
|
||
if !strings.Contains(flags, expected) {
|
||
t.Fatalf("expected flags %q to contain %q", flags, expected)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestInstallOptionalDriverAgentFromLocalZipExtractsDuckDBDLL(t *testing.T) {
|
||
if runtime.GOOS != "windows" || runtime.GOARCH != "amd64" {
|
||
t.Skip("DuckDB DLL support file is only required on windows/amd64")
|
||
}
|
||
|
||
tmpDir := t.TempDir()
|
||
zipPath := filepath.Join(tmpDir, "duckdb-driver.zip")
|
||
zipFile, err := os.Create(zipPath)
|
||
if err != nil {
|
||
t.Fatalf("create zip failed: %v", err)
|
||
}
|
||
zw := zip.NewWriter(zipFile)
|
||
for name, content := range map[string]string{
|
||
"Windows/duckdb-driver-agent-windows-amd64.exe": "agent",
|
||
"Windows/duckdb.dll": "dll",
|
||
} {
|
||
w, err := zw.Create(name)
|
||
if err != nil {
|
||
t.Fatalf("create zip entry %s failed: %v", name, err)
|
||
}
|
||
if _, err := w.Write([]byte(content)); err != nil {
|
||
t.Fatalf("write zip entry %s failed: %v", name, err)
|
||
}
|
||
}
|
||
if err := zw.Close(); err != nil {
|
||
t.Fatalf("close zip writer failed: %v", err)
|
||
}
|
||
if err := zipFile.Close(); err != nil {
|
||
t.Fatalf("close zip file failed: %v", err)
|
||
}
|
||
|
||
target := filepath.Join(tmpDir, "install", "duckdb-driver-agent.exe")
|
||
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
||
t.Fatalf("create install dir failed: %v", err)
|
||
}
|
||
entryName, err := installOptionalDriverAgentFromLocalZip(zipPath, driverDefinition{Type: "duckdb", Name: "DuckDB"}, target, "")
|
||
if err != nil {
|
||
t.Fatalf("install local DuckDB zip failed: %v", err)
|
||
}
|
||
if entryName != "Windows/duckdb-driver-agent-windows-amd64.exe" {
|
||
t.Fatalf("unexpected extracted agent entry: %q", entryName)
|
||
}
|
||
dllBytes, err := os.ReadFile(filepath.Join(filepath.Dir(target), "duckdb.dll"))
|
||
if err != nil {
|
||
t.Fatalf("expected duckdb.dll to be extracted: %v", err)
|
||
}
|
||
if string(dllBytes) != "dll" {
|
||
t.Fatalf("unexpected duckdb.dll content: %q", string(dllBytes))
|
||
}
|
||
}
|
||
|
||
func TestDownloadOptionalDriverAgentBinaryInstallsDuckDBDedicatedZip(t *testing.T) {
|
||
if runtime.GOOS != "windows" || runtime.GOARCH != "amd64" {
|
||
t.Skip("DuckDB dedicated zip flow only applies on windows/amd64")
|
||
}
|
||
|
||
originalValidateFunc := validateOptionalDriverAgentExecutableFunc
|
||
validateOptionalDriverAgentExecutableFunc = func(driverType string, executablePath string) error {
|
||
return nil
|
||
}
|
||
t.Cleanup(func() {
|
||
validateOptionalDriverAgentExecutableFunc = originalValidateFunc
|
||
})
|
||
|
||
tmpDir := t.TempDir()
|
||
zipPath := filepath.Join(tmpDir, duckDBWindowsDriverZipAssetName)
|
||
zipFile, err := os.Create(zipPath)
|
||
if err != nil {
|
||
t.Fatalf("create zip failed: %v", err)
|
||
}
|
||
zw := zip.NewWriter(zipFile)
|
||
for name, content := range map[string]string{
|
||
"Windows/duckdb-driver-agent-windows-amd64.exe": "agent",
|
||
"Windows/duckdb.dll": "dll",
|
||
} {
|
||
w, err := zw.Create(name)
|
||
if err != nil {
|
||
t.Fatalf("create zip entry %s failed: %v", name, err)
|
||
}
|
||
if _, err := w.Write([]byte(content)); err != nil {
|
||
t.Fatalf("write zip entry %s failed: %v", name, err)
|
||
}
|
||
}
|
||
if err := zw.Close(); err != nil {
|
||
t.Fatalf("close zip writer failed: %v", err)
|
||
}
|
||
if err := zipFile.Close(); err != nil {
|
||
t.Fatalf("close zip file failed: %v", err)
|
||
}
|
||
|
||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
http.ServeFile(w, r, zipPath)
|
||
}))
|
||
defer server.Close()
|
||
proxySnapshot := currentGlobalProxyConfig()
|
||
if _, err := setGlobalProxyConfig(false, proxySnapshot.Proxy); err != nil {
|
||
t.Fatalf("disable global proxy failed: %v", err)
|
||
}
|
||
t.Cleanup(func() {
|
||
_, _ = setGlobalProxyConfig(proxySnapshot.Enabled, proxySnapshot.Proxy)
|
||
})
|
||
|
||
target := filepath.Join(tmpDir, "install", "duckdb-driver-agent.exe")
|
||
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
||
t.Fatalf("create install dir failed: %v", err)
|
||
}
|
||
|
||
hash, err := downloadOptionalDriverAgentBinary(nil, driverDefinition{Type: "duckdb", Name: "DuckDB"}, server.URL+"/"+duckDBWindowsDriverZipAssetName+"?source=release", target)
|
||
if err != nil {
|
||
t.Fatalf("download dedicated zip failed: %v", err)
|
||
}
|
||
if strings.TrimSpace(hash) == "" {
|
||
t.Fatal("expected hash for installed duckdb agent")
|
||
}
|
||
if _, err := os.Stat(target); err != nil {
|
||
t.Fatalf("expected duckdb agent to be installed: %v", err)
|
||
}
|
||
dllBytes, err := os.ReadFile(filepath.Join(filepath.Dir(target), "duckdb.dll"))
|
||
if err != nil {
|
||
t.Fatalf("expected duckdb.dll to be installed: %v", err)
|
||
}
|
||
if string(dllBytes) != "dll" {
|
||
t.Fatalf("unexpected duckdb.dll content: %q", string(dllBytes))
|
||
}
|
||
}
|
||
|
||
func TestOptionalDriverDownloadZipURLAcceptsQueryString(t *testing.T) {
|
||
if !isOptionalDriverDownloadZipURL("https://example.com/duckdb-driver.zip?token=abc") {
|
||
t.Fatal("expected signed zip URL to be treated as zip download")
|
||
}
|
||
if isOptionalDriverDownloadZipURL("https://example.com/duckdb-driver-agent.exe?token=abc") {
|
||
t.Fatal("expected exe URL with query to remain non-zip download")
|
||
}
|
||
}
|
||
|
||
func TestDownloadDriverPackageRejectsUnsupportedMongoVersion(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
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, "only 1.17.x and 2.x are supported") {
|
||
t.Fatalf("expected English support-range error, got %q", result.Message)
|
||
}
|
||
if strings.Contains(result.Message, "仅支持 1.17.x 和 2.x") {
|
||
t.Fatalf("expected localized wrapper instead of fixed Chinese, got %q", result.Message)
|
||
}
|
||
}
|
||
|
||
func TestDownloadDriverPackageUsesCurrentLanguageForBuiltinWrapper(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
result := app.DownloadDriverPackage("mysql", "", "", t.TempDir())
|
||
if result.Success {
|
||
t.Fatal("expected built-in driver download to be rejected")
|
||
}
|
||
if !strings.Contains(result.Message, "Built-in drivers do not need extension package downloads") {
|
||
t.Fatalf("expected English built-in wrapper, got %q", result.Message)
|
||
}
|
||
if strings.Contains(result.Message, "内置驱动无需下载扩展包") {
|
||
t.Fatalf("expected localized wrapper instead of fixed Chinese, got %q", result.Message)
|
||
}
|
||
}
|
||
|
||
func TestBuildDriverVersionDisplayLabelUsesLocalizedText(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
label := buildDriverVersionDisplayLabel(driverVersionOptionItem{
|
||
Source: "latest",
|
||
Recommended: true,
|
||
}, app.appText)
|
||
if got, want := label, "Unlabeled version (latest) (recommended)"; got != want {
|
||
t.Fatalf("expected localized English display label %q, got %q", want, got)
|
||
}
|
||
if strings.Contains(label, "未标注版本") || strings.Contains(label, "(最新)") || strings.Contains(label, "(推荐)") {
|
||
t.Fatalf("expected localized display label instead of fixed Chinese, got %q", label)
|
||
}
|
||
|
||
rawVersionLabel := buildDriverVersionDisplayLabel(driverVersionOptionItem{
|
||
Version: "v1.2.3",
|
||
Source: "latest",
|
||
}, app.appText)
|
||
if got, want := rawVersionLabel, "v1.2.3 (latest)"; got != want {
|
||
t.Fatalf("expected raw version with localized suffix %q, got %q", want, got)
|
||
}
|
||
}
|
||
|
||
func TestResolveDriverPackageSizeTextUsesLocalizedStatusText(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
builtInText := resolveDriverPackageSizeText(driverDefinition{BuiltIn: true}, installedDriverPackage{}, false, nil, app.appText)
|
||
if got, want := builtInText, "Built-in"; got != want {
|
||
t.Fatalf("expected localized built-in package size text %q, got %q", want, got)
|
||
}
|
||
if strings.Contains(builtInText, "内置") {
|
||
t.Fatalf("expected localized built-in package size text instead of fixed Chinese, got %q", builtInText)
|
||
}
|
||
|
||
pendingText := resolveDriverPackageSizeText(driverDefinition{Type: "jdbc-only-test"}, installedDriverPackage{}, false, map[string]int64{}, app.appText)
|
||
if got, want := pendingText, "Pending release"; got != want {
|
||
t.Fatalf("expected localized pending-release package size text %q, got %q", want, got)
|
||
}
|
||
if strings.Contains(pendingText, "待发布") {
|
||
t.Fatalf("expected localized pending-release package size text instead of fixed Chinese, got %q", pendingText)
|
||
}
|
||
|
||
sizeText := resolveDriverPackageSizeText(driverDefinition{Type: "mariadb"}, installedDriverPackage{}, false, map[string]int64{
|
||
"mariadb": 12 * 1024 * 1024,
|
||
}, app.appText)
|
||
if got, want := sizeText, "12.00 MB"; got != want {
|
||
t.Fatalf("expected raw size text %q to stay unchanged, got %q", want, got)
|
||
}
|
||
}
|
||
|
||
func TestDriverVersionDisplayLabelsAndPackageSizeStatusesUseI18nKeys(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
|
||
if !strings.Contains(source, "resolveDriverVersionOptions(definition, repositoryURL, a.appText)") {
|
||
t.Fatal("expected GetDriverVersionList to pass app localizer into resolveDriverVersionOptions")
|
||
}
|
||
if !strings.Contains(source, "buildDriverVersionDisplayLabel(option, text)") {
|
||
t.Fatal("expected resolveDriverVersionOptions to pass localizer into buildDriverVersionDisplayLabel")
|
||
}
|
||
if !strings.Contains(source, "resolveDriverPackageSizeText(definition, pkg, packageMetaExists, packageSizeBytesMap, a.appText)") {
|
||
t.Fatal("expected GetDriverStatusList to pass app localizer into resolveDriverPackageSizeText")
|
||
}
|
||
|
||
buildStart := strings.Index(source, "func buildDriverVersionDisplayLabel")
|
||
if buildStart < 0 {
|
||
t.Fatal("methods_driver.go missing buildDriverVersionDisplayLabel")
|
||
}
|
||
buildRest := source[buildStart:]
|
||
buildEnd := strings.Index(buildRest, "\nfunc resolveRecentDriverVersionOptions")
|
||
if buildEnd < 0 {
|
||
t.Fatal("buildDriverVersionDisplayLabel function boundary not found")
|
||
}
|
||
buildSource := buildRest[:buildEnd]
|
||
|
||
for _, rawText := range []string{`"未标注版本"`, `"(最新)"`, `"(推荐)"`} {
|
||
if strings.Contains(buildSource, rawText) {
|
||
t.Fatalf("buildDriverVersionDisplayLabel still contains raw display text %s", rawText)
|
||
}
|
||
}
|
||
for _, key := range []string{
|
||
"driver_manager.version.unlabeled",
|
||
"driver_manager.version.latest_suffix",
|
||
"driver_manager.version.recommended_suffix",
|
||
} {
|
||
if !strings.Contains(buildSource, key) {
|
||
t.Fatalf("buildDriverVersionDisplayLabel does not reference i18n key %q", key)
|
||
}
|
||
}
|
||
|
||
sizeStart := strings.Index(source, "func resolveDriverPackageSizeText")
|
||
if sizeStart < 0 {
|
||
t.Fatal("methods_driver.go missing resolveDriverPackageSizeText")
|
||
}
|
||
sizeRest := source[sizeStart:]
|
||
sizeEnd := strings.Index(sizeRest, "\nfunc readInstalledPackageSizeBytes")
|
||
if sizeEnd < 0 {
|
||
t.Fatal("resolveDriverPackageSizeText function boundary not found")
|
||
}
|
||
sizeSource := sizeRest[:sizeEnd]
|
||
|
||
for _, rawText := range []string{`"内置"`, `"待发布"`} {
|
||
if strings.Contains(sizeSource, rawText) {
|
||
t.Fatalf("resolveDriverPackageSizeText still contains raw status text %s", rawText)
|
||
}
|
||
}
|
||
for _, key := range []string{
|
||
"driver_manager.package_size.built_in",
|
||
"driver_manager.package_size.pending_release",
|
||
} {
|
||
if !strings.Contains(sizeSource, key) {
|
||
t.Fatalf("resolveDriverPackageSizeText does not reference i18n key %q", key)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestLocalizedDriverNeedsUpdateTextsUseCurrentLanguage(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
reason, message := app.localizedDriverNeedsUpdateTexts("rev-old", "rev-new", 3)
|
||
if !strings.Contains(reason, "Reinstall required to apply driver updates.") {
|
||
t.Fatalf("expected English update reason, got %q", reason)
|
||
}
|
||
if !strings.Contains(reason, "installed revision rev-old.") || !strings.Contains(reason, "expected revision rev-new.") {
|
||
t.Fatalf("expected English revision detail, got %q", reason)
|
||
}
|
||
if !strings.Contains(message, "Affects 3 saved connections") {
|
||
t.Fatalf("expected affected-connections detail, got %q", message)
|
||
}
|
||
if strings.Contains(reason, "需要重装") || strings.Contains(message, "已保存连接") {
|
||
t.Fatalf("expected localized update texts instead of fixed Chinese, got reason=%q message=%q", reason, message)
|
||
}
|
||
}
|
||
|
||
func TestLocalizeDriverSelectionErrorUsesCurrentLanguageForSlimBuild(t *testing.T) {
|
||
app := NewApp()
|
||
app.SetLanguage("en-US")
|
||
|
||
definition := driverDefinition{Type: "clickhouse", Name: "ClickHouse"}
|
||
err := app.localizeDriverSelectionError(definition, &driverBuildUnavailableError{Name: "ClickHouse"})
|
||
if err == nil {
|
||
t.Fatal("expected localized slim-build error")
|
||
}
|
||
if !strings.Contains(err.Error(), "ClickHouse is not included in the current slim build") {
|
||
t.Fatalf("expected English slim-build wrapper, got %q", err.Error())
|
||
}
|
||
if strings.Contains(err.Error(), "当前发行包为精简构建") {
|
||
t.Fatalf("expected localized wrapper instead of fixed Chinese, got %q", err.Error())
|
||
}
|
||
}
|
||
|
||
func TestResolveRecentDriverVersionMetasIncludesHistoricalTDengineVersionsFromCache(t *testing.T) {
|
||
seedGoModuleVersionCache(t, "github.com/taosdata/driver-go/v3", []string{
|
||
"3.8.0",
|
||
"3.7.8",
|
||
"3.7.7",
|
||
"3.7.6",
|
||
"3.7.5",
|
||
"3.7.4",
|
||
"3.7.3",
|
||
"3.7.2",
|
||
"3.7.1",
|
||
"3.7.0",
|
||
"3.6.0",
|
||
"3.5.8",
|
||
"3.5.7",
|
||
"3.5.6",
|
||
"3.5.5",
|
||
"3.5.4",
|
||
"3.5.3",
|
||
"3.5.2",
|
||
"3.5.1",
|
||
"3.5.0",
|
||
"3.3.1",
|
||
"3.1.0",
|
||
"3.0.4",
|
||
"3.0.3",
|
||
"3.0.2",
|
||
"3.0.1",
|
||
"3.0.0",
|
||
})
|
||
|
||
metas := resolveRecentDriverVersionMetas("tdengine", driverRecentVersionLimit)
|
||
versions := make([]string, 0, len(metas))
|
||
for _, meta := range metas {
|
||
versions = append(versions, meta.Version)
|
||
}
|
||
|
||
if !containsVersion(versions, "3.5.8") {
|
||
t.Fatalf("expected tdengine historical version 3.5.8 to remain selectable, got %v", versions)
|
||
}
|
||
if !containsVersion(versions, "3.3.1") {
|
||
t.Fatalf("expected tdengine historical version 3.3.1 to remain selectable, got %v", versions)
|
||
}
|
||
}
|
||
|
||
func TestResolveRecentDriverVersionMetasFallsBackToHistoricalTDengineMatrix(t *testing.T) {
|
||
driverModuleVersionMu.Lock()
|
||
original := driverModuleVersionMap
|
||
driverModuleVersionMap = map[string]goModuleVersionListCacheEntry{}
|
||
driverModuleVersionMu.Unlock()
|
||
t.Cleanup(func() {
|
||
driverModuleVersionMu.Lock()
|
||
driverModuleVersionMap = original
|
||
driverModuleVersionMu.Unlock()
|
||
})
|
||
|
||
metas := resolveRecentDriverVersionMetas("tdengine", driverRecentVersionLimit)
|
||
versions := make([]string, 0, len(metas))
|
||
for _, meta := range metas {
|
||
versions = append(versions, meta.Version)
|
||
}
|
||
|
||
if !containsVersion(versions, "3.5.8") {
|
||
t.Fatalf("expected tdengine fallback list to include 3.5.8, got %v", versions)
|
||
}
|
||
if !containsVersion(versions, "3.3.1") {
|
||
t.Fatalf("expected tdengine fallback list to include 3.3.1, got %v", versions)
|
||
}
|
||
}
|
||
|
||
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 try published assets before source build")
|
||
}
|
||
|
||
explicitURL := driverReleaseDownloadURL("v1.17.4", 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 TestShouldPreferSourceBuildBeforeDownloadDoesNotPreferKingbase(t *testing.T) {
|
||
if shouldPreferSourceBuildBeforeDownload("kingbase", "0.0.0-20201021123113-29bd62a876c3") {
|
||
t.Fatal("expected kingbase release install not to prefer source build before download")
|
||
}
|
||
}
|
||
|
||
func TestShouldPreferSourceBuildBeforeDownloadForDevelopmentBuild(t *testing.T) {
|
||
if shouldPreferSourceBuildBeforeDownloadForBuildType("dev", "mariadb", "1.9.3") {
|
||
t.Fatal("expected development release build to prefer published MariaDB driver-agent before source fallback")
|
||
}
|
||
if shouldPreferSourceBuildBeforeDownloadForBuildType("development", "clickhouse", "2.43.1") && !shouldUseDuckDBWindowsDynamicLibrary("clickhouse") {
|
||
t.Fatal("expected development build alias not to prefer source build for ClickHouse")
|
||
}
|
||
if shouldPreferSourceBuildBeforeDownloadForBuildType("production", "mariadb", "1.9.3") {
|
||
t.Fatal("expected production build not to prefer source build for MariaDB")
|
||
}
|
||
if shouldPreferSourceBuildBeforeDownloadForBuildType("dev", "mysql", "") {
|
||
t.Fatal("expected built-in drivers not to prefer optional driver-agent source build")
|
||
}
|
||
}
|
||
|
||
func TestShouldRequireSourceBuildBeforeDownloadForDevelopmentBuild(t *testing.T) {
|
||
if shouldRequireSourceBuildBeforeDownloadForBuildType("dev", "duckdb", "2.5.6") {
|
||
t.Fatal("expected development build to allow DuckDB release bundle fallback after local build failure")
|
||
}
|
||
if shouldUseDuckDBWindowsDynamicLibrary("duckdb") {
|
||
if !shouldPreferSourceBuildBeforeDownloadForBuildType("dev", "duckdb", "2.5.6") {
|
||
t.Fatal("expected DuckDB Windows dynamic-library install to prefer local source build before bundle fallback")
|
||
}
|
||
} else if shouldPreferSourceBuildBeforeDownloadForBuildType("dev", "duckdb", "2.5.6") {
|
||
t.Fatal("expected development build not to prefer DuckDB source build on non-Windows dynamic-library platforms")
|
||
}
|
||
if shouldRequireSourceBuildBeforeDownloadForBuildType("development", "mariadb", "1.9.3") {
|
||
t.Fatal("expected development build alias to allow published MariaDB driver-agent fallback")
|
||
}
|
||
if shouldRequireSourceBuildBeforeDownloadForBuildType("production", "duckdb", "2.5.6") {
|
||
t.Fatal("expected production build to allow DuckDB release bundle fallback")
|
||
}
|
||
if shouldRequireSourceBuildBeforeDownloadForBuildType("dev", "mysql", "") {
|
||
t.Fatal("expected built-in drivers not to require optional driver-agent source build")
|
||
}
|
||
}
|
||
|
||
func TestOptionalDriverInstallTimeoutsStayBounded(t *testing.T) {
|
||
if optionalDriverBundleDownloadTimeout > 15*time.Minute {
|
||
t.Fatalf("driver bundle download timeout should stay bounded, got %s", optionalDriverBundleDownloadTimeout)
|
||
}
|
||
if optionalDriverSourceBuildTimeout > 8*time.Minute {
|
||
t.Fatalf("driver source build timeout should stay bounded, got %s", optionalDriverSourceBuildTimeout)
|
||
}
|
||
}
|
||
|
||
func TestResolveDuckDBWindowsCGOToolchainBinFromCandidates(t *testing.T) {
|
||
binDir := t.TempDir()
|
||
writeSelfExecutable(t, filepath.Join(binDir, "gcc.exe"))
|
||
writeSelfExecutable(t, filepath.Join(binDir, "g++.exe"))
|
||
|
||
got, err := resolveDuckDBWindowsCGOToolchainBinFromCandidates([]string{
|
||
filepath.Join(t.TempDir(), "missing"),
|
||
binDir,
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("expected toolchain bin to resolve: %v", err)
|
||
}
|
||
if got != filepath.Clean(binDir) {
|
||
t.Fatalf("expected %q, got %q", filepath.Clean(binDir), got)
|
||
}
|
||
}
|
||
|
||
func TestPrependPathEnvUsesCurrentEnvPath(t *testing.T) {
|
||
basePath := "base-path"
|
||
firstPath := "first-path"
|
||
secondPath := "second-path"
|
||
env := []string{"PATH=" + basePath}
|
||
env = prependPathEnv(env, firstPath)
|
||
env = prependPathEnv(env, secondPath)
|
||
|
||
got := envValue(env, "PATH")
|
||
want := strings.Join([]string{secondPath, firstPath, basePath}, string(os.PathListSeparator))
|
||
if got != want {
|
||
t.Fatalf("expected PATH %q, got %q", want, got)
|
||
}
|
||
}
|
||
|
||
func TestResolveOptionalDriverAgentDownloadURLsIncludesPublishedKingbaseAsset(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("kingbase")
|
||
if !ok {
|
||
t.Fatal("expected kingbase driver definition")
|
||
}
|
||
|
||
version := normalizeVersion(definition.PinnedVersion)
|
||
assetName := optionalDriverReleaseAssetNameForVersion("kingbase", version)
|
||
publishedAssets := map[string]int64{
|
||
assetName: 18 << 20,
|
||
}
|
||
seedReleaseAssetCacheEntry(t, "tag:v"+version, publishedAssets, publishedAssets)
|
||
seedReleaseAssetCacheEntry(t, "latest", publishedAssets, publishedAssets)
|
||
|
||
urls := resolveOptionalDriverAgentDownloadURLs(definition, "builtin://activate/kingbase", version)
|
||
if len(urls) == 0 {
|
||
t.Fatal("expected kingbase pinned install to include published download candidates")
|
||
}
|
||
|
||
if !strings.Contains(urls[0], assetName) {
|
||
t.Fatalf("expected first kingbase download URL to contain %q, got %q", assetName, urls[0])
|
||
}
|
||
}
|
||
|
||
func TestInstallOptionalDriverAgentFromLocalPathSupportsMongoV1DirectoryImport(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mongodb")
|
||
if !ok {
|
||
t.Fatal("expected mongodb driver definition")
|
||
}
|
||
|
||
packageRoot := t.TempDir()
|
||
platformDir := filepath.Join(packageRoot, optionalDriverBundlePlatformDir(runtime.GOOS))
|
||
if err := os.MkdirAll(platformDir, 0o755); err != nil {
|
||
t.Fatalf("mkdir package dir failed: %v", err)
|
||
}
|
||
|
||
assetName := mongoVersionedReleaseAssetName(1)
|
||
writeSelfExecutable(t, filepath.Join(platformDir, assetName))
|
||
|
||
installRoot := filepath.Join(t.TempDir(), "drivers")
|
||
meta, err := installOptionalDriverAgentFromLocalPath(definition, packageRoot, installRoot, "1.17.4")
|
||
if err != nil {
|
||
t.Fatalf("expected mongodb v1 directory import to succeed, got %v", err)
|
||
}
|
||
if meta.Version != "1.17.4" {
|
||
t.Fatalf("expected imported version to stay 1.17.4, got %q", meta.Version)
|
||
}
|
||
if filepath.Base(meta.FilePath) != assetName {
|
||
t.Fatalf("expected source file %q, got %q", assetName, meta.FilePath)
|
||
}
|
||
if !strings.Contains(meta.DownloadURL, assetName) {
|
||
t.Fatalf("expected download source to reference %q, got %q", assetName, meta.DownloadURL)
|
||
}
|
||
if _, err := os.Stat(meta.ExecutablePath); err != nil {
|
||
t.Fatalf("expected imported executable to exist, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestInstallOptionalDriverAgentFromLocalPathSupportsMongoV1ZipImport(t *testing.T) {
|
||
definition, ok := resolveDriverDefinition("mongodb")
|
||
if !ok {
|
||
t.Fatal("expected mongodb driver definition")
|
||
}
|
||
|
||
assetName := mongoVersionedReleaseAssetName(1)
|
||
zipPath := filepath.Join(t.TempDir(), "mongodb-v1.zip")
|
||
writeZipWithSelfExecutable(t, zipPath, filepath.ToSlash(filepath.Join(optionalDriverBundlePlatformDir(runtime.GOOS), assetName)))
|
||
|
||
installRoot := filepath.Join(t.TempDir(), "drivers")
|
||
meta, err := installOptionalDriverAgentFromLocalPath(definition, zipPath, installRoot, "1.17.4")
|
||
if err != nil {
|
||
t.Fatalf("expected mongodb v1 zip import to succeed, got %v", err)
|
||
}
|
||
if meta.Version != "1.17.4" {
|
||
t.Fatalf("expected imported version to stay 1.17.4, got %q", meta.Version)
|
||
}
|
||
if !strings.Contains(meta.DownloadURL, assetName) {
|
||
t.Fatalf("expected zip download source to reference %q, got %q", assetName, meta.DownloadURL)
|
||
}
|
||
if _, err := os.Stat(meta.ExecutablePath); err != nil {
|
||
t.Fatalf("expected imported executable to exist, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestDownloadOptionalDriverAgentFromBundleSharesConcurrentDownload(t *testing.T) {
|
||
resetOptionalDriverBundleDownloadCacheForTest(t)
|
||
proxySnapshot := currentGlobalProxyConfig()
|
||
if _, err := setGlobalProxyConfig(false, proxySnapshot.Proxy); err != nil {
|
||
t.Fatalf("disable global proxy failed: %v", err)
|
||
}
|
||
t.Cleanup(func() {
|
||
_, _ = setGlobalProxyConfig(proxySnapshot.Enabled, proxySnapshot.Proxy)
|
||
})
|
||
|
||
bundlePath := filepath.Join(t.TempDir(), "GoNavi-DriverAgents.zip")
|
||
writeZipWithSelfExecutableEntries(t, bundlePath, []string{
|
||
optionalDriverBundleEntryPath("clickhouse"),
|
||
optionalDriverBundleEntryPath("mongodb"),
|
||
})
|
||
|
||
var requestCount int32
|
||
releaseDownload := make(chan struct{})
|
||
var releaseOnce sync.Once
|
||
release := func() {
|
||
releaseOnce.Do(func() {
|
||
close(releaseDownload)
|
||
})
|
||
}
|
||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
atomic.AddInt32(&requestCount, 1)
|
||
<-releaseDownload
|
||
http.ServeFile(w, r, bundlePath)
|
||
}))
|
||
defer server.Close()
|
||
defer release()
|
||
|
||
errCh := make(chan error, 2)
|
||
clickhouseTarget := filepath.Join(t.TempDir(), optionalDriverExecutableBaseName("clickhouse"))
|
||
mongodbTarget := filepath.Join(t.TempDir(), optionalDriverExecutableBaseName("mongodb"))
|
||
go func() {
|
||
_, _, err := downloadOptionalDriverAgentFromBundle(
|
||
nil,
|
||
driverDefinition{Type: "clickhouse", Name: "ClickHouse"},
|
||
server.URL,
|
||
clickhouseTarget,
|
||
)
|
||
errCh <- err
|
||
}()
|
||
|
||
deadline := time.Now().Add(2 * time.Second)
|
||
for atomic.LoadInt32(&requestCount) == 0 && time.Now().Before(deadline) {
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
if atomic.LoadInt32(&requestCount) != 1 {
|
||
t.Fatalf("expected first bundle request to start, got %d", atomic.LoadInt32(&requestCount))
|
||
}
|
||
|
||
go func() {
|
||
_, _, err := downloadOptionalDriverAgentFromBundle(
|
||
nil,
|
||
driverDefinition{Type: "mongodb", Name: "MongoDB"},
|
||
server.URL,
|
||
mongodbTarget,
|
||
)
|
||
errCh <- err
|
||
}()
|
||
|
||
time.Sleep(100 * time.Millisecond)
|
||
if got := atomic.LoadInt32(&requestCount); got != 1 {
|
||
t.Fatalf("expected concurrent bundle install to wait for first download, got %d requests", got)
|
||
}
|
||
release()
|
||
|
||
for i := 0; i < 2; i++ {
|
||
if err := <-errCh; err != nil {
|
||
t.Fatalf("bundle install failed: %v", err)
|
||
}
|
||
}
|
||
if got := atomic.LoadInt32(&requestCount); got != 1 {
|
||
t.Fatalf("expected one shared bundle download, got %d requests", got)
|
||
}
|
||
}
|
||
|
||
func TestDownloadOptionalDriverAgentFromBundleLocalizesInvalidBundleDetail(t *testing.T) {
|
||
resetOptionalDriverBundleDownloadCacheForTest(t)
|
||
proxySnapshot := currentGlobalProxyConfig()
|
||
if _, err := setGlobalProxyConfig(false, proxySnapshot.Proxy); err != nil {
|
||
t.Fatalf("disable global proxy failed: %v", err)
|
||
}
|
||
t.Cleanup(func() {
|
||
_, _ = setGlobalProxyConfig(proxySnapshot.Enabled, proxySnapshot.Proxy)
|
||
})
|
||
|
||
app := NewApp()
|
||
app.SetLanguage(string(i18n.LanguageEnUS))
|
||
|
||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
_, _ = w.Write([]byte("not-a-zip"))
|
||
}))
|
||
defer server.Close()
|
||
|
||
target := filepath.Join(t.TempDir(), optionalDriverExecutableBaseName("clickhouse"))
|
||
_, _, err := downloadOptionalDriverAgentFromBundle(
|
||
app,
|
||
driverDefinition{Type: "clickhouse", Name: "ClickHouse"},
|
||
server.URL,
|
||
target,
|
||
)
|
||
if err == nil {
|
||
t.Fatal("expected invalid bundle to fail")
|
||
}
|
||
|
||
message := localizedDriverBackendErrorMessage(app, err)
|
||
if !strings.Contains(message, "Failed to download driver bundle:") {
|
||
t.Fatalf("expected English bundle wrapper, got %q", message)
|
||
}
|
||
if !strings.Contains(message, "open driver bundle failed") {
|
||
t.Fatalf("expected English internal bundle-open detail, got %q", message)
|
||
}
|
||
if strings.Contains(message, "打开驱动总包失败") {
|
||
t.Fatalf("expected no Chinese internal bundle-open detail in en-US mode, got %q", message)
|
||
}
|
||
}
|
||
|
||
func TestInstallOptionalDriverAgentPackageAcceptsStaleDownloadRevision(t *testing.T) {
|
||
originalProbe := optionalDriverAgentMetadataProbe
|
||
t.Cleanup(func() {
|
||
optionalDriverAgentMetadataProbe = originalProbe
|
||
})
|
||
|
||
tmpDir := t.TempDir()
|
||
staleAgent := filepath.Join(tmpDir, "stale-driver-agent")
|
||
if runtime.GOOS == "windows" {
|
||
staleAgent += ".exe"
|
||
}
|
||
writeSelfExecutable(t, staleAgent)
|
||
|
||
staleServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
http.ServeFile(w, r, staleAgent)
|
||
}))
|
||
defer staleServer.Close()
|
||
proxySnapshot := currentGlobalProxyConfig()
|
||
if _, err := setGlobalProxyConfig(false, proxySnapshot.Proxy); err != nil {
|
||
t.Fatalf("disable global proxy failed: %v", err)
|
||
}
|
||
t.Cleanup(func() {
|
||
_, _ = setGlobalProxyConfig(proxySnapshot.Enabled, proxySnapshot.Proxy)
|
||
})
|
||
|
||
optionalDriverAgentMetadataProbe = func(driverType string, executablePath string) (db.OptionalDriverAgentMetadata, error) {
|
||
return db.OptionalDriverAgentMetadata{
|
||
DriverType: driverType,
|
||
AgentRevision: "src-stale-agent",
|
||
}, nil
|
||
}
|
||
|
||
meta, err := installOptionalDriverAgentPackage(
|
||
nil,
|
||
driverDefinition{Type: "sqlserver", Name: "SQL Server"},
|
||
"1.9.6",
|
||
filepath.Join(tmpDir, "drivers"),
|
||
staleServer.URL,
|
||
)
|
||
if err != nil {
|
||
t.Fatalf("expected stale direct download to be installed with an update hint, got %v", err)
|
||
}
|
||
if meta.DownloadURL != staleServer.URL {
|
||
t.Fatalf("expected direct download source to be preserved, got %q", meta.DownloadURL)
|
||
}
|
||
if meta.AgentRevision != "src-stale-agent" {
|
||
t.Fatalf("expected stale agent revision to be recorded, got %q", meta.AgentRevision)
|
||
}
|
||
if _, err := os.Stat(meta.ExecutablePath); err != nil {
|
||
t.Fatalf("expected runtime executable to stay installed, got %v", err)
|
||
}
|
||
needsUpdate, reason, expectedRevision := optionalDriverAgentRevisionStatus("sqlserver", meta, true)
|
||
if !needsUpdate {
|
||
t.Fatalf("expected stale installed revision to be surfaced as needsUpdate; expected=%q", expectedRevision)
|
||
}
|
||
if !strings.Contains(reason, "强烈建议重装") {
|
||
t.Fatalf("expected advisory reinstall reason, got %q", reason)
|
||
}
|
||
}
|
||
|
||
func seedReleaseAssetSizeCache(t *testing.T, cacheKey string, sizeByKey map[string]int64) {
|
||
t.Helper()
|
||
|
||
seedReleaseAssetCacheEntry(t, cacheKey, sizeByKey, sizeByKey)
|
||
}
|
||
|
||
func seedReleaseAssetCacheEntry(t *testing.T, cacheKey string, sizeByKey map[string]int64, publishedAssets map[string]int64) {
|
||
t.Helper()
|
||
|
||
driverReleaseSizeMu.Lock()
|
||
original := cloneReleaseAssetSizeCache(driverReleaseSizeMap)
|
||
driverReleaseSizeMap[cacheKey] = driverReleaseAssetSizeCacheEntry{
|
||
LoadedAt: time.Now(),
|
||
SizeByKey: cloneInt64Map(sizeByKey),
|
||
PublishedAssets: cloneBoolMapFromSizes(publishedAssets),
|
||
}
|
||
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),
|
||
PublishedAssets: cloneBoolMap(value.PublishedAssets),
|
||
Err: value.Err,
|
||
}
|
||
}
|
||
return cloned
|
||
}
|
||
|
||
func cloneBoolMap(src map[string]bool) map[string]bool {
|
||
if len(src) == 0 {
|
||
return map[string]bool{}
|
||
}
|
||
cloned := make(map[string]bool, len(src))
|
||
for key, value := range src {
|
||
cloned[key] = value
|
||
}
|
||
return cloned
|
||
}
|
||
|
||
func cloneBoolMapFromSizes(src map[string]int64) map[string]bool {
|
||
if len(src) == 0 {
|
||
return map[string]bool{}
|
||
}
|
||
cloned := make(map[string]bool, len(src))
|
||
for key := range src {
|
||
cloned[key] = true
|
||
}
|
||
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 seedGoModuleVersionCache(t *testing.T, modulePath string, versions []string) {
|
||
t.Helper()
|
||
|
||
driverModuleVersionMu.Lock()
|
||
original := make(map[string]goModuleVersionListCacheEntry, len(driverModuleVersionMap))
|
||
for key, value := range driverModuleVersionMap {
|
||
original[key] = goModuleVersionListCacheEntry{
|
||
LoadedAt: value.LoadedAt,
|
||
Versions: append([]goModuleVersionMeta(nil), value.Versions...),
|
||
Err: value.Err,
|
||
}
|
||
}
|
||
driverModuleVersionMap[modulePath] = goModuleVersionListCacheEntry{
|
||
LoadedAt: time.Now(),
|
||
Versions: mapVersionsToMetas(versions),
|
||
}
|
||
driverModuleVersionMu.Unlock()
|
||
|
||
t.Cleanup(func() {
|
||
driverModuleVersionMu.Lock()
|
||
driverModuleVersionMap = original
|
||
driverModuleVersionMu.Unlock()
|
||
})
|
||
}
|
||
|
||
func mapVersionsToMetas(versions []string) []goModuleVersionMeta {
|
||
result := make([]goModuleVersionMeta, 0, len(versions))
|
||
for _, version := range versions {
|
||
result = append(result, goModuleVersionMeta{Version: version})
|
||
}
|
||
return result
|
||
}
|
||
|
||
func containsVersion(versions []string, target string) bool {
|
||
for _, version := range versions {
|
||
if version == target {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
func writeSelfExecutable(t *testing.T, targetPath string) {
|
||
t.Helper()
|
||
|
||
selfPath, err := os.Executable()
|
||
if err != nil {
|
||
t.Fatalf("executable path failed: %v", err)
|
||
}
|
||
content, err := os.ReadFile(selfPath)
|
||
if err != nil {
|
||
t.Fatalf("read self executable failed: %v", err)
|
||
}
|
||
if err := os.WriteFile(targetPath, content, 0o755); err != nil {
|
||
t.Fatalf("write executable failed: %v", err)
|
||
}
|
||
}
|
||
|
||
func writeZipWithSelfExecutable(t *testing.T, zipPath string, entryName string) {
|
||
t.Helper()
|
||
writeZipWithSelfExecutableEntries(t, zipPath, []string{entryName})
|
||
}
|
||
|
||
func writeZipWithSelfExecutableEntries(t *testing.T, zipPath string, entryNames []string) {
|
||
t.Helper()
|
||
|
||
selfPath, err := os.Executable()
|
||
if err != nil {
|
||
t.Fatalf("executable path failed: %v", err)
|
||
}
|
||
content, err := os.ReadFile(selfPath)
|
||
if err != nil {
|
||
t.Fatalf("read self executable failed: %v", err)
|
||
}
|
||
|
||
file, err := os.Create(zipPath)
|
||
if err != nil {
|
||
t.Fatalf("create zip failed: %v", err)
|
||
}
|
||
defer file.Close()
|
||
|
||
writer := zip.NewWriter(file)
|
||
for _, entryName := range entryNames {
|
||
entry, err := writer.Create(entryName)
|
||
if err != nil {
|
||
t.Fatalf("create zip entry failed: %v", err)
|
||
}
|
||
if _, err := entry.Write(content); err != nil {
|
||
t.Fatalf("write zip entry failed: %v", err)
|
||
}
|
||
}
|
||
if err := writer.Close(); err != nil {
|
||
t.Fatalf("close zip writer failed: %v", err)
|
||
}
|
||
}
|
||
|
||
func resetOptionalDriverBundleDownloadCacheForTest(t *testing.T) {
|
||
t.Helper()
|
||
reset := func() {
|
||
optionalDriverBundleDownloadMu.Lock()
|
||
paths := make([]string, 0, len(optionalDriverBundleDownloads))
|
||
for _, state := range optionalDriverBundleDownloads {
|
||
if state != nil && strings.TrimSpace(state.path) != "" {
|
||
paths = append(paths, state.path)
|
||
}
|
||
}
|
||
optionalDriverBundleDownloads = make(map[string]*optionalDriverBundleDownloadState)
|
||
optionalDriverBundleDownloadMu.Unlock()
|
||
for _, path := range paths {
|
||
_ = os.Remove(path)
|
||
}
|
||
}
|
||
reset()
|
||
t.Cleanup(reset)
|
||
}
|
||
|
||
func TestOptionalDriverBundleCacheHelpersDoNotContainLegacyChineseWrappers(t *testing.T) {
|
||
source := methodsDriverSource(t)
|
||
|
||
functionNames := []string{
|
||
"downloadOptionalDriverBundleToCache",
|
||
"acquireOptionalDriverBundlePath",
|
||
}
|
||
functionSource := ""
|
||
for _, name := range functionNames {
|
||
start := strings.Index(source, "func "+name)
|
||
if start < 0 {
|
||
t.Fatalf("methods_driver.go missing %s", name)
|
||
}
|
||
rest := source[start+len("func "+name):]
|
||
end := strings.Index(rest, "\nfunc ")
|
||
if end < 0 {
|
||
t.Fatalf("%s function boundary not found", name)
|
||
}
|
||
functionSource += rest[:end]
|
||
}
|
||
|
||
for _, rawWrapper := range []string{
|
||
`fmt.Errorf("打开驱动总包失败:%w"`,
|
||
`fmt.Errorf("关闭驱动总包失败:%w"`,
|
||
`fmt.Errorf("驱动总包缓存文件不可用")`,
|
||
} {
|
||
if strings.Contains(functionSource, rawWrapper) {
|
||
t.Fatalf("optional driver bundle cache helper still contains legacy raw wrapper %s", rawWrapper)
|
||
}
|
||
}
|
||
}
|