diff --git a/internal/ai/context/builder_i18n_test.go b/internal/ai/context/builder_i18n_test.go index 7085108f..9a59d95c 100644 --- a/internal/ai/context/builder_i18n_test.go +++ b/internal/ai/context/builder_i18n_test.go @@ -1,10 +1,8 @@ package aicontext import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -260,21 +258,3 @@ func TestFormatDatabaseContextCatalogKeysExist(t *testing.T) { } } -func TestFormatDatabaseContextSourceUsesI18nShellKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("builder.go") - if err != nil { - t.Fatalf("read builder.go: %v", err) - } - source := string(sourceBytes) - - for _, legacy := range []string{`"## 当前数据库上下文`, `"### 表结构`, `"| 列名 | 类型 | 可空 | 主键 | 备注 |`, `"**索引:**`, `" (唯一)"`, `"**采样数据`} { - if strings.Contains(source, legacy) { - t.Fatalf("builder.go still contains legacy database context shell %q", legacy) - } - } - for _, key := range databaseContextShellKeys { - if !strings.Contains(source, key) { - t.Fatalf("builder.go does not reference database context shell key %q", key) - } - } -} diff --git a/internal/ai/provider/claude_cli_test.go b/internal/ai/provider/claude_cli_test.go index 91cd94f3..f186ee85 100644 --- a/internal/ai/provider/claude_cli_test.go +++ b/internal/ai/provider/claude_cli_test.go @@ -654,48 +654,6 @@ func TestClaudeCLIProvider_ChatStreamUsesRequestTimeoutWhenNoMeaningfulResponseA assertClaudeCLIRuntimeErrorIsEnglish(t, lastChunk.Error) } -func TestClaudeCLIRuntimeErrorsStayEnglish(t *testing.T) { - message, hasError := extractClaudeCLIEventError(cliStreamEvent{Type: "error"}) - if !hasError { - t.Fatal("expected fallback error") - } - if message != "claude CLI returned an unknown error" { - t.Fatalf("expected English unknown error, got %q", message) - } - - authMessage, hasAuthError := extractClaudeCLISystemRetryError(cliStreamEvent{ - Type: "system", - Subtype: "api_retry", - ErrorStatus: 401, - Error: cliStreamEventError{Message: "authentication_failed"}, - }) - if !hasAuthError { - t.Fatal("expected auth retry error") - } - if authMessage != "claude CLI authentication failed (HTTP 401): authentication_failed" { - t.Fatalf("expected English auth retry error, got %q", authMessage) - } - - source, err := os.ReadFile("claude_cli.go") - if err != nil { - t.Fatalf("read claude_cli.go: %v", err) - } - for _, notWant := range []string{ - "执行超时", - "执行失败", - "返回错误", - "创建 stdout 管道失败", - "启动 claude CLI 失败", - "鉴权失败", - "异常退出", - "返回未知错误", - } { - if strings.Contains(string(source), notWant) { - t.Fatalf("expected Claude CLI runtime wrappers to stay English, found %q", notWant) - } - } -} - func assertClaudeCLIRuntimeErrorIsEnglish(t *testing.T, message string) { t.Helper() if strings.TrimSpace(message) == "" { diff --git a/internal/ai/service/service_cursor_i18n_test.go b/internal/ai/service/service_cursor_i18n_test.go index fff43e5a..d2a2c0a0 100644 --- a/internal/ai/service/service_cursor_i18n_test.go +++ b/internal/ai/service/service_cursor_i18n_test.go @@ -1,42 +1,12 @@ package aiservice import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/ai" "GoNavi-Wails/shared/i18n" ) -func TestAIServiceCursorModelListErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - functionSource := aiServiceFunctionSource(t, source, "func fetchCursorModels(config ai.ProviderConfig, localizer *i18n.Localizer) ([]string, error) {") - for _, rawMessage := range []string{ - `fmt.Errorf("请求模型列表失败: %w", err)`, - `fmt.Errorf("获取模型列表失败 (HTTP %d): %s", resp.StatusCode, string(body))`, - `fmt.Errorf("解析模型列表失败: %w", err)`, - } { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("fetchCursorModels still contains raw cursor model-list text %q", rawMessage) - } - } - for _, symbol := range []string{ - "localizeModelListRequestCreateError", - "localizeModelListRequestError", - "localizeModelListHTTPStatusError", - "localizeModelListParseError", - } { - if !strings.Contains(functionSource, symbol) { - t.Fatalf("fetchCursorModels does not reference cursor model-list localization symbol %q", symbol) - } - } -} func TestFetchCursorModelsUsesEnglishCreateRequestError(t *testing.T) { localizer, err := i18n.NewLocalizer(i18n.LanguageEnUS) diff --git a/internal/ai/service/service_i18n_test.go b/internal/ai/service/service_i18n_test.go index 87bd0ce8..af9008d3 100644 --- a/internal/ai/service/service_i18n_test.go +++ b/internal/ai/service/service_i18n_test.go @@ -30,41 +30,6 @@ func aiServiceFunctionSource(t *testing.T, source string, signature string) stri return source[start : start+len(signature)+end] } -func TestAIServiceProviderSelectionUsesLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (s *Service) AIListModels() map[string]interface{} {": { - rawMessages: []string{`"error": "未找到活跃 Provider"`}, - keys: []string{"ai_service.backend.error.active_provider_not_found"}, - }, - "func (s *Service) getActiveProvider() (provider.Provider, error) {": { - rawMessages: []string{`fmt.Errorf("未配置 AI Provider,请先在设置中配置")`}, - keys: []string{"ai_service.backend.error.provider_not_configured"}, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw AI service text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference AI service i18n key %q", signature, key) - } - } - } -} func TestAIGetBuiltinPromptsUsesCurrentLanguageForPromptTitles(t *testing.T) { service := NewServiceWithSecretStore(nil) @@ -89,109 +54,7 @@ func mapKeys(m map[string]string) []string { return keys } -func TestAIServiceModelListErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - checks := map[string]struct { - rawMessages []string - symbols []string - }{ - "func filterFetchedModelsForProvider(config ai.ProviderConfig, models []string, localizer *i18n.Localizer) ([]string, error) {": { - rawMessages: []string{"volcengineCodingPlanEmptyModelsError"}, - symbols: []string{"volcengineCodingPlanModelsEmptyKey"}, - }, - "func fetchOpenAIModels(config ai.ProviderConfig, localizer *i18n.Localizer) ([]string, error) {": { - rawMessages: []string{`fmt.Errorf("请求模型列表失败: %w", err)`, `fmt.Errorf("获取模型列表失败 (HTTP %d): %s", resp.StatusCode, string(body))`, `fmt.Errorf("解析模型列表失败: %w", err)`}, - symbols: []string{"localizeModelListRequestCreateError", "localizeModelListRequestError", "localizeModelListHTTPStatusError", "localizeModelListParseError"}, - }, - "func fetchAnthropicModels(config ai.ProviderConfig, localizer *i18n.Localizer) ([]string, error) {": { - rawMessages: []string{`fmt.Errorf("请求模型列表失败: %w", err)`, `fmt.Errorf("获取模型列表失败 (HTTP %d): %s", resp.StatusCode, string(body))`, `fmt.Errorf("解析模型列表失败: %w", err)`}, - symbols: []string{"localizeModelListRequestCreateError", "localizeModelListRequestError", "localizeModelListHTTPStatusError", "localizeModelListParseError"}, - }, - "func fetchGeminiModels(config ai.ProviderConfig, localizer *i18n.Localizer) ([]string, error) {": { - rawMessages: []string{`fmt.Errorf("创建请求失败: %w", err)`, `fmt.Errorf("请求模型列表失败: %w", err)`, `fmt.Errorf("获取模型列表失败 (HTTP %d): %s", resp.StatusCode, string(body))`, `fmt.Errorf("解析模型列表失败: %w", err)`}, - symbols: []string{"localizeModelListRequestCreateError", "localizeModelListRequestError", "localizeModelListHTTPStatusError", "localizeModelListParseError"}, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw model-list text %q", signature, rawMessage) - } - } - for _, symbol := range check.symbols { - if !strings.Contains(functionSource, symbol) { - t.Fatalf("%s does not reference model-list localization symbol %q", signature, symbol) - } - } - } -} - -func TestAIServiceHealthCheckRequestErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - requiredTexts []string - requiredKeys []string - }{ - "func (s *Service) localizeProviderHealthCheckRequestError(err error) error {": { - rawMessages: []string{`"创建请求失败: "`, `"序列化请求失败: "`}, - requiredTexts: []string{`"create request failed: "`, `"serialize request failed: "`}, - requiredKeys: []string{ - "ai_service.backend.error.provider_request_create_failed", - "ai_service.backend.error.provider_request_serialize_failed", - }, - }, - "func trimLocalizedModelListRequestCreateDetail(err error) string {": { - rawMessages: []string{`"创建请求失败: "`}, - requiredTexts: []string{`"create request failed: "`}, - }, - "func newModelsRequest(config ai.ProviderConfig, localizer *i18n.Localizer) (*http.Request, error) {": { - rawMessages: []string{`fmt.Errorf("创建请求失败: %w", err)`}, - requiredTexts: []string{`fmt.Errorf("create request failed: %w", err)`}, - }, - "func newAnthropicMessagesHealthCheckRequest(config ai.ProviderConfig) (*http.Request, error) {": { - rawMessages: []string{ - `fmt.Errorf("序列化请求失败: %w", err)`, - `fmt.Errorf("创建请求失败: %w", err)`, - }, - requiredTexts: []string{ - `fmt.Errorf("serialize request failed: %w", err)`, - `fmt.Errorf("create request failed: %w", err)`, - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw health-check text %q", signature, rawMessage) - } - } - for _, requiredText := range check.requiredTexts { - if !strings.Contains(functionSource, requiredText) { - t.Fatalf("%s does not reference health-check wrapper %q", signature, requiredText) - } - } - for _, requiredKey := range check.requiredKeys { - if !strings.Contains(functionSource, requiredKey) { - t.Fatalf("%s does not reference AI service health-check key %q", signature, requiredKey) - } - } - } -} func TestAIServiceLocalizeProviderHealthCheckRequestErrorSupportsEnglishWrappers(t *testing.T) { service := NewService() @@ -272,52 +135,6 @@ func TestAIServiceModelListCatalogKeysExist(t *testing.T) { } } -func TestAIServiceSessionPersistenceErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (s *Service) AISaveSession(sessionID string, title string, updatedAt float64, messagesJSON string) error {": { - rawMessages: []string{ - `fmt.Errorf("创建 sessions 目录失败: %w", err)`, - `fmt.Errorf("序列化会话数据失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.sessions_dir_create_failed", - "ai_service.backend.error.session_serialize_failed", - "ai_service.backend.error.session_write_failed", - }, - }, - "func (s *Service) AIDeleteSession(sessionID string) error {": { - rawMessages: []string{ - `fmt.Errorf("删除会话失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.session_delete_failed", - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw AI session persistence text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference AI session persistence i18n key %q", signature, key) - } - } - } -} func TestAIServiceSessionPersistenceCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -341,43 +158,6 @@ func TestAIServiceSessionPersistenceCatalogKeysExist(t *testing.T) { } } -func TestAIServiceSessionLoadErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (s *Service) AILoadSession(sessionID string) map[string]interface{} {": { - rawMessages: []string{ - `"error": "会话不存在"`, - `"error": "会话数据损坏"`, - }, - keys: []string{ - "ai_service.backend.error.session_missing", - "ai_service.backend.error.session_corrupt", - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw AI session load text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference AI session load i18n key %q", signature, key) - } - } - } -} func TestAIServiceSessionLoadCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -514,117 +294,7 @@ func TestAIDeleteSessionUsesCurrentLanguageForDeleteFailure(t *testing.T) { assertLocalizedAIServiceError(t, service, err, "ai_service.backend.error.session_delete_failed", "删除会话失败") } -func TestAIServiceMCPServerMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("extensions_service.go") - if err != nil { - t.Fatalf("read extensions_service.go: %v", err) - } - source := string(sourceBytes) - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (s *Service) AISaveMCPServer(config ai.MCPServerConfig) error {": { - rawMessages: []string{ - `fmt.Errorf("MCP 服务命令不能为空")`, - }, - keys: []string{ - "ai_service.backend.error.mcp_command_required", - }, - }, - "func (s *Service) AITestMCPServer(config ai.MCPServerConfig) map[string]any {": { - rawMessages: []string{ - `"message": "MCP 服务命令不能为空"`, - `fmt.Sprintf("MCP 服务连接成功,发现 %d 个工具", len(tools))`, - }, - keys: []string{ - "ai_service.backend.error.mcp_command_required", - "ai_service.backend.message.mcp_test_success", - }, - }, - "func (s *Service) AICallMCPTool(alias string, argumentsJSON string) (ai.MCPToolCallResult, error) {": { - rawMessages: []string{ - `fmt.Errorf("未找到 MCP 服务: %s", serverID)`, - `fmt.Errorf("MCP 服务未启用: %s", serverConfig.Name)`, - `fmt.Errorf("解析 MCP 工具参数失败: %w", err)`, - `fmt.Errorf("调用 MCP 工具失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_server_not_found", - "ai_service.backend.error.mcp_server_disabled", - "ai_service.backend.error.mcp_tool_arguments_parse_failed", - "ai_chat.panel.tool_error.mcp_failed_with_detail", - }, - }, - "func (s *Service) withMCPClientSession(localizer *i18n.Localizer, serverConfig ai.MCPServerConfig, fn func(context.Context, *mcp.ClientSession) error) error {": { - rawMessages: []string{ - `fmt.Errorf("暂不支持的 MCP transport: %s", serverConfig.Transport)`, - `fmt.Errorf("MCP 服务命令不能为空")`, - }, - keys: []string{ - "ai_service.backend.error.mcp_transport_unsupported", - "ai_service.backend.error.mcp_command_required", - }, - }, - "func parseMCPToolAlias(localizer *i18n.Localizer, alias string) (string, string, error) {": { - rawMessages: []string{ - `fmt.Errorf("无效的 MCP 工具别名: %s", alias)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_tool_alias_invalid", - }, - }, - "func formatMCPToolCallContent(localizer *i18n.Localizer, result *mcp.CallToolResult) string {": { - rawMessages: []string{ - `return "MCP 工具调用失败"`, - }, - keys: []string{ - "ai_chat.panel.tool_error.mcp_failed", - }, - }, - "func normalizeSkillConfig(config ai.SkillConfig, localizer *i18n.Localizer) ai.SkillConfig {": { - rawMessages: []string{ - `Name: firstNonEmpty(strings.TrimSpace(config.Name), "未命名 Skill"),`, - }, - keys: []string{ - "ai_service.backend.message.skill_unnamed", - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw MCP service text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference MCP service i18n key %q", signature, key) - } - } - } -} - -func TestAIServiceMCPClientHomeDirSentinelUsesEnglishInternalText(t *testing.T) { - sourceBytes, err := os.ReadFile("claude_code_mcp.go") - if err != nil { - t.Fatalf("read claude_code_mcp.go: %v", err) - } - source := string(sourceBytes) - - if strings.Contains(source, `errors.New("无法确定用户目录")`) { - t.Fatalf(`claude_code_mcp.go still contains raw MCP client home-dir sentinel %q`, `errors.New("无法确定用户目录")`) - } - if !strings.Contains(source, `errors.New("user home directory is unavailable")`) { - t.Fatalf(`claude_code_mcp.go does not contain expected English MCP client home-dir sentinel %q`, `errors.New("user home directory is unavailable")`) - } - if !strings.Contains(source, `"ai.service.mcp_client.user_home_dir_unavailable"`) { - t.Fatal(`claude_code_mcp.go does not reference ai.service.mcp_client.user_home_dir_unavailable`) - } -} func TestAIServiceMCPServerCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -654,117 +324,6 @@ func TestAIServiceMCPServerCatalogKeysExist(t *testing.T) { } } -func TestAIServiceMCPHTTPServerMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("mcp_http_server.go") - if err != nil { - t.Fatalf("read mcp_http_server.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (s *Service) startMCPHTTPServer(options ai.MCPHTTPServerOptions) (ai.MCPHTTPServerStatus, error) {": { - rawMessages: []string{ - `fmt.Sprintf("GoNavi MCP HTTP 服务启动失败:%v", err)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_http_start_failed", - }, - }, - "func (s *Service) AIStopMCPHTTPServer() (ai.MCPHTTPServerStatus, error) {": { - rawMessages: []string{ - `"GoNavi MCP HTTP 服务已停止"`, - }, - keys: []string{ - "ai_settings.mcp_http.message.stopped", - }, - }, - "func (s *Service) stopMCPHTTPServer(ctx context.Context, message string) (ai.MCPHTTPServerStatus, error) {": { - rawMessages: []string{ - `"GoNavi MCP HTTP 服务未启动"`, - `fmt.Sprintf("GoNavi MCP HTTP 服务停止失败:%v", err)`, - }, - keys: []string{ - "ai_settings.mcp_http.status.not_running", - "ai_service.backend.error.mcp_http_stop_failed", - }, - }, - "func (s *Service) watchMCPHTTPServer(runtime *mcpHTTPServerRuntime) {": { - rawMessages: []string{ - `"GoNavi MCP HTTP 服务已停止"`, - `fmt.Sprintf("GoNavi MCP HTTP 服务异常退出:%v", err)`, - }, - keys: []string{ - "ai_settings.mcp_http.message.stopped", - "ai_service.backend.error.mcp_http_process_exited", - }, - }, - "func startMCPHTTPCommandProcess(ctx context.Context, options mcpHTTPProcessStartOptions, textLookup mcpHTTPTextLookup) (mcpHTTPProcess, error) {": { - rawMessages: []string{ - `fmt.Errorf("定位当前 GoNavi 可执行文件失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_http_executable_resolve_failed", - }, - }, - "func waitForMCPHTTPReady(ctx context.Context, process mcpHTTPProcess, status ai.MCPHTTPServerStatus, textLookup mcpHTTPTextLookup) error {": { - rawMessages: []string{ - `fmt.Errorf("MCP HTTP 子进程已退出")`, - }, - keys: []string{ - "ai_service.backend.error.mcp_http_subprocess_exited", - }, - }, - "func waitMCPHTTPHealthEndpoint(ctx context.Context, healthURL string, textLookup mcpHTTPTextLookup) error {": { - rawMessages: []string{ - `fmt.Errorf("healthz 返回 HTTP %d", resp.StatusCode)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_http_health_status_failed", - }, - }, - "func statusFromMCPHTTPOptions(options mcpHTTPProcessStartOptions, token string, textLookup mcpHTTPTextLookup) ai.MCPHTTPServerStatus {": { - rawMessages: []string{ - `"GoNavi MCP HTTP 服务已启动"`, - }, - keys: []string{ - "ai_settings.mcp_http.message.started", - }, - }, - "func generateMCPHTTPToken(textLookup mcpHTTPTextLookup) (string, error) {": { - rawMessages: []string{ - `fmt.Errorf("生成 MCP HTTP Token 失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.mcp_http_token_generate_failed", - }, - }, - "func defaultMCPHTTPServerStatus(textLookup mcpHTTPTextLookup) ai.MCPHTTPServerStatus {": { - rawMessages: []string{ - `"GoNavi MCP HTTP 服务未启动"`, - }, - keys: []string{ - "ai_settings.mcp_http.status.not_running", - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw MCP HTTP service text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference MCP HTTP service i18n key %q", signature, key) - } - } - } -} func TestAIServiceMCPHTTPServerCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/ai/service/service_session_i18n_test.go b/internal/ai/service/service_session_i18n_test.go index 13775e3e..a4752d2c 100644 --- a/internal/ai/service/service_session_i18n_test.go +++ b/internal/ai/service/service_session_i18n_test.go @@ -1,65 +1,12 @@ package aiservice import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/ai" "GoNavi-Wails/shared/i18n" ) -func TestAIServiceAdditionalSessionAndModelMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("service.go") - if err != nil { - t.Fatalf("read service.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func newModelsRequest(config ai.ProviderConfig, localizer *i18n.Localizer) (*http.Request, error) {": { - rawMessages: []string{ - `fmt.Errorf("当前供应商不支持远端模型列表")`, - }, - keys: []string{ - "ai_service.backend.error.models_remote_unsupported", - }, - }, - "func (s *Service) storeSessionProviderRuntime(sessionID string, providerKey string, state json.RawMessage, messages []ai.Message) error {": { - rawMessages: []string{ - `fmt.Errorf("序列化会话 Provider 消息失败: %w", err)`, - }, - keys: []string{ - "ai_service.backend.error.session_provider_messages_serialize_failed", - }, - }, - "func (s *Service) loadOrCreateSessionFile(sessionID string) (sessionFileData, error) {": { - rawMessages: []string{ - `Title: "新的对话"`, - }, - keys: []string{ - "ai_chat.panel.session.default_title", - }, - }, - } - - for signature, check := range checks { - functionSource := aiServiceFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw AI service text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference AI service i18n key %q", signature, key) - } - } - } -} func TestAIServiceAdditionalSessionAndModelCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/app_i18n_test.go b/internal/app/app_i18n_test.go index c358cfa8..747123c7 100644 --- a/internal/app/app_i18n_test.go +++ b/internal/app/app_i18n_test.go @@ -41,67 +41,6 @@ func expectedLocalizedAppMessage(base string) string { return base + defaultAppText("driver_manager.backend.message.log_hint", map[string]any{"path": path}) } -func TestAppUserVisibleConnectionMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("app.go") - if err != nil { - t.Fatalf("read app.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func wrapConnectError": { - rawMessages: []string{ - `fmt.Errorf("数据库连接超时:%s %s:%d/%s:%w", config.Type, config.Host, config.Port, dbName, err)`, - }, - keys: []string{ - "db.backend.message.connect_timeout_detail", - }, - }, - "func (a *App) getDatabaseWithPing": { - rawMessages: []string{ - `fmt.Sprintf("连接最近失败,正在冷却中,请 %s 后重试;上次错误:%s",`, - }, - keys: []string{ - "db.backend.message.connect_failure_cooldown", - }, - }, - "func (a *App) CancelQuery": { - rawMessages: []string{ - `Message: "查询已取消"`, - `Message: "查询不存在或已完成"`, - }, - keys: []string{ - "query_editor.message.cancel_success", - "query_editor.message.cancel_no_running", - }, - }, - "func (a *App) ResetWebViewZoom() (result connection.QueryResult)": { - rawMessages: []string{ - `Message: fmt.Sprintf("重置 WebView2 zoom 失败:%v", recovered)`, - }, - keys: []string{ - "app.backend.error.reset_webview_zoom_failed", - }, - }, - } - - for signature, check := range checks { - body := appFunctionSource(t, source, signature) - for _, raw := range check.rawMessages { - if strings.Contains(body, raw) { - t.Fatalf("%s still contains raw user-visible message %q", signature, raw) - } - } - for _, key := range check.keys { - if !strings.Contains(body, key) { - t.Fatalf("%s should reference localized key %q", signature, key) - } - } - } -} func TestAppBackendCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/connection_package_i18n_test.go b/internal/app/connection_package_i18n_test.go index 0ca24aab..89c63d57 100644 --- a/internal/app/connection_package_i18n_test.go +++ b/internal/app/connection_package_i18n_test.go @@ -2,10 +2,8 @@ package app import ( "errors" - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -23,25 +21,6 @@ func connectionPackageFunctionSource(t *testing.T, source string, signature stri return source[start : start+len(signature)+end] } -func TestConnectionPackageInternalSentinelsDoNotUseLegacyChineseText(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_package_types.go") - if err != nil { - t.Fatalf("read connection_package_types.go: %v", err) - } - source := string(sourceBytes) - - for _, literal := range []string{ - "恢复包密码不能为空", - "文件密码错误或文件已损坏", - "不支持的连接恢复包格式", - "连接导入文件过大", - "连接恢复包过大", - } { - if strings.Contains(source, literal) { - t.Fatalf("connection_package_types.go still contains legacy Chinese sentinel text %q", literal) - } - } -} func TestImportConnectionsPayloadLocalizesPasswordRequiredErrorInGerman(t *testing.T) { app := NewAppWithSecretStore(newFakeAppSecretStore()) @@ -134,20 +113,3 @@ func TestImportConnectionsPayloadLocalizesMySQLWorkbenchNoConnectionsErrorInGerm } } -func TestConnectionPackageDialogBoundariesUseLocalizedMessageHelpers(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - importFunction := connectionPackageFunctionSource(t, source, "func (a *App) ImportConfigFile() connection.QueryResult") - if !strings.Contains(importFunction, "localizedConnectionPackageMessage(") { - t.Fatal("ImportConfigFile should localize connection-package sentinel errors before returning QueryResult.Message") - } - - exportFunction := connectionPackageFunctionSource(t, source, "func (a *App) ExportConnectionsPackage(options ConnectionExportOptions) connection.QueryResult") - if !strings.Contains(exportFunction, "localizedConnectionPackageExportMessage(") { - t.Fatal("ExportConnectionsPackage should map export oversize errors through a dedicated localized message helper") - } -} diff --git a/internal/app/connection_readonly_i18n_test.go b/internal/app/connection_readonly_i18n_test.go index 8a73315e..aad42ff7 100644 --- a/internal/app/connection_readonly_i18n_test.go +++ b/internal/app/connection_readonly_i18n_test.go @@ -1,48 +1,13 @@ package app import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" syncjob "GoNavi-Wails/internal/sync" "GoNavi-Wails/shared/i18n" ) -func TestConnectionReadOnlyMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_readonly.go") - if err != nil { - t.Fatalf("read connection_readonly.go: %v", err) - } - source := string(sourceBytes) - - rawMessages := []string{ - `return "当前连接已启用生产保护,仅允许执行查询操作"`, - `return fmt.Sprintf("当前连接已启用生产保护,禁止执行%s", label)`, - } - for _, raw := range rawMessages { - if strings.Contains(source, raw) { - t.Fatalf("connection_readonly.go still contains raw user-visible text %q", raw) - } - } - - keys := []string{ - "query_editor.message.connection_readonly_blocked", - "connection.backend.error.readonly_action_blocked", - "connection.backend.action.create_database", - "connection.backend.action.import_data", - "connection.backend.action.data_sync_structure", - "connection.backend.action.data_sync_write", - "connection.backend.action.clear_table", - "connection.backend.action.truncate_table", - } - for _, key := range keys { - if !strings.Contains(source, key) { - t.Fatalf("connection_readonly.go should reference localized key %q", key) - } - } -} func TestConnectionReadOnlyCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/connection_secret_resolution_test.go b/internal/app/connection_secret_resolution_test.go index ca97edd5..a409367c 100644 --- a/internal/app/connection_secret_resolution_test.go +++ b/internal/app/connection_secret_resolution_test.go @@ -2,10 +2,8 @@ package app import ( "fmt" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -121,33 +119,6 @@ func TestResolveConnectionSecretsOnDarwinUsesInlineSavedSecrets(t *testing.T) { } } -func TestConnectionSecretResolutionMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_secret_resolution.go") - if err != nil { - t.Fatalf("read connection_secret_resolution.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("未找到已保存连接,可能已被删除,请刷新后重试")`, - `fmt.Errorf("未找到当前连接对应的已保存密文,请重新填写密码并保存后再试")`, - `fmt.Errorf("系统密文存储当前不可用,请检查系统钥匙串或凭据管理器后再试")`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("connection_secret_resolution.go still contains raw secret resolution text %q", rawMessage) - } - } - - for _, key := range []string{ - "connection_modal.secret.error.saved_connection_deleted", - "connection_modal.secret.error.saved_connection_missing", - "connection_modal.secret.error.store_unavailable", - } { - if !strings.Contains(source, key) { - t.Fatalf("connection_secret_resolution.go does not reference secret resolution i18n key %q", key) - } - } -} func TestConnectionSecretResolutionCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/db_proxy_i18n_test.go b/internal/app/db_proxy_i18n_test.go index 84a61ace..10c2a897 100644 --- a/internal/app/db_proxy_i18n_test.go +++ b/internal/app/db_proxy_i18n_test.go @@ -1,10 +1,8 @@ package app import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" proxytunnel "GoNavi-Wails/internal/proxy" "GoNavi-Wails/shared/i18n" @@ -106,39 +104,6 @@ func TestDBProxyHTTPTunnelValidationCatalogKeysExist(t *testing.T) { } } -func TestDBProxyHTTPTunnelValidationSourceUsesLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("db_proxy.go") - if err != nil { - t.Fatalf("read db_proxy.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("HTTP 隧道与普通代理不能同时启用")`, - `fmt.Errorf("HTTP 隧道主机不能为空")`, - `fmt.Errorf("HTTP 隧道端口无效:%d", config.HTTPTunnel.Port)`, - `fmt.Errorf("代理连接 SSH 网关失败:%w", err)`, - `fmt.Errorf("目标端口无效:%d", targetPort)`, - `fmt.Errorf("解析代理本地转发地址失败:%s", forwarder.LocalAddr)`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("db_proxy.go still contains raw HTTP Tunnel validation text %q", rawMessage) - } - } - - for _, key := range []string{ - "db.backend.error.http_tunnel_proxy_conflict", - "db.backend.error.http_tunnel_host_required", - "db.backend.error.http_tunnel_port_invalid", - "db.backend.error.proxy_ssh_gateway_connect_failed", - "db.backend.error.proxy_target_port_invalid", - "db.backend.error.proxy_local_forward_addr_parse_failed", - } { - if !strings.Contains(source, key) { - t.Fatalf("db_proxy.go does not reference DB proxy HTTP Tunnel i18n key %q", key) - } - } -} func TestDBProxyBuildProxyForwardAddressUsesCurrentLanguageForTargetPortValidationError(t *testing.T) { app := NewApp() diff --git a/internal/app/global_proxy_secret_test.go b/internal/app/global_proxy_secret_test.go index c114907e..4318d912 100644 --- a/internal/app/global_proxy_secret_test.go +++ b/internal/app/global_proxy_secret_test.go @@ -6,11 +6,9 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "strconv" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -367,19 +365,3 @@ func TestConfigureGlobalProxyUsesCurrentLanguageForSuccessMessage(t *testing.T) } } -func TestBuildProxyURLFromConfigDefensiveErrorsStayInternalEnglish(t *testing.T) { - sourceBytes, err := os.ReadFile("global_proxy.go") - if err != nil { - t.Fatalf("read global_proxy.go: %v", err) - } - source := string(sourceBytes) - for _, literal := range []string{ - "不支持的代理类型", - "代理地址不能为空", - "代理端口无效", - } { - if strings.Contains(source, literal) { - t.Fatalf("buildProxyURLFromConfig should not keep raw Chinese defensive error %q", literal) - } - } -} diff --git a/internal/app/methods_data_root_i18n_test.go b/internal/app/methods_data_root_i18n_test.go index 49de8d05..e0c3f879 100644 --- a/internal/app/methods_data_root_i18n_test.go +++ b/internal/app/methods_data_root_i18n_test.go @@ -39,192 +39,7 @@ func methodsLogDirectoryFunctionSource(t *testing.T, source string, signature st return source[start : start+len(signature)+end] } -func TestMethodsDataRootMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_data_root.go") - if err != nil { - t.Fatalf("read methods_data_root.go: %v", err) - } - source := string(sourceBytes) - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) SelectDataRootDirectory": { - rawMessages: []string{ - `Title: "选择 GoNavi 数据目录"`, - }, - keys: []string{ - "app.data_root.backend.dialog.select_directory", - }, - }, - "func (a *App) ApplyDataRootDirectory": { - rawMessages: []string{ - `Message: "数据目录未发生变化"`, - `message := "数据目录已更新,建议重启应用以让 AI 与其他运行态模块完全切换到新目录"`, - `message = "数据已迁移并切换到新目录,建议重启应用以完成全部模块切换"`, - }, - keys: []string{ - "app.data_root.backend.message.unchanged", - "app.data_root.backend.message.updated_restart", - "app.data_root.backend.message.migrated_restart", - }, - }, - "func dataRootLocalizeSetActiveRootError": { - rawMessages: nil, - keys: []string{ - "app.data_root.backend.error.create_data_directory_failed", - "app.data_root.backend.error.create_bootstrap_directory_failed", - }, - }, - "func (a *App) OpenDataRootDirectory": { - rawMessages: []string{ - `Message: "数据目录不存在或不可访问"`, - `fmt.Sprintf("当前平台暂不支持打开目录:%s", stdRuntime.GOOS)`, - `fmt.Sprintf("打开数据目录失败:%v", err)`, - `Message: "已打开数据目录"`, - }, - keys: []string{ - "app.data_root.backend.error.directory_unavailable", - "app.data_root.backend.error.open_directory_unsupported", - "app.data_root.backend.error.open_directory_failed", - "app.data_root.backend.message.opened", - }, - }, - "func migrateDataRootContentsWithText": { - rawMessages: []string{ - `fmt.Errorf("数据目录不能为空")`, - `fmt.Errorf("解析源数据目录失败:%w", err)`, - `fmt.Errorf("解析目标数据目录失败:%w", err)`, - `fmt.Errorf("目标数据目录不能位于源目录内部")`, - `fmt.Errorf("创建目标数据目录失败:%w", err)`, - `fmt.Errorf("读取源数据目录失败:%w", err)`, - `fmt.Errorf("读取源数据失败(%s):%w", name, err)`, - `fmt.Errorf("迁移目录失败(%s):%w", name, err)`, - `fmt.Errorf("迁移文件失败(%s):%w", name, err)`, - }, - keys: []string{ - "app.data_root.backend.error.directory_empty", - "app.data_root.backend.error.resolve_source_failed", - "app.data_root.backend.error.resolve_target_failed", - "app.data_root.backend.error.target_inside_source", - "app.data_root.backend.error.create_target_failed", - "app.data_root.backend.error.read_source_root_failed", - "app.data_root.backend.error.read_source_failed", - "app.data_root.backend.error.migrate_directory_failed", - "app.data_root.backend.error.migrate_file_failed", - }, - }, - "func rewriteSecurityUpdateBackupPathsWithText": { - rawMessages: []string{ - `fmt.Errorf("读取迁移后的安全更新状态失败:%w", err)`, - `fmt.Errorf("写入迁移后的安全更新状态失败:%w", err)`, - `fmt.Errorf("读取迁移后的安全更新备份清单失败:%w", err)`, - `fmt.Errorf("解析迁移后的安全更新备份清单失败:%w", err)`, - `fmt.Errorf("写入迁移后的安全更新备份清单失败:%w", err)`, - `fmt.Errorf("读取迁移后的安全更新结果失败:%w", err)`, - `fmt.Errorf("解析迁移后的安全更新结果失败:%w", err)`, - `fmt.Errorf("写入迁移后的安全更新结果失败:%w", err)`, - }, - keys: []string{ - "app.data_root.backend.error.read_migrated_security_update_state_failed", - "app.data_root.backend.error.write_migrated_security_update_state_failed", - "app.data_root.backend.error.read_migrated_security_update_manifest_failed", - "app.data_root.backend.error.parse_migrated_security_update_manifest_failed", - "app.data_root.backend.error.write_migrated_security_update_manifest_failed", - "app.data_root.backend.error.read_migrated_security_update_result_failed", - "app.data_root.backend.error.parse_migrated_security_update_result_failed", - "app.data_root.backend.error.write_migrated_security_update_result_failed", - }, - }, - } - - for signature, check := range checks { - body := methodsDataRootFunctionSource(t, source, signature) - for _, raw := range check.rawMessages { - if strings.Contains(body, raw) { - t.Fatalf("%s still contains raw data-root text %q", signature, raw) - } - } - for _, key := range check.keys { - if !strings.Contains(body, key) { - t.Fatalf("%s should reference localized key %q", signature, key) - } - } - } -} - -func TestMethodsLogDirectoryMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_log_directory.go") - if err != nil { - t.Fatalf("read methods_log_directory.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) SelectLogDirectory": { - rawMessages: []string{ - `Message: "日志目录设置仅可在桌面应用中使用"`, - `Message: "日志目录由环境变量 GONAVI_LOG_DIR 管理"`, - `Title: "选择 GoNavi 日志目录"`, - }, - keys: []string{ - "app.data_root.log_directory.backend.error.desktop_only", - "app.data_root.log_directory.backend.error.environment_managed", - "app.data_root.log_directory.backend.dialog.select_directory", - }, - }, - "func (a *App) ApplyLogDirectory": { - rawMessages: []string{ - `Message: "日志目录设置仅可在桌面应用中使用"`, - `Message: "日志目录由环境变量 GONAVI_LOG_DIR 管理"`, - `Message: fmt.Sprintf("保存日志目录失败:%v", err)`, - `message := "日志目录已保存,重启应用后生效"`, - `message = "日志目录未发生变化"`, - }, - keys: []string{ - "app.data_root.log_directory.backend.error.desktop_only", - "app.data_root.log_directory.backend.error.environment_managed", - "app.data_root.log_directory.backend.error.save_failed", - "app.data_root.log_directory.backend.message.updated_restart", - "app.data_root.log_directory.backend.message.unchanged", - }, - }, - "func (a *App) OpenLogDirectory": { - rawMessages: []string{ - `Message: "日志目录设置仅可在桌面应用中使用"`, - `Message: "当前日志目录不存在或不可访问"`, - `Message: fmt.Sprintf("当前平台暂不支持打开日志目录:%s", stdRuntime.GOOS)`, - `Message: fmt.Sprintf("打开日志目录失败:%v", err)`, - `Message: "已打开日志目录"`, - }, - keys: []string{ - "app.data_root.log_directory.backend.error.desktop_only", - "app.data_root.log_directory.backend.error.directory_unavailable", - "app.data_root.log_directory.backend.error.open_directory_unsupported", - "app.data_root.log_directory.backend.error.open_directory_failed", - "app.data_root.log_directory.backend.message.opened", - }, - }, - } - - for signature, check := range checks { - body := methodsLogDirectoryFunctionSource(t, source, signature) - for _, raw := range check.rawMessages { - if strings.Contains(body, raw) { - t.Fatalf("%s still contains raw log-directory text %q", signature, raw) - } - } - for _, key := range check.keys { - if !strings.Contains(body, key) { - t.Fatalf("%s should reference localized key %q", signature, key) - } - } - } -} func TestMethodsDataRootCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/methods_db_i18n_test.go b/internal/app/methods_db_i18n_test.go index 63f35ba4..ac95af97 100644 --- a/internal/app/methods_db_i18n_test.go +++ b/internal/app/methods_db_i18n_test.go @@ -119,162 +119,6 @@ func (f *fakeManagedExecOnlyDB) OpenSessionExecer(ctx context.Context) (db.State var _ db.SessionExecerProvider = (*fakeManagedExecOnlyDB)(nil) -func TestMethodsDBConnectionAndMongoMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func validateTestConnectionInputWithText": { - rawMessages: []string{ - `"请先选择数据源类型"`, - `"请填写 ClickHouse 主机地址或连接 URI"`, - }, - keys: []string{ - "db.backend.error.data_source_type_required", - "db.backend.error.clickhouse_address_required", - }, - }, - "func (a *App) DBConnect": { - rawMessages: []string{`Message: "连接成功"`}, - keys: []string{"db.backend.message.connect_success"}, - }, - "func (a *App) DBReleaseConnection": { - rawMessages: []string{`Message: "连接已释放"`}, - keys: []string{"db.backend.message.release_success"}, - }, - "func (a *App) TestConnection": { - rawMessages: []string{ - `Message: "连接成功"`, - `fmt.Sprintf("连接成功但释放测试连接失败:%v", closeErr)`, - }, - keys: []string{ - "db.backend.message.connect_success", - "db.backend.error.test_connection_close_failed", - }, - }, - "func (a *App) MongoDiscoverMembers": { - rawMessages: []string{ - `Message: "当前 MongoDB 驱动不支持成员发现"`, - `fmt.Sprintf("发现 %d 个成员"`, - }, - keys: []string{ - "db.backend.error.mongo_member_discovery_unsupported", - "db.backend.message.mongo_members_discovered", - }, - }, - "func buildCreateSchemaSQLWithText": { - rawMessages: []string{ - `"模式名称不能为空"`, - `"当前数据源(%s)暂不支持通过此入口新建模式"`, - }, - keys: []string{ - "db.backend.error.schema_name_required", - "db.backend.error.schema_create_unsupported", - }, - }, - "func buildRenameSchemaSQLWithText": { - rawMessages: []string{ - `"模式名称不能为空"`, - `"新旧模式名称不能相同"`, - `"当前数据源(%s)暂不支持通过此入口编辑模式"`, - }, - keys: []string{ - "db.backend.error.schema_name_required", - "db.backend.error.schema_same_name", - "db.backend.error.schema_rename_unsupported", - }, - }, - "func buildDropSchemaSQLWithText": { - rawMessages: []string{ - `"模式名称不能为空"`, - `"当前数据源(%s)暂不支持通过此入口删除模式"`, - }, - keys: []string{ - "db.backend.error.schema_name_required", - "db.backend.error.schema_drop_unsupported", - }, - }, - "func resolveSchemaDDLTargetDatabaseWithText": { - rawMessages: []string{`"目标数据库不能为空"`}, - keys: []string{"db.backend.error.target_database_required"}, - }, - "func (a *App) CreateSchema": { - rawMessages: []string{`Message: "模式创建成功"`}, - keys: []string{"db.backend.message.schema_created"}, - }, - "func (a *App) RenameSchema": { - rawMessages: []string{`Message: "模式重命名成功"`}, - keys: []string{"db.backend.message.schema_renamed"}, - }, - "func (a *App) DropSchema": { - rawMessages: []string{`Message: "模式删除成功"`}, - keys: []string{"db.backend.message.schema_dropped"}, - }, - "func (a *App) CreateDatabase": { - rawMessages: []string{ - `Message: "数据库名称不能为空"`, - `Message: "Sphinx 暂不支持创建数据库"`, - `"数据库创建成功"`, - `"当前数据源(%s)的「数据库」实际为用户/Schema,暂不支持通过此入口创建,请使用 SQL 编辑器执行 CREATE USER 语句"`, - }, - keys: []string{ - "db.backend.error.database_name_required", - "db.backend.error.database_create_sphinx_unsupported", - "db.backend.error.database_create_user_schema_unsupported", - "db.backend.message.database_created", - }, - }, - "func (a *App) RenameDatabase": { - rawMessages: []string{ - `Message: "数据库名称不能为空"`, - `Message: "新旧数据库名称不能相同"`, - `Message: "数据库重命名成功"`, - `Message: "MySQL/MariaDB/OceanBase/StarRocks/Sphinx 不支持直接重命名数据库,请新建库后迁移数据"`, - `"当前数据源(%s)暂不支持重命名数据库"`, - }, - keys: []string{ - "db.backend.error.database_name_required", - "db.backend.error.database_same_name", - "db.backend.error.database_rename_direct_unsupported", - "db.backend.error.database_rename_unsupported", - "db.backend.message.database_renamed", - }, - }, - "func (a *App) DropDatabase": { - rawMessages: []string{ - `Message: "数据库名称不能为空"`, - `"当前数据源(%s)暂不支持删除数据库"`, - `Message: "数据库删除成功"`, - }, - keys: []string{ - "db.backend.error.database_name_required", - "db.backend.error.database_drop_unsupported", - "db.backend.message.database_dropped", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw DB backend text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference DB backend i18n key %q", signature, key) - } - } - } -} - func TestMethodsDBConnectionAndMongoCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -518,39 +362,6 @@ func TestMethodsDBDatabaseDDLSuccessUsesEnglishMessages(t *testing.T) { } } -func TestMethodsDBQueryMultiMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - // DBQueryMulti only selects the audit policy; the shared execution path owns - // all user-facing multi-statement messages. - functionSource := methodsDBFunctionSource(t, source, "func (a *App) dbQueryMulti") - rawMessages := []string{ - `fmt.Sprintf("第 %d 条语句执行失败: %v", idx+1, err)`, - `fmt.Sprintf("(前 %d 条已执行成功)", len(resultSets))`, - `fmt.Sprintf("当前数据源(%s)本次未走原生多语句结果路径,已自动拆分为 %d 条语句逐条执行。", runConfig.Type, len(statements))`, - } - keys := []string{ - "db.backend.error.multi_statement_execution_failed", - "db.backend.error.multi_statement_previous_success", - "db.backend.message.multi_statement_sequential_fallback", - } - - for _, rawMessage := range rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("DBQueryMulti still contains raw multi-statement text %q", rawMessage) - } - } - for _, key := range keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("DBQueryMulti does not reference multi-statement i18n key %q", key) - } - } -} - func TestMethodsDBQueryMultiCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -636,95 +447,6 @@ func TestMethodsDBQueryMultiSequentialFallbackUsesEnglishMessages(t *testing.T) } } -func TestMethodsDBManagedTransactionMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db_transaction.go") - if err != nil { - t.Fatalf("read methods_db_transaction.go: %v", err) - } - source := string(sourceBytes) - - functionSource := methodsDBFunctionSource(t, source, "func (a *App) finishManagedSQLTransaction") - rawMessages := []string{ - `Message: "事务 ID 不能为空"`, - `Message: "事务不存在或已结束"`, - `fmt.Sprintf("事务%s失败: %v", action, execErr)`, - `fmt.Sprintf("事务%s成功,但关闭会话失败: %v", action, closeErr)`, - `Message: "事务已提交"`, - `Message: "事务已回滚"`, - `action := "回滚"`, - `action = "提交"`, - } - keys := []string{ - "db.backend.error.transaction_id_required", - "db.backend.error.transaction_not_found", - "db.backend.error.transaction_commit_failed", - "db.backend.error.transaction_rollback_failed", - "db.backend.error.transaction_commit_close_failed", - "db.backend.error.transaction_rollback_close_failed", - "db.backend.message.transaction_committed", - "db.backend.message.transaction_rolled_back", - } - - for _, rawMessage := range rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("finishManagedSQLTransaction still contains raw transaction text %q", rawMessage) - } - } - for _, key := range keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("finishManagedSQLTransaction does not reference transaction i18n key %q", key) - } - } -} - -func TestMethodsDBManagedTransactionExecutionMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db_transaction.go") - if err != nil { - t.Fatalf("read methods_db_transaction.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) DBQueryMultiTransactional": { - rawMessages: []string{ - `fmt.Sprintf("当前数据源(%s)不支持 SQL 编辑器托管事务", transactionDBType)`, - `fmt.Errorf("%v;回滚失败: %w", err, rollbackErr)`, - }, - keys: []string{ - "db.backend.error.managed_transaction_unsupported", - "db.backend.error.transaction_rollback_failed", - }, - }, - "func executeManagedSQLTransactionStatementsWithObserver": { - rawMessages: []string{ - `fmt.Errorf("当前事务会话不支持查询语句")`, - `fmt.Errorf("第 %d 条语句执行失败: %w", idx+1, err)`, - }, - keys: []string{ - "db.backend.error.transaction_query_unsupported", - "db.backend.error.multi_statement_execution_failed", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw managed-transaction execution text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference managed-transaction execution i18n key %q", signature, key) - } - } - } -} - func TestMethodsDBManagedTransactionCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -1052,84 +774,6 @@ func TestMethodsDBManagedTransactionExecutionUsesEnglishMessages(t *testing.T) { }) } -func TestMethodsDBTableDDLMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func resolveCreateStatementWithFallbackWithText": { - rawMessages: []string{ - `fmt.Errorf("表名不能为空")`, - }, - keys: []string{ - "db.backend.error.table_name_required", - }, - }, - "func buildFallbackCreateStatementWithText": { - rawMessages: []string{ - `fmt.Errorf("表名不能为空")`, - `fmt.Errorf("未获取到字段定义,无法生成建表语句")`, - `fmt.Errorf("字段定义为空,无法生成建表语句")`, - }, - keys: []string{ - "db.backend.error.table_name_required", - "db.backend.error.table_columns_missing_for_ddl", - "db.backend.error.table_columns_empty_for_ddl", - }, - }, - "func (a *App) RenameTable": { - rawMessages: []string{ - `Message: "表名不能为空"`, - `Message: "新旧表名不能相同"`, - `Message: "新表名不能包含 schema 或数据库前缀"`, - `"当前数据源(%s)暂不支持重命名表"`, - `Message: "旧表名不能为空"`, - `Message: "表重命名成功"`, - }, - keys: []string{ - "db.backend.error.table_name_required", - "db.backend.error.table_same_name", - "db.backend.error.table_new_name_no_qualifier", - "db.backend.error.table_rename_unsupported", - "db.backend.error.old_table_name_required", - "db.backend.message.table_renamed", - }, - }, - "func (a *App) DropTable": { - rawMessages: []string{ - `Message: "表名不能为空"`, - `"当前数据源(%s)暂不支持删除表"`, - `Message: "表删除成功"`, - }, - keys: []string{ - "db.backend.error.table_name_required", - "db.backend.error.table_drop_unsupported", - "db.backend.message.table_dropped", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw DB table text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference DB table i18n key %q", signature, key) - } - } - } -} - func TestMethodsDBTableDDLCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -1341,64 +985,6 @@ func TestMethodsDBShowCreateTableFallbackUsesEnglishMessages(t *testing.T) { }) } -func TestMethodsDBViewDDLMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) DropView": { - rawMessages: []string{ - `Message: "视图名称不能为空"`, - `"当前数据源(%s)暂不支持删除视图"`, - `Message: "视图删除成功"`, - }, - keys: []string{ - "db.backend.error.view_name_required", - "db.backend.error.view_drop_unsupported", - "db.backend.message.view_dropped", - }, - }, - "func (a *App) RenameView": { - rawMessages: []string{ - `Message: "视图名称不能为空"`, - `Message: "新旧视图名称不能相同"`, - `Message: "新视图名不能包含 schema 或数据库前缀"`, - `Message: "旧视图名不能为空"`, - `"当前数据源(%s)暂不支持重命名视图"`, - `Message: "视图重命名成功"`, - }, - keys: []string{ - "db.backend.error.view_name_required", - "db.backend.error.view_same_name", - "db.backend.error.view_new_name_no_qualifier", - "db.backend.error.old_view_name_required", - "db.backend.error.view_rename_unsupported", - "db.backend.message.view_renamed", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw DB view text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference DB view i18n key %q", signature, key) - } - } - } -} - func TestMethodsDBViewDDLCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -1527,51 +1113,6 @@ func TestMethodsDBViewDDLSuccessUsesEnglishMessages(t *testing.T) { } } -func TestMethodsDBRoutineDDLMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) DropFunction": { - rawMessages: []string{ - `Message: "函数/存储过程名称不能为空"`, - `"当前数据源(%s)暂不支持删除函数/存储过程"`, - `Message: "DuckDB 暂不支持存储过程"`, - `label := "函数"`, - `label = "存储过程"`, - `fmt.Sprintf("%s删除成功", label)`, - }, - keys: []string{ - "db.backend.error.routine_name_required", - "db.backend.error.routine_drop_unsupported", - "db.backend.error.duckdb_procedure_drop_unsupported", - "db.backend.message.function_dropped", - "db.backend.message.procedure_dropped", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw DB routine text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference DB routine i18n key %q", signature, key) - } - } - } -} - func TestMethodsDBRoutineDDLCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { diff --git a/internal/app/methods_db_oracle_i18n_test.go b/internal/app/methods_db_oracle_i18n_test.go index 87f8ff84..9fc31424 100644 --- a/internal/app/methods_db_oracle_i18n_test.go +++ b/internal/app/methods_db_oracle_i18n_test.go @@ -1,10 +1,8 @@ package app import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -45,51 +43,6 @@ func (db *fakeOracleMetadataDB) GetTriggers(dbName, tableName string) ([]connect return nil, nil } -func TestMethodsDBOracleMetadataMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_db.go") - if err != nil { - t.Fatalf("read methods_db.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func inferOracleColumnsFromDictionary": { - rawMessages: []string{ - `fmt.Errorf("未获取到字段定义")`, - }, - keys: []string{ - "db.backend.error.column_definitions_missing", - }, - }, - "func inferOracleColumnsFromEmptySelect": { - rawMessages: []string{ - `fmt.Errorf("表名不能为空")`, - `fmt.Errorf("未获取到字段定义")`, - }, - keys: []string{ - "db.backend.error.table_name_required", - "db.backend.error.column_definitions_missing", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsDBFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw Oracle metadata text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference Oracle metadata i18n key %q", signature, key) - } - } - } -} func TestMethodsDBOracleMetadataCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/methods_explain_backend_i18n_test.go b/internal/app/methods_explain_backend_i18n_test.go index 33e99a46..b1b5659a 100644 --- a/internal/app/methods_explain_backend_i18n_test.go +++ b/internal/app/methods_explain_backend_i18n_test.go @@ -3,10 +3,8 @@ package app import ( "context" "errors" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -48,69 +46,6 @@ func (db *fakeExplainErrorDatabase) Explain(ctx context.Context, query string) ( return "", connection.ExplainFormatJSON, db.explainErr } -func TestMethodsExplainBackendErrorSourcesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_explain.go") - if err != nil { - t.Fatalf("read methods_explain.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) executeExplain": { - rawMessages: []string{ - `fmt.Errorf("驱动 EXPLAIN 执行失败`, - `fmt.Errorf("执行 EXPLAIN 失败`, - }, - keys: []string{ - "sql_analysis.backend.error.driver_explain_failed", - "sql_analysis.backend.error.explain_execution_failed", - }, - }, - "func collectExplainRawWithText": { - rawMessages: []string{ - `fmt.Errorf("未返回 EXPLAIN 结果集")`, - `fmt.Errorf("EXPLAIN 结果集为空")`, - }, - keys: []string{ - "sql_analysis.backend.error.explain_result_missing", - "sql_analysis.backend.error.explain_result_empty", - }, - }, - "func parseExplainRawWithText": { - rawMessages: []string{ - `fmt.Errorf("当前数据源`, - }, - keys: []string{ - "sql_analysis.backend.error.explain_dialect_unsupported", - }, - }, - "func buildExplainQueryWithText": { - rawMessages: []string{ - `fmt.Errorf("当前数据源`, - }, - keys: []string{ - "sql_analysis.backend.error.explain_query_not_implemented", - }, - }, - } - - for signature, check := range checks { - functionSource := methodsExplainFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw explain backend text %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference explain backend i18n key %q", signature, key) - } - } - } -} func TestMethodsExplainBackendErrorCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/methods_explain_i18n_test.go b/internal/app/methods_explain_i18n_test.go index cf93a307..717e5231 100644 --- a/internal/app/methods_explain_i18n_test.go +++ b/internal/app/methods_explain_i18n_test.go @@ -2,10 +2,8 @@ package app import ( "context" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/internal/db" "GoNavi-Wails/shared/i18n" @@ -63,81 +61,6 @@ func (db *fakeExplainDatabase) Explain(ctx context.Context, query string) (strin return db.explainRaw, db.explainFormat, nil } -func TestMethodsExplainAndQueryHistoryMessagesUseLocalizedText(t *testing.T) { - explainSourceBytes, err := os.ReadFile("methods_explain.go") - if err != nil { - t.Fatalf("read methods_explain.go: %v", err) - } - queryHistorySourceBytes, err := os.ReadFile("methods_query_history.go") - if err != nil { - t.Fatalf("read methods_query_history.go: %v", err) - } - - explainSource := string(explainSourceBytes) - queryHistorySource := string(queryHistorySourceBytes) - - checks := map[string]struct { - source string - signature string - rawMessages []string - keys []string - }{ - "DiagnoseQuery": { - source: explainSource, - signature: "func (a *App) DiagnoseQuery", - rawMessages: []string{ - `Message: "查询语句不能为空"`, - `Message: "诊断仅支持 SELECT / WITH 查询;写操作请使用 EXPLAIN PLAN 模式(PR2 支持)"`, - `fmt.Sprintf("当前数据源(%s)暂不支持 SQL 诊断;一期支持 MySQL/PostgreSQL/SQLite/ClickHouse/Oracle/SQLServer/OceanBase", dbType)`, - `Message: "诊断完成"`, - }, - keys: []string{ - "sql_analysis.backend.error.query_required", - "sql_analysis.backend.error.select_only", - "sql_analysis.backend.error.unsupported_db_type", - "sql_analysis.backend.message.completed", - }, - }, - "GetSlowQueries": { - source: queryHistorySource, - signature: "func (a *App) GetSlowQueries", - rawMessages: []string{ - `Message: "无法解析连接指纹"`, - `Message: "加载完成"`, - }, - keys: []string{ - "query_history.backend.error.connection_fingerprint_invalid", - "query_history.backend.message.loaded", - }, - }, - "ClearSlowQueries": { - source: queryHistorySource, - signature: "func (a *App) ClearSlowQueries", - rawMessages: []string{ - `Message: "无法解析连接指纹"`, - `Message: "已清空慢查询历史"`, - }, - keys: []string{ - "query_history.backend.error.connection_fingerprint_invalid", - "query_history.backend.message.cleared", - }, - }, - } - - for name, check := range checks { - body := methodsExplainFunctionSource(t, check.source, check.signature) - for _, raw := range check.rawMessages { - if strings.Contains(body, raw) { - t.Fatalf("%s still contains raw user-visible text %q", name, raw) - } - } - for _, key := range check.keys { - if !strings.Contains(body, key) { - t.Fatalf("%s should reference localized key %q", name, key) - } - } - } -} func TestMethodsExplainAndQueryHistoryCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/methods_file_i18n_test.go b/internal/app/methods_file_i18n_test.go index 893464b9..c6cfa9e8 100644 --- a/internal/app/methods_file_i18n_test.go +++ b/internal/app/methods_file_i18n_test.go @@ -1,7 +1,6 @@ package app import ( - "os" "strings" "testing" @@ -22,158 +21,6 @@ func methodsFileFunctionSource(t *testing.T, source string, signature string) st return source[start : start+len(signature)+end] } -func TestMethodsFileSelectorsUseLocalizedDialogText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - for _, literal := range []string{ - "\u9009\u62e9 SSH \u79c1\u94a5\u6587\u4ef6", - "\u79c1\u94a5\u6587\u4ef6", - "\u6240\u6709\u6587\u4ef6", - "\u9009\u62e9 TLS \u8bc1\u4e66\u6587\u4ef6", - "\u8bc1\u4e66\u6587\u4ef6", - "\u9009\u62e9 CA/\u670d\u52a1\u7aef\u8bc1\u4e66\u6587\u4ef6", - "\u9009\u62e9\u5ba2\u6237\u7aef\u8bc1\u4e66\u6587\u4ef6", - "\u9009\u62e9\u5ba2\u6237\u7aef\u79c1\u94a5\u6587\u4ef6", - "\u6570\u636e\u5e93\u6587\u4ef6", - "\u9009\u62e9\u6570\u636e\u5e93\u6587\u4ef6", - "\u9009\u62e9 SQLite \u6570\u636e\u6587\u4ef6", - "SQLite \u6587\u4ef6", - "\u9009\u62e9 DuckDB \u6570\u636e\u6587\u4ef6", - "DuckDB \u6587\u4ef6", - } { - if strings.Contains(source, literal) { - t.Fatalf("methods_file.go still contains raw dialog text %q", literal) - } - } -} - -func TestExternalSQLFileBackendMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string][]string{ - "func normalizeSQLFileName": { - "\u0053\u0051\u004c \u6587\u4ef6\u540d\u4e0d\u80fd\u4e3a\u7a7a", - "\u0053\u0051\u004c \u6587\u4ef6\u540d\u4e0d\u80fd\u5305\u542b\u8def\u5f84\u5206\u9694\u7b26", - }, - "func normalizeSQLDirectoryName": { - "\u76ee\u5f55\u540d\u4e0d\u80fd\u4e3a\u7a7a", - "\u76ee\u5f55\u540d\u4e0d\u80fd\u5305\u542b\u8def\u5f84\u5206\u9694\u7b26", - }, - "func normalizeSQLDirectoryPath": { - "\u76ee\u5f55\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u65e0\u6cd5\u8bfb\u53d6\u76ee\u5f55\u4fe1\u606f:", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f\u76ee\u5f55", - }, - "func normalizeExistingSQLDirectoryPath": { - "\u76ee\u5f55\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u65e0\u6cd5\u8bfb\u53d6\u76ee\u5f55\u4fe1\u606f:", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f\u76ee\u5f55", - }, - "func normalizeExistingSQLFilePath": { - "\u6587\u4ef6\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u65e0\u6cd5\u8bfb\u53d6\u6587\u4ef6\u4fe1\u606f:", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f SQL \u6587\u4ef6", - "\u4ec5\u652f\u6301 SQL \u6587\u4ef6", - }, - "func createSQLFileInDirectory": { - "\u0053\u0051\u004c \u6587\u4ef6\u5df2\u5b58\u5728", - "\u65e0\u6cd5\u8bfb\u53d6\u6587\u4ef6\u4fe1\u606f:", - "\u65e0\u6cd5\u521b\u5efa SQL \u6587\u4ef6:", - }, - "func createSQLDirectoryInDirectory": { - "\u76ee\u5f55\u5df2\u5b58\u5728", - "\u65e0\u6cd5\u8bfb\u53d6\u76ee\u5f55\u4fe1\u606f:", - "\u65e0\u6cd5\u521b\u5efa\u76ee\u5f55:", - }, - "func deleteSQLFileByPath": { - "\u65e0\u6cd5\u5220\u9664 SQL \u6587\u4ef6:", - }, - "func deleteSQLDirectoryByPath": { - "\u65e0\u6cd5\u5220\u9664\u76ee\u5f55:", - "\u4ec5\u652f\u6301\u5220\u9664\u7a7a\u76ee\u5f55", - }, - "func renameSQLFileByPath": { - "\u76ee\u6807 SQL \u6587\u4ef6\u5df2\u5b58\u5728", - "\u65e0\u6cd5\u8bfb\u53d6\u76ee\u6807\u6587\u4ef6\u4fe1\u606f:", - "\u65e0\u6cd5\u91cd\u547d\u540d SQL \u6587\u4ef6:", - }, - "func renameSQLDirectoryByPath": { - "\u76ee\u6807\u76ee\u5f55\u5df2\u5b58\u5728", - "\u65e0\u6cd5\u8bfb\u53d6\u76ee\u6807\u76ee\u5f55\u4fe1\u606f:", - "\u65e0\u6cd5\u91cd\u547d\u540d\u76ee\u5f55:", - }, - "func readSQLFileByPath": { - "\u6587\u4ef6\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u65e0\u6cd5\u8bfb\u53d6\u6587\u4ef6\u4fe1\u606f:", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f SQL \u6587\u4ef6", - }, - "func writeSQLFileByPath": { - "\u6587\u4ef6\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u65e0\u6cd5\u8bfb\u53d6\u6587\u4ef6\u4fe1\u606f:", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f SQL \u6587\u4ef6", - "\u65e0\u6cd5\u5199\u5165 SQL \u6587\u4ef6:", - }, - "func writeExportedSQLFileByPath": { - "\u6587\u4ef6\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f SQL \u6587\u4ef6", - "\u65e0\u6cd5\u8bfb\u53d6\u6587\u4ef6\u4fe1\u606f:", - "\u65e0\u6cd5\u5199\u5165 SQL \u6587\u4ef6:", - }, - "func (a *App) OpenSQLFile": { - "Select SQL File", - "SQL Files (*.sql)", - "All Files (*.*)", - }, - "func (a *App) SelectSQLDirectory": { - "\u9009\u62e9 SQL \u76ee\u5f55", - }, - "func (a *App) ListSQLDirectory": { - "\u76ee\u5f55\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - "\u6240\u9009\u8def\u5f84\u4e0d\u662f\u76ee\u5f55", - }, - "func (a *App) ExportSQLFile": { - "\u5bfc\u51fa SQL \u6587\u4ef6", - "SQL Files (*.sql)", - "All Files (*.*)", - "SQL \u6587\u4ef6\u5df2\u5bfc\u51fa", - }, - } - - for signature, literals := range checks { - functionSource := methodsFileFunctionSource(t, source, signature) - for _, literal := range literals { - if strings.Contains(functionSource, literal) { - t.Fatalf("%s still contains raw external SQL file text %q", signature, literal) - } - } - } -} - -func TestImportConfigFileAllowsNavicatNCXSelection(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := methodsFileFunctionSource(t, string(sourceBytes), "func (a *App) ImportConfigFile() connection.QueryResult") - - for _, want := range []string{ - "Navicat Connections (*.ncx)", - `Pattern: "*.ncx"`, - } { - if !strings.Contains(source, want) { - t.Fatalf("ImportConfigFile missing Navicat NCX selector %q", want) - } - } -} - func TestExternalSQLFileBackendCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -223,23 +70,6 @@ func TestExternalSQLFileBackendCatalogKeysExist(t *testing.T) { } } -func TestExportDriverAgentGuardUsesLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - functionSource := methodsFileFunctionSource(t, source, "func verifyOptionalDriverAgentReadyForExport(config connection.ConnectionConfig) error {") - rawLiteral := `fmt.Errorf("当前导出依赖最新的 %s driver-agent 流式协议;为避免大结果集回退到高内存缓冲模式,请在驱动管理中重装后重试:%w", displayName, err)` - if strings.Contains(functionSource, rawLiteral) { - t.Fatalf("verifyOptionalDriverAgentReadyForExport still contains raw export driver-agent guard text %q", rawLiteral) - } - if !strings.Contains(functionSource, "file.backend.error.export_driver_agent_streaming_required") { - t.Fatal("verifyOptionalDriverAgentReadyForExport does not reference export driver-agent streaming i18n key") - } -} - func TestExportDriverAgentGuardCatalogKeyExists(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -286,48 +116,6 @@ func TestFileSelectorDialogCatalogKeysExist(t *testing.T) { } } -func TestImportDataBackendMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string][]string{ - "func (a *App) PreviewImportFile": { - "\u6587\u4ef6\u8def\u5f84\u4e0d\u80fd\u4e3a\u7a7a", - }, - "func (a *App) ImportData": { - "Import into %s", - "Data Files", - }, - "func parseImportFileWithText": { - "JSON Parse Error:", - "CSV Parse Error:", - "CSV empty or missing header", - "Excel Parse Error:", - "Excel file has no sheets", - "Excel Read Error:", - "Excel empty or missing header", - "Unsupported file format", - }, - "func (a *App) ImportDataWithProgress": { - "\u65e0\u53ef\u5bfc\u5165\u6570\u636e", - "Row %d:", - "Imported: %d, Failed: %d", - }, - } - - for signature, literals := range checks { - functionSource := methodsFileFunctionSource(t, source, signature) - for _, literal := range literals { - if strings.Contains(functionSource, literal) { - t.Fatalf("%s still contains raw import data text %q", signature, literal) - } - } - } -} - func TestImportDataBackendCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -363,24 +151,6 @@ func TestImportDataBackendCatalogKeysExist(t *testing.T) { } } -func TestApplyChangesMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - functionSource := methodsFileFunctionSource(t, source, "func (a *App) ApplyChanges") - - for _, literal := range []string{ - "\u4e8b\u52a1\u63d0\u4ea4\u6210\u529f", - "\u5f53\u524d\u6570\u636e\u5e93\u7c7b\u578b\u4e0d\u652f\u6301\u6279\u91cf\u63d0\u4ea4", - } { - if strings.Contains(functionSource, literal) { - t.Fatalf("ApplyChanges still contains raw table edit commit text %q", literal) - } - } -} - func TestApplyChangesCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -401,46 +171,6 @@ func TestApplyChangesCatalogKeysExist(t *testing.T) { } } -func TestTableDataClearMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string][]string{ - "func buildTableDataClearSQLWithText": { - "\u5f53\u524d\u6570\u636e\u5e93\u7c7b\u578b", - "\u4e0d\u652f\u6301\u622a\u65ad\u8868", - "\u4e0d\u652f\u6301\u7684\u8868\u6570\u636e\u6e05\u7406\u6a21\u5f0f", - }, - "func tableDataClearActionLabels": { - "\u622a\u65ad\u8868", - "\u622a\u65ad", - "\u6e05\u7a7a\u8868", - "\u6e05\u7a7a", - }, - "func (a *App) runTableDataClear": { - "\u672a\u6307\u5b9a\u8981\u5904\u7406\u7684\u8868", - "\u5355\u6b21\u6700\u591a\u5904\u7406", - "\u5f20\u8868\uff0c\u5f53\u524d\u9009\u4e2d", - "\u5931\u8d25:", - "\uff08\u6ce8\u610f\uff1a\u524d ", - "\u4e14\u65e0\u6cd5\u6062\u590d", - "\"\u6210\u529f\"", - }, - } - - for signature, literals := range checks { - functionSource := methodsFileFunctionSource(t, source, signature) - for _, literal := range literals { - if strings.Contains(functionSource, literal) { - t.Fatalf("%s still contains raw table data clear text %q", signature, literal) - } - } - } -} - func TestTableDataClearCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -469,110 +199,6 @@ func TestTableDataClearCatalogKeysExist(t *testing.T) { } } -func TestExportBackendMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string][]string{ - "func (a *App) ExportConnectionsPackage": { - "Export Connections", - "\u5bfc\u51fa\u5b8c\u6210", - }, - "func (a *App) ExportTable": { - "Export %s", - "\u5bfc\u51fa\u5b8c\u6210", - "Message: \"\u5199\u5165\u5931\u8d25\uff1a", - "\u6b63\u5728\u51c6\u5907\u5bfc\u51fa", - "\u6b63\u5728\u5bfc\u51fa SQL \u6587\u4ef6", - }, - "func (a *App) exportTablesSQL": { - "\u65e0\u6548\u7684\u5bfc\u51fa\u6a21\u5f0f", - "Export Tables (SQL)", - "\u5bfc\u51fa\u5b8c\u6210", - "\u6b63\u5728\u51c6\u5907\u6279\u91cf\u5bf9\u8c61\u5bfc\u51fa", - }, - "func (a *App) ExportDatabaseSQL": { - "\u6570\u636e\u5e93\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a", - "Export %s (SQL)", - "\u5bfc\u51fa\u5b8c\u6210", - }, - "func (a *App) ExportSchemaSQL": { - "\u6570\u636e\u5e93\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a", - "\u6a21\u5f0f\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a", - "Export %s.%s (SQL)", - "\u672a\u5728\u6a21\u5f0f %s \u4e0b\u83b7\u53d6\u5230\u53ef\u5bfc\u51fa\u7684\u8868\u6216\u89c6\u56fe", - "\u5bfc\u51fa\u5b8c\u6210", - }, - "func (a *App) ExportData": { - "Export Data", - "Message: \"\u5199\u5165\u5931\u8d25\uff1a", - "\u5bfc\u51fa\u5b8c\u6210", - "\u6b63\u5728\u51c6\u5907\u5bfc\u51fa", - }, - "func (a *App) ExportQuery": { - "\u67e5\u8be2\u8bed\u53e5\u4e0d\u80fd\u4e3a\u7a7a", - "Export Query Result", - "\u4ec5\u652f\u6301 SELECT/WITH \u67e5\u8be2\u5bfc\u51fa", - "Message: \"\u5199\u5165\u5931\u8d25\uff1a", - "\u5bfc\u51fa\u5b8c\u6210", - "\u6b63\u5728\u51c6\u5907\u5bfc\u51fa", - }, - "func (r *exportProgressReporter) Finalizing": { - "\u6b63\u5728\u5b8c\u6210\u6587\u4ef6\u5199\u5165", - "\u6b63\u5728\u5c01\u88c5\u5e76\u538b\u7f29 XLSX \u6587\u4ef6", - "\u6b63\u5728\u5b8c\u6210 CSV \u5199\u5165", - }, - "func (r *exportProgressReporter) Done": { - "\u5bfc\u51fa\u5b8c\u6210", - }, - "func (r *exportProgressReporter) Error": { - "\u5bfc\u51fa\u5931\u8d25", - }, - "func resolveBatchObjectsTargetName": { - "\u5f53\u524d\u6570\u636e\u5e93", - `fmt.Sprintf("%s · %d 个对象", safeDbName, len(objectNames))`, - }, - "func (a *App) ExportDatabasesSQLWithOptions": { - "\u8bf7\u81f3\u5c11\u9009\u62e9\u4e00\u4e2a\u6570\u636e\u5e93", - "Title: \"\u9009\u62e9\u6279\u91cf\u5bfc\u51fa\u76ee\u5f55\"", - `fmt.Sprintf("%d 个数据库", len(normalizedDbNames))`, - "\u6b63\u5728\u51c6\u5907\u6279\u91cf\u5e93\u5bfc\u51fa", - `fmt.Sprintf("正在导出 %s (%d/%d)", name, index+1, len(normalizedDbNames))`, - "Message: \"\u5bfc\u51fa\u5b8c\u6210\"", - }, - "func (a *App) exportDatabaseSQLToFile": { - "\u6570\u636e\u5e93\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a", - }, - "func (c *countingExportConsumer) SetColumns": { - "\u6b63\u5728\u5199\u5165\u6587\u4ef6", - }, - "func (c *countingExportConsumer) ConsumeRow": { - "\u6b63\u5728\u5199\u5165\u6587\u4ef6", - }, - "func (c *countingExportConsumer) ConsumeRowValues": { - "\u6b63\u5728\u5199\u5165\u6587\u4ef6", - }, - "func exportQueryResultToFile": { - "\u6b63\u5728\u67e5\u8be2\u6570\u636e", - }, - "func writeRowsToFileWithReporter": { - "\u6b63\u5728\u5199\u5165\u6587\u4ef6", - }, - } - - for signature, literals := range checks { - functionSource := methodsFileFunctionSource(t, source, signature) - for _, literal := range literals { - if strings.Contains(functionSource, literal) { - t.Fatalf("%s still contains raw export text %q", signature, literal) - } - } - } -} - func TestExportBackendCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -623,51 +249,6 @@ func TestExportBackendCatalogKeysExist(t *testing.T) { } } -func TestExecuteSQLFileMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - for _, literal := range []string{ - "\u6587\u4ef6\u8def\u5f84\u4e3a\u7a7a", - "\u65e0\u6cd5\u6253\u5f00\u6587\u4ef6:", - "\u7528\u6237\u53d6\u6d88\u6267\u884c", - "\u6267\u884c\u5df2\u53d6\u6d88\u3002\u5df2\u6267\u884c", - "\u6587\u4ef6\u8bfb\u53d6\u9519\u8bef:", - "\u6267\u884c\u5b8c\u6210\u3002\u6210\u529f", - "\u9519\u8bef\u8be6\u60c5\uff08\u524d ", - "\u6761\u9519\u8bef\u672a\u663e\u793a", - "\u5df2\u53d1\u9001\u53d6\u6d88\u8bf7\u6c42", - "\u672a\u627e\u5230\u8be5\u4efb\u52a1", - "\u6279\u91cf\u6267\u884c\u5931\u8d25:", - "\u56de\u6eda\u5931\u8d25:", - "\u6761\u8bed\u53e5\u6267\u884c\u5931\u8d25", - "\u6761\u8d77\u7684\u6279\u91cf\u8bed\u53e5\u6267\u884c\u5931\u8d25", - } { - if strings.Contains(source, literal) { - t.Fatalf("methods_file.go still contains raw SQL file execution text %q", literal) - } - } -} - -func TestAppLogBackendMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - source := string(sourceBytes) - - for _, literal := range []string{ - "\u5f53\u524d\u672a\u627e\u5230 GoNavi \u65e5\u5fd7\u6587\u4ef6", - } { - if strings.Contains(source, literal) { - t.Fatalf("methods_file.go still contains raw app log backend text %q", literal) - } - } -} - func TestExecuteSQLFileMessageCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { diff --git a/internal/app/methods_file_ssh_key_test.go b/internal/app/methods_file_ssh_key_test.go index 194fc99d..4011c847 100644 --- a/internal/app/methods_file_ssh_key_test.go +++ b/internal/app/methods_file_ssh_key_test.go @@ -3,7 +3,6 @@ package app import ( "os" "path/filepath" - "strings" "testing" ) @@ -42,33 +41,3 @@ func TestResolveFileOpenDialogDirectoryHandlesExtensionlessSSHKeys(t *testing.T) } } -func TestSelectSSHKeyFileSourceAllowsExtensionlessKeys(t *testing.T) { - source, err := os.ReadFile("methods_file.go") - if err != nil { - t.Fatalf("read methods_file.go: %v", err) - } - text := string(source) - - selectFnStart := strings.Index(text, "func (a *App) SelectSSHKeyFile(") - if selectFnStart < 0 { - t.Fatal("SelectSSHKeyFile not found") - } - selectFnEnd := strings.Index(text[selectFnStart:], "\nfunc (a *App) ") - if selectFnEnd < 0 { - t.Fatal("SelectSSHKeyFile end not found") - } - fn := text[selectFnStart : selectFnStart+selectFnEnd] - - if !strings.Contains(fn, "resolveFileOpenDialogDirectory(currentPath, fallbackDir)") { - t.Fatal("SelectSSHKeyFile should resolve default directory for extensionless key paths") - } - if !strings.Contains(fn, "ShowHiddenFiles: true") { - t.Fatal("SelectSSHKeyFile should show hidden files so ~/.ssh keys are visible") - } - if strings.Contains(fn, `Pattern: "*.pem;*.key;*.ppk`) || strings.Contains(fn, "id_rsa*") { - t.Fatal("SelectSSHKeyFile must not restrict filters to extension-only or id_rsa globs") - } - if !strings.Contains(fn, `Pattern: "*.*"`) { - t.Fatal("SelectSSHKeyFile should allow all files for extensionless OpenSSH keys") - } -} diff --git a/internal/app/methods_redis_i18n_test.go b/internal/app/methods_redis_i18n_test.go index c7094ee5..86b9dfe9 100644 --- a/internal/app/methods_redis_i18n_test.go +++ b/internal/app/methods_redis_i18n_test.go @@ -2,10 +2,8 @@ package app import ( "errors" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" redislib "GoNavi-Wails/internal/redis" "GoNavi-Wails/shared/i18n" @@ -25,128 +23,6 @@ func redisFunctionSource(t *testing.T, source string, signature string) string { return source[start : start+len(signature)+end] } -func TestRedisBackendOperationMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_redis.go") - if err != nil { - t.Fatalf("read methods_redis.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]struct { - rawMessages []string - keys []string - }{ - "func (a *App) RedisConnect": { - rawMessages: []string{`Message: "连接成功"`}, - keys: []string{"redis.backend.message.connect_success"}, - }, - "func (a *App) RedisTestConnection": { - rawMessages: []string{ - `Message: "连接成功"`, - `fmt.Sprintf("连接成功但释放测试连接失败:%v", closeErr)`, - }, - keys: []string{ - "redis.backend.message.connect_success", - "redis.backend.error.test_connection_close_failed", - }, - }, - "func (a *App) RedisSetString": { - rawMessages: []string{`Message: "设置成功"`}, - keys: []string{"redis.backend.message.set_success"}, - }, - "func (a *App) RedisSetHashField": { - rawMessages: []string{`Message: "设置成功"`}, - keys: []string{"redis.backend.message.set_success"}, - }, - "func (a *App) RedisSetTTL": { - rawMessages: []string{`Message: "设置成功"`}, - keys: []string{"redis.backend.message.set_success"}, - }, - "func (a *App) RedisExecuteCommand": { - rawMessages: []string{`Message: "命令不能为空"`}, - keys: []string{"redis.backend.error.command_required"}, - }, - "func (a *App) RedisSelectDB": { - rawMessages: []string{`Message: "切换成功"`}, - keys: []string{"redis.backend.message.select_db_success"}, - }, - "func (a *App) RedisRenameKey": { - rawMessages: []string{`Message: "重命名成功"`}, - keys: []string{"redis.backend.message.rename_success"}, - }, - "func (a *App) RedisDeleteHashField": { - rawMessages: []string{`Message: "删除成功"`}, - keys: []string{"redis.backend.message.delete_success"}, - }, - "func (a *App) RedisListPush": { - rawMessages: []string{`Message: "添加成功"`}, - keys: []string{"redis.backend.message.add_success"}, - }, - "func (a *App) RedisListSet": { - rawMessages: []string{`Message: "设置成功"`}, - keys: []string{"redis.backend.message.set_success"}, - }, - "func (a *App) RedisListRemove": { - rawMessages: []string{`Message: "删除成功"`}, - keys: []string{"redis.backend.message.delete_success"}, - }, - "func (a *App) RedisSetAdd": { - rawMessages: []string{`Message: "添加成功"`}, - keys: []string{"redis.backend.message.add_success"}, - }, - "func (a *App) RedisSetRemove": { - rawMessages: []string{`Message: "删除成功"`}, - keys: []string{"redis.backend.message.delete_success"}, - }, - "func (a *App) RedisZSetAdd": { - rawMessages: []string{`Message: "添加成功"`}, - keys: []string{"redis.backend.message.add_success"}, - }, - "func (a *App) RedisZSetRemove": { - rawMessages: []string{`Message: "删除成功"`}, - keys: []string{"redis.backend.message.delete_success"}, - }, - "func (a *App) RedisStreamAdd": { - rawMessages: []string{`Message: "添加成功"`}, - keys: []string{"redis.backend.message.add_success"}, - }, - "func (a *App) RedisStreamDelete": { - rawMessages: []string{`Message: "删除成功"`}, - keys: []string{"redis.backend.message.delete_success"}, - }, - "func (a *App) RedisFlushDB": { - rawMessages: []string{`Message: "清空成功"`}, - keys: []string{"redis.backend.message.flush_success"}, - }, - "func (a *App) RedisExportKeys": { - keys: []string{ - "redis.backend.error.export_no_keys", - "redis.backend.message.export_success", - }, - }, - "func (a *App) RedisImportKeys": { - keys: []string{ - "redis.backend.error.import_no_keys_selected", - "redis.backend.error.import_payload_invalid", - "redis.backend.message.import_success", - }, - }, - } - - for signature, check := range checks { - functionSource := redisFunctionSource(t, source, signature) - for _, rawMessage := range check.rawMessages { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw Redis operation result %q", signature, rawMessage) - } - } - for _, key := range check.keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference Redis i18n key %q", signature, key) - } - } - } -} func TestRedisBackendOperationMessageCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/app/methods_saved_connections_test.go b/internal/app/methods_saved_connections_test.go index e5fff1ff..d452998e 100644 --- a/internal/app/methods_saved_connections_test.go +++ b/internal/app/methods_saved_connections_test.go @@ -1,11 +1,8 @@ package app import ( - "os" "reflect" - "strings" "testing" - "GoNavi-Wails/internal/connection" ) @@ -373,21 +370,6 @@ func TestDuplicateConnectionClonesSecretBundle(t *testing.T) { } } -func TestSavedConnectionsDoesNotHardcodeDuplicateNameChinese(t *testing.T) { - source, err := os.ReadFile("saved_connections.go") - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - text := string(source) - for _, legacy := range []string{ - `trimmedBaseName = "连接"`, - `suffix := " - 副本"`, - } { - if strings.Contains(text, legacy) { - t.Fatalf("saved_connections.go still hardcodes duplicate connection text %s", legacy) - } - } -} func TestSaveGlobalProxyReturnsSecretlessView(t *testing.T) { app := NewAppWithSecretStore(newFakeAppSecretStore()) diff --git a/internal/app/methods_sync_test.go b/internal/app/methods_sync_test.go index 96413053..56976031 100644 --- a/internal/app/methods_sync_test.go +++ b/internal/app/methods_sync_test.go @@ -1,10 +1,8 @@ package app import ( - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" datasync "GoNavi-Wails/internal/sync" "GoNavi-Wails/shared/i18n" @@ -221,32 +219,6 @@ func TestResolveDataSyncConfigSecretsRestoresOracleServiceNameFromSavedConnectio } } -func TestMethodsSyncSecretRestoreMessagesUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_sync.go") - if err != nil { - t.Fatalf("read methods_sync.go: %v", err) - } - source := string(sourceBytes) - functionSource := methodsSyncFunctionSource(t, source, "func (a *App) resolveDataSyncConfigSecrets") - - for _, literal := range []string{ - `"恢复源数据库连接密文失败: %w"`, - `"恢复目标数据库连接密文失败: %w"`, - } { - if strings.Contains(functionSource, literal) { - t.Fatalf("resolveDataSyncConfigSecrets still contains raw DataSync secret restore text %q", literal) - } - } - - for _, key := range []string{ - "data_sync.backend.error.restore_source_secret_failed", - "data_sync.backend.error.restore_target_secret_failed", - } { - if !strings.Contains(functionSource, key) { - t.Fatalf("resolveDataSyncConfigSecrets does not reference DataSync backend i18n key %q", key) - } - } -} func TestMethodsSyncSecretRestoreCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -331,18 +303,3 @@ func TestDataSyncPreviewSourceFailureUsesLocalizedMessage(t *testing.T) { } } -func TestMethodsSyncPreviewSuccessMessageUsesLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("methods_sync.go") - if err != nil { - t.Fatalf("read methods_sync.go: %v", err) - } - source := string(sourceBytes) - functionSource := methodsSyncFunctionSource(t, source, "func (a *App) DataSyncPreview") - - if strings.Contains(functionSource, `Message: "OK"`) { - t.Fatal(`DataSyncPreview still returns raw success text "OK"`) - } - if !strings.Contains(functionSource, "data_sync.backend.result.preview_ready") { - t.Fatal("DataSyncPreview does not reference data_sync.backend.result.preview_ready") - } -} diff --git a/internal/app/saved_queries_test.go b/internal/app/saved_queries_test.go index a5fa8e23..c4513557 100644 --- a/internal/app/saved_queries_test.go +++ b/internal/app/saved_queries_test.go @@ -611,15 +611,6 @@ func TestSavedQueryRepositoryLocalizesGeneratedName(t *testing.T) { } } -func TestSavedQueriesDoesNotHardcodeGeneratedNameChinese(t *testing.T) { - source, err := os.ReadFile("saved_queries.go") - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - if strings.Contains(string(source), `fmt.Sprintf("查询-%d", index+1)`) { - t.Fatal("saved_queries.go still hardcodes generated saved query name") - } -} func TestSavedQueryRepositorySerializesConcurrentWrites(t *testing.T) { app := NewAppWithSecretStore(secretstore.NewUnavailableStore("test")) diff --git a/internal/app/security_update_engine_test.go b/internal/app/security_update_engine_test.go index c3b0a555..13da5112 100644 --- a/internal/app/security_update_engine_test.go +++ b/internal/app/security_update_engine_test.go @@ -171,57 +171,8 @@ func TestGetSecurityUpdateStatusIncludesPendingAIProviderIssuesBeforeStart(t *te } } -func TestSecurityUpdateEngineDoesNotHardcodeAIProviderIssueMessages(t *testing.T) { - source, err := os.ReadFile("security_update_engine.go") - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - text := string(source) - for _, legacy := range []string{ - "AI 提供商配置需要补充后才能完成安全更新", - "AI 提供商配置已不存在或仍需重新保存后才能完成安全更新", - "AI 提供商配置仍保存在当前应用配置中,完成安全更新后会迁入新的安全存储。", - } { - if strings.Contains(text, legacy) { - t.Fatalf("security_update_engine.go still hardcodes AI provider issue message %q", legacy) - } - } -} -func TestSecurityUpdateEngineDoesNotHardcodeConnectionIssueMessages(t *testing.T) { - source, err := os.ReadFile("security_update_engine.go") - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - text := string(source) - for _, legacy := range []string{ - "连接配置已不存在或仍需重新保存后才能完成安全更新", - "连接配置仍需补充后才能完成安全更新", - "连接密码已丢失,请重新保存后再继续", - } { - if strings.Contains(text, legacy) { - t.Fatalf("security_update_engine.go still hardcodes connection issue message %q", legacy) - } - } -} -func TestSecurityUpdateEngineDoesNotHardcodeGlobalProxyIssueMessages(t *testing.T) { - source, err := os.ReadFile("security_update_engine.go") - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - text := string(source) - for _, legacy := range []string{ - "全局代理", - "全局代理配置已不存在或仍需重新保存后才能完成安全更新", - "全局代理密码仍需补充后才能完成安全更新", - "全局代理密码已丢失,请重新保存后再继续", - } { - if strings.Contains(text, legacy) { - t.Fatalf("security_update_engine.go still hardcodes global proxy issue text %q", legacy) - } - } -} func TestRetrySecurityUpdateCurrentRoundReusesMigrationIDAfterPendingIssueIsFixed(t *testing.T) { store := newFakeAppSecretStore() diff --git a/internal/db/batch_insert_test.go b/internal/db/batch_insert_test.go index 58214cc2..83c1c12b 100644 --- a/internal/db/batch_insert_test.go +++ b/internal/db/batch_insert_test.go @@ -5,10 +5,8 @@ import ( "database/sql/driver" "errors" "fmt" - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -385,43 +383,6 @@ func TestBatchInsertErrorsUseCurrentLanguage(t *testing.T) { } } -func TestBatchInsertErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("batch_insert.go") - if err != nil { - t.Fatalf("read batch_insert.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("表名不能为空")`, - `fmt.Errorf("列名引用函数不能为空")`, - `fmt.Errorf("占位符函数不能为空")`, - `fmt.Errorf("执行函数不能为空")`, - `fmt.Errorf("字面量函数不能为空")`, - `fmt.Errorf("插入失败:%v", err)`, - `fmt.Errorf("插入失败:%v; sql=%s", err, query)`, - `fmt.Errorf("插入未生效:未影响任何行")`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("batch_insert.go still contains raw batch insert text %q", rawMessage) - } - } - - for _, key := range []string{ - "db.backend.error.table_name_required", - "db.backend.error.batch_insert_quote_column_required", - "db.backend.error.batch_insert_placeholder_required", - "db.backend.error.batch_insert_exec_required", - "db.backend.error.batch_insert_literal_required", - "db.backend.error.batch_insert_failed", - "db.backend.error.batch_insert_failed_with_sql", - "db.backend.error.batch_insert_no_rows_affected", - } { - if !strings.Contains(source, key) { - t.Fatalf("batch_insert.go does not reference i18n key %q", key) - } - } -} func TestBatchInsertErrorCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/clickhouse_impl_test.go b/internal/db/clickhouse_impl_test.go index a59099ba..d06088f2 100644 --- a/internal/db/clickhouse_impl_test.go +++ b/internal/db/clickhouse_impl_test.go @@ -232,21 +232,6 @@ func TestClickHouseCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) { } } -func TestClickHouseCreateStatementSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("clickhouse_impl.go") - if err != nil { - t.Fatalf("read clickhouse_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawClickHouseCreateStatementNotFoundText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("clickhouse_impl.go still contains raw create-statement text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("clickhouse_impl.go does not reference db.backend.error.create_table_statement_not_found") - } -} func TestClickHouseApplyChangesErrorsUseCurrentLanguage(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) @@ -348,28 +333,6 @@ func TestClickHouseTableNameRequiredUsesCurrentLanguage(t *testing.T) { } } -func TestClickHouseApplyChangesErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("clickhouse_impl.go") - if err != nil { - t.Fatalf("read clickhouse_impl.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("` + rawClickHouseTableNameRequiredText() + `")`, - `fmt.Errorf("delete error: %v; sql=%s", err, query)`, - `fmt.Errorf("update error: %v; sql=%s", err, query)`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("clickhouse_impl.go still contains raw ApplyChanges text %q", rawMessage) - } - } - for _, key := range clickHouseApplyChangesI18nKeys() { - if !strings.Contains(source, key) { - t.Fatalf("clickhouse_impl.go does not reference i18n key %q", key) - } - } -} func TestClickHouseApplyChangesCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -674,32 +637,6 @@ func TestClickHouseConnectFailureSummaryUsesCurrentLanguage(t *testing.T) { } } -func TestClickHouseProtocolFailureSourceUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("clickhouse_impl.go") - if err != nil { - t.Fatalf("read clickhouse_impl.go: %v", err) - } - source := string(sourceBytes) - for _, rawMessage := range []string{ - "当前 ClickHouse HTTP 端口不支持 client_protocol_version", - "服务端响应不像 Native 握手", - "服务端响应不像 HTTP 响应", - "未知错误", - "未获取到驱动返回的错误详情", - "ClickHouse 连接验证失败", - "第%d次 TLS 配置失败", - "第%d次连接验证失败", - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("clickhouse_impl.go still contains raw user-facing ClickHouse protocol text %q", rawMessage) - } - } - for _, key := range clickHouseProtocolFailureI18nKeys { - if !strings.Contains(source, key) { - t.Fatalf("clickhouse_impl.go does not reference i18n key %q", key) - } - } -} func TestClickHouseProtocolFailureCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/custom_impl_test.go b/internal/db/custom_impl_test.go index e9bf8194..21ea6f83 100644 --- a/internal/db/custom_impl_test.go +++ b/internal/db/custom_impl_test.go @@ -5,11 +5,9 @@ import ( "database/sql" "database/sql/driver" "errors" - "os" "strings" "sync" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -216,36 +214,6 @@ func TestCustomDBApplyChangesErrorsUseCurrentLanguage(t *testing.T) { }) } -func TestCustomDBApplyChangesErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("custom_impl.go") - if err != nil { - t.Fatalf("read custom_impl.go: %v", err) - } - source := string(sourceBytes) - functionSource := databaseFunctionSource(t, source, "func (c *CustomDB) ApplyChanges(tableName string, changes connection.ChangeSet) error") - - rawConnectionNotOpenText := string([]rune{0x8fde, 0x63a5, 0x672a, 0x6253, 0x5f00}) - rawDeleteFailedText := string([]rune{0x5220, 0x9664, 0x5931, 0x8d25}) - rawUpdateKeyConditionsRequiredText := string([]rune{0x66f4, 0x65b0, 0x64cd, 0x4f5c, 0x9700, 0x8981, 0x4e3b, 0x952e, 0x6761, 0x4ef6}) - rawUpdateFailedText := string([]rune{0x66f4, 0x65b0, 0x5931, 0x8d25}) - - for _, rawMessage := range []string{ - `fmt.Errorf("` + rawConnectionNotOpenText + `")`, - `fmt.Errorf("` + rawDeleteFailedText + `:%v", err)`, - `fmt.Errorf("` + rawUpdateKeyConditionsRequiredText + `")`, - `fmt.Errorf("` + rawUpdateFailedText + `:%v", err)`, - } { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("CustomDB ApplyChanges still contains raw user-visible text %q", rawMessage) - } - } - - for _, key := range customDBApplyChangesI18nKeys() { - if !strings.Contains(functionSource, key) { - t.Fatalf("CustomDB ApplyChanges does not reference i18n key %q", key) - } - } -} func TestCustomDBApplyChangesCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -335,30 +303,6 @@ func TestCustomDBBasicExecutionConnectionNotOpenUsesCurrentLanguage(t *testing.T } } -func TestCustomDBBasicExecutionConnectionNotOpenSourcesUseI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("custom_impl.go") - if err != nil { - t.Fatalf("read custom_impl.go: %v", err) - } - source := string(sourceBytes) - rawConnectionNotOpenText := string([]rune{0x8fde, 0x63a5, 0x672a, 0x6253, 0x5f00}) - - for _, signature := range []string{ - "func (c *CustomDB) Ping() error", - "func (c *CustomDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error)", - "func (c *CustomDB) Query(query string) ([]map[string]interface{}, []string, error)", - "func (c *CustomDB) ExecContext(ctx context.Context, query string) (int64, error)", - "func (c *CustomDB) Exec(query string) (int64, error)", - } { - functionSource := databaseFunctionSource(t, source, signature) - if strings.Contains(functionSource, `fmt.Errorf("`+rawConnectionNotOpenText+`")`) { - t.Fatalf("%s still contains raw connection-not-open text", signature) - } - if !strings.Contains(functionSource, "db.backend.error.connection_not_open") { - t.Fatalf("%s does not reference connection-not-open i18n key", signature) - } - } -} func TestCustomDBConnectReportsUnsupportedODBCDriverName(t *testing.T) { db := &CustomDB{} diff --git a/internal/db/dameng_i18n_test.go b/internal/db/dameng_i18n_test.go index b63f6f45..1ba14dd6 100644 --- a/internal/db/dameng_i18n_test.go +++ b/internal/db/dameng_i18n_test.go @@ -6,11 +6,9 @@ import ( "database/sql" "database/sql/driver" "io" - "os" "strings" "sync" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -99,18 +97,3 @@ func TestDamengCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) { } } -func TestDamengCreateStatementSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("dameng_impl.go") - if err != nil { - t.Fatalf("read dameng_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawDamengCreateStatementNotFoundText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("dameng_impl.go still contains raw create-statement text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("dameng_impl.go does not reference db.backend.error.create_table_statement_not_found") - } -} diff --git a/internal/db/database_i18n_test.go b/internal/db/database_i18n_test.go index 041e3281..f1b62e07 100644 --- a/internal/db/database_i18n_test.go +++ b/internal/db/database_i18n_test.go @@ -91,43 +91,6 @@ func TestRequireSingleRowAffectedUsesLocalizedText(t *testing.T) { } } -func TestRequireSingleRowAffectedSourceUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("database.go") - if err != nil { - t.Fatalf("read database.go: %v", err) - } - source := string(sourceBytes) - functionSource := databaseFunctionSource(t, source, "func requireSingleRowAffected(result sql.Result, action rowMutationAction) error") - actionSource := databaseFunctionSource(t, source, "func localizedRowMutationAction(action rowMutationAction) string") - - for _, rawMessage := range []string{ - `fmt.Errorf("%s未生效:无法确认影响行数:%v", action, err)`, - `fmt.Errorf("%s未生效:未匹配到任何行", action)`, - `fmt.Errorf("%s未生效:影响了 %d 行,期望只影响 1 行", action, affected)`, - } { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("requireSingleRowAffected still contains raw row affected text %q", rawMessage) - } - } - - for _, key := range []string{ - "db.backend.error.row_action_not_effective_rows_affected_unknown", - "db.backend.error.row_action_not_effective_no_rows_matched", - "db.backend.error.row_action_not_effective_multiple_rows", - } { - if !strings.Contains(functionSource, key) { - t.Fatalf("requireSingleRowAffected does not reference i18n key %q", key) - } - } - for _, key := range []string{ - "db.backend.action.delete", - "db.backend.action.update", - } { - if !strings.Contains(actionSource, key) { - t.Fatalf("localizedRowMutationAction does not reference i18n key %q", key) - } - } -} func TestDatabaseRowAffectedCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -221,30 +184,6 @@ func TestSQLConnTransactionExecerUsesCurrentLanguageForConnectionNotOpen(t *test } } -func TestDatabaseConnectionNotOpenSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("database.go") - if err != nil { - t.Fatalf("read database.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]string{ - "func (e *sqlConnStatementExecer) ExecContext(ctx context.Context, query string) (int64, error)": "db.backend.error.connection_not_open", - "func (e *sqlConnStatementExecer) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error)": "db.backend.error.connection_not_open", - "func (e *sqlConnStatementExecer) QueryMultiContext(ctx context.Context, query string) ([]connection.ResultSetData, error)": "db.backend.error.connection_not_open", - "func (e *sqlConnTransactionExecer) activeConn() (*sql.Conn, error)": "db.backend.error.connection_not_open", - } - - for signature, key := range checks { - functionSource := databaseFunctionSource(t, source, signature) - if strings.Contains(functionSource, `fmt.Errorf("连接未打开")`) { - t.Fatalf("%s still contains raw connection-not-open text", signature) - } - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference i18n key %q", signature, key) - } - } -} func TestDatabaseConnectionNotOpenCatalogKeyExists(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -304,28 +243,6 @@ func TestWrapDatabaseConnectionErrorsUseCurrentLanguage(t *testing.T) { } } -func TestDatabaseConnectionWrapperHelperUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("database.go") - if err != nil { - t.Fatalf("read database.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string]string{ - "func wrapDatabaseConnectionOpenError(err error) error": "db.backend.error.connection_open_failed_prefix", - "func wrapDatabaseConnectionVerifyError(err error) error": "db.backend.error.connection_verify_failed_prefix", - } - - for signature, key := range checks { - functionSource := databaseFunctionSource(t, source, signature) - if strings.Contains(functionSource, `fmt.Errorf("打开数据库连接失败:%w", err)`) || strings.Contains(functionSource, `fmt.Errorf("连接建立后验证失败:%w", err)`) { - t.Fatalf("%s still contains raw database connection wrapper text", signature) - } - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference i18n key %q", signature, key) - } - } -} func TestDatabaseConnectionWrapperSourcesUseI18nHelpers(t *testing.T) { type sourceCheck struct { @@ -456,32 +373,6 @@ func TestFormatCustomDriverOpenErrorUsesCurrentLanguageForUnknownDrivers(t *test } } -func TestFormatCustomDriverOpenErrorSourceUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("custom_impl.go") - if err != nil { - t.Fatalf("read custom_impl.go: %v", err) - } - source := string(sourceBytes) - functionSource := databaseFunctionSource(t, source, "func formatCustomDriverOpenError(driver string, err error) error") - - for _, rawMessage := range []string{ - `fmt.Errorf("打开数据库连接失败:自定义连接不支持直接填写系统 ODBC/JDBC 驱动名 %q;请填写 GoNavi 已注册的 Go database/sql 驱动名。当前版本未注册通用 ODBC 驱动,因此暂不支持通过 %q 连接 InterSystems IRIS:%w", driver, driver, err)`, - `fmt.Errorf("打开数据库连接失败:自定义连接驱动 %q 未在 GoNavi 中注册;请填写已注册的 Go database/sql 驱动名,不能填写系统 ODBC/JDBC 驱动名:%w", driver, err)`, - } { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("formatCustomDriverOpenError still contains raw custom driver guidance %q", rawMessage) - } - } - - for _, key := range []string{ - "db.backend.error.custom_driver_system_odbc_unsupported_prefix", - "db.backend.error.custom_driver_unregistered_prefix", - } { - if !strings.Contains(functionSource, key) { - t.Fatalf("formatCustomDriverOpenError does not reference i18n key %q", key) - } - } -} func TestCustomDriverOpenErrorCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -525,23 +416,6 @@ func TestNewDatabaseUnsupportedTypeUsesCurrentLanguage(t *testing.T) { } } -func TestNewDatabaseUnsupportedTypeSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("database.go") - if err != nil { - t.Fatalf("read database.go: %v", err) - } - source := string(sourceBytes) - functionSource := databaseFunctionSource(t, source, "func NewDatabase(dbType string) (Database, error)") - - rawUnsupportedDatabaseTypeText := "\u4e0d\u652f\u6301\u7684\u6570\u636e\u5e93\u7c7b\u578b" - rawUnsupportedDatabaseTypeSnippet := `fmt.Errorf("` + rawUnsupportedDatabaseTypeText + `:%s", dbType)` - if strings.Contains(functionSource, rawUnsupportedDatabaseTypeSnippet) { - t.Fatal("NewDatabase still contains raw unsupported database type text") - } - if !strings.Contains(functionSource, "db.backend.error.unsupported_database_type") { - t.Fatal("NewDatabase does not reference db.backend.error.unsupported_database_type") - } -} func TestNewDatabaseUnsupportedTypeCatalogKeyExists(t *testing.T) { catalogs, err := i18n.LoadCatalogs() @@ -620,41 +494,6 @@ func TestTransactionExecerStateErrorsUseCurrentLanguage(t *testing.T) { } } -func TestDatabaseTransactionStateSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("database.go") - if err != nil { - t.Fatalf("read database.go: %v", err) - } - source := string(sourceBytes) - - checks := map[string][]string{ - "func (e *sqlConnTransactionExecer) activeConn() (*sql.Conn, error)": { - "db.backend.error.connection_not_open", - "db.backend.error.transaction_already_finished", - }, - "func (e *sqlTxStatementExecer) activeTx() (*sql.Tx, error)": { - "db.backend.error.transaction_not_open", - "db.backend.error.transaction_already_finished", - }, - } - - for signature, keys := range checks { - functionSource := databaseFunctionSource(t, source, signature) - for _, rawMessage := range []string{ - `fmt.Errorf("` + rawTransactionNotOpenText + `")`, - `fmt.Errorf("` + rawTransactionAlreadyFinishedText + `")`, - } { - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("%s still contains raw transaction state text %q", signature, rawMessage) - } - } - for _, key := range keys { - if !strings.Contains(functionSource, key) { - t.Fatalf("%s does not reference i18n key %q", signature, key) - } - } - } -} func TestDatabaseTransactionStateCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/driver_support_test.go b/internal/db/driver_support_test.go index 5687ea3c..eccb482c 100644 --- a/internal/db/driver_support_test.go +++ b/internal/db/driver_support_test.go @@ -257,22 +257,6 @@ func TestDriverRuntimeSupportStatusUsesCurrentLanguageForMissingOptionalDriverAg } } -func TestResolveExternalDriverRootSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("driver_support.go") - if err != nil { - t.Fatalf("read driver_support.go: %v", err) - } - source := string(sourceBytes) - functionSource := driverSupportFunctionSource(t, source, "func resolveExternalDriverRoot(downloadDir string) (string, error)") - rawCreateDirectoryWrapper := "fmt.Errorf(\"\\u521b\\u5efa\\u9a71\\u52a8\\u76ee\\u5f55\\u5931\\u8d25\\uff1a%w\", err)" - - if strings.Contains(functionSource, rawCreateDirectoryWrapper) { - t.Fatal("resolveExternalDriverRoot still contains raw Chinese create-directory wrapper") - } - if !strings.Contains(functionSource, "driver_manager.backend.error.create_directory_failed") { - t.Fatal("resolveExternalDriverRoot does not reference driver_manager.backend.error.create_directory_failed") - } -} func TestResolveExternalDriverRootUsesCurrentLanguageForCreateDirectoryFailure(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) diff --git a/internal/db/elasticsearch_i18n_test.go b/internal/db/elasticsearch_i18n_test.go index b98fb654..0283c1c7 100644 --- a/internal/db/elasticsearch_i18n_test.go +++ b/internal/db/elasticsearch_i18n_test.go @@ -4,10 +4,8 @@ package db import ( "context" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -111,17 +109,3 @@ func TestElasticsearchConnectionNotOpenUsesCurrentLanguage(t *testing.T) { } } -func TestElasticsearchConnectionNotOpenSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("elasticsearch_impl.go") - if err != nil { - t.Fatalf("read elasticsearch_impl.go: %v", err) - } - source := string(sourceBytes) - - if strings.Contains(source, `fmt.Errorf("`+rawElasticsearchConnectionNotOpenText+`")`) { - t.Fatal("elasticsearch_impl.go still contains raw connection-not-open text") - } - if !strings.Contains(source, "db.backend.error.connection_not_open") { - t.Fatal("elasticsearch_impl.go does not reference db.backend.error.connection_not_open") - } -} diff --git a/internal/db/highgo_i18n_test.go b/internal/db/highgo_i18n_test.go index 85ecc11d..a5f81f96 100644 --- a/internal/db/highgo_i18n_test.go +++ b/internal/db/highgo_i18n_test.go @@ -3,10 +3,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -69,18 +67,3 @@ func TestHighGoMetadataErrorsUseCurrentLanguage(t *testing.T) { } } -func TestHighGoMetadataErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("highgo_impl.go") - if err != nil { - t.Fatalf("read highgo_impl.go: %v", err) - } - source := string(sourceBytes) - rawMessage := `fmt.Errorf("` + rawHighGoTableNameRequiredText + `")` - - if strings.Contains(source, rawMessage) { - t.Fatalf("highgo_impl.go still contains raw HighGo metadata text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.table_name_required") { - t.Fatal("highgo_impl.go does not reference db.backend.error.table_name_required") - } -} diff --git a/internal/db/iris_i18n_test.go b/internal/db/iris_i18n_test.go index 377a08ab..07eb5860 100644 --- a/internal/db/iris_i18n_test.go +++ b/internal/db/iris_i18n_test.go @@ -3,10 +3,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -42,22 +40,6 @@ func TestIRISTableRefErrorsUseCurrentLanguage(t *testing.T) { } } -func TestIRISTableNameRequiredSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("iris_impl.go") - if err != nil { - t.Fatalf("read iris_impl.go: %v", err) - } - source := string(sourceBytes) - functionSource := databaseFunctionSource(t, source, "func parseIRISTableRef(defaultSchema, raw string) (irisTableRef, error)") - rawMessage := `fmt.Errorf("` + rawIRISTableNameRequiredText + `")` - - if strings.Contains(functionSource, rawMessage) { - t.Fatalf("parseIRISTableRef still contains raw IRIS table-name-required text %q", rawMessage) - } - if !strings.Contains(functionSource, "db.backend.error.table_name_required") { - t.Fatal("parseIRISTableRef does not reference db.backend.error.table_name_required") - } -} func TestIRISTableNameRequiredCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/kingbase_i18n_test.go b/internal/db/kingbase_i18n_test.go index 169e4e07..0976a7f1 100644 --- a/internal/db/kingbase_i18n_test.go +++ b/internal/db/kingbase_i18n_test.go @@ -3,10 +3,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -69,18 +67,3 @@ func TestKingbaseMetadataErrorsUseCurrentLanguage(t *testing.T) { } } -func TestKingbaseTableNameRequiredSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("kingbase_impl.go") - if err != nil { - t.Fatalf("read kingbase_impl.go: %v", err) - } - source := string(sourceBytes) - rawMessage := `fmt.Errorf("` + rawKingbaseTableNameRequiredText + `")` - - if strings.Contains(source, rawMessage) { - t.Fatalf("kingbase_impl.go still contains raw Kingbase table-name-required text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.table_name_required") { - t.Fatal("kingbase_impl.go does not reference db.backend.error.table_name_required") - } -} diff --git a/internal/db/mariadb_i18n_test.go b/internal/db/mariadb_i18n_test.go index 9022b395..9947bc7c 100644 --- a/internal/db/mariadb_i18n_test.go +++ b/internal/db/mariadb_i18n_test.go @@ -6,11 +6,9 @@ import ( "database/sql" "database/sql/driver" "io" - "os" "strings" "sync" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -100,21 +98,6 @@ func TestMariaDBCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) { } } -func TestMariaDBCreateStatementSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("mariadb_impl.go") - if err != nil { - t.Fatalf("read mariadb_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawMariaDBCreateStatementNotFoundText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("mariadb_impl.go still contains raw create-statement text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("mariadb_impl.go does not reference db.backend.error.create_table_statement_not_found") - } -} func TestMariaDBGetAllColumnsDatabaseRequiredUsesCurrentLanguage(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) @@ -136,18 +119,3 @@ func TestMariaDBGetAllColumnsDatabaseRequiredUsesCurrentLanguage(t *testing.T) { } } -func TestMariaDBGetAllColumnsDatabaseRequiredSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("mariadb_impl.go") - if err != nil { - t.Fatalf("read mariadb_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawMariaDBAllColumnsDatabaseRequiredText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("mariadb_impl.go still contains raw database-name text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.database_name_required") { - t.Fatal("mariadb_impl.go does not reference db.backend.error.database_name_required") - } -} diff --git a/internal/db/mqtt_i18n_test.go b/internal/db/mqtt_i18n_test.go index 860c5660..b17dccff 100644 --- a/internal/db/mqtt_i18n_test.go +++ b/internal/db/mqtt_i18n_test.go @@ -1,10 +1,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -53,45 +51,6 @@ func TestMQTTTimeoutMessagesUseCurrentLanguage(t *testing.T) { } } -func TestMQTTTimeoutSourceUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("mqtt_impl.go") - if err != nil { - t.Fatalf("read mqtt_impl.go: %v", err) - } - source := string(sourceBytes) - - checks := []struct { - signature string - rawText string - key string - }{ - { - signature: "func newPahoMQTTRuntime(config connection.ConnectionConfig) (mqttRuntime, error)", - rawText: `fmt.Errorf("MQTT 连接超时")`, - key: "db.backend.error.mqtt_connect_timeout", - }, - { - signature: "func (r *pahoMQTTRuntime) FetchMessages(ctx context.Context, request mqttFetchRequest) ([]mqttMessageRecord, error)", - rawText: `fmt.Errorf("MQTT 订阅超时")`, - key: "db.backend.error.mqtt_subscribe_timeout", - }, - { - signature: "func (r *pahoMQTTRuntime) Publish(ctx context.Context, command mqttPublishCommand) (int64, error)", - rawText: `fmt.Errorf("MQTT 发布超时")`, - key: "db.backend.error.mqtt_publish_timeout", - }, - } - - for _, tc := range checks { - functionSource := databaseFunctionSource(t, source, tc.signature) - if strings.Contains(functionSource, tc.rawText) { - t.Fatalf("%s still contains raw MQTT timeout text %q", tc.signature, tc.rawText) - } - if !strings.Contains(functionSource, tc.key) { - t.Fatalf("%s does not reference i18n key %q", tc.signature, tc.key) - } - } -} func TestMQTTTimeoutCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/mysql_i18n_test.go b/internal/db/mysql_i18n_test.go index 47b3ddbe..afd3a25f 100644 --- a/internal/db/mysql_i18n_test.go +++ b/internal/db/mysql_i18n_test.go @@ -4,11 +4,9 @@ import ( "database/sql" "database/sql/driver" "io" - "os" "strings" "sync" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -98,21 +96,6 @@ func TestMySQLCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) { } } -func TestMySQLCreateStatementSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("mysql_impl.go") - if err != nil { - t.Fatalf("read mysql_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawMySQLCreateStatementNotFoundText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("mysql_impl.go still contains raw create-statement text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("mysql_impl.go does not reference db.backend.error.create_table_statement_not_found") - } -} func TestMySQLGetAllColumnsDatabaseRequiredUsesCurrentLanguage(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) @@ -134,18 +117,3 @@ func TestMySQLGetAllColumnsDatabaseRequiredUsesCurrentLanguage(t *testing.T) { } } -func TestMySQLGetAllColumnsDatabaseRequiredSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("mysql_impl.go") - if err != nil { - t.Fatalf("read mysql_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawMySQLAllColumnsDatabaseRequiredText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("mysql_impl.go still contains raw database-name text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.database_name_required") { - t.Fatal("mysql_impl.go does not reference db.backend.error.database_name_required") - } -} diff --git a/internal/db/oceanbase_impl_test.go b/internal/db/oceanbase_impl_test.go index 33df035b..25144d1c 100644 --- a/internal/db/oceanbase_impl_test.go +++ b/internal/db/oceanbase_impl_test.go @@ -8,16 +8,13 @@ import ( "errors" "net" "net/url" - "os" "slices" "strconv" "strings" "testing" "time" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" - mysqlDriver "github.com/go-sql-driver/mysql" ) @@ -923,26 +920,6 @@ func TestOceanBaseOracleCreateStatementFallbackErrorUsesCurrentLanguage(t *testi } } -func TestOceanBaseOracleCreateStatementSourceUsesI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("oceanbase_impl.go") - if err != nil { - t.Fatalf("read oceanbase_impl.go: %v", err) - } - source := string(sourceBytes) - - if strings.Contains(source, `fmt.Errorf("未找到建表语句")`) { - t.Fatal("oceanbase_impl.go still contains raw create-statement-not-found error") - } - if strings.Contains(source, "OceanBase Oracle SHOW CREATE TABLE 兜底失败") { - t.Fatal("oceanbase_impl.go still contains raw OceanBase SHOW CREATE TABLE fallback wrapper") - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("oceanbase_impl.go does not reference db.backend.error.create_table_statement_not_found") - } - if !strings.Contains(source, "db.backend.error.oceanbase_oracle_show_create_table_fallback_failed") { - t.Fatal("oceanbase_impl.go does not reference OceanBase SHOW CREATE TABLE fallback i18n key") - } -} // 用户通过 ConnectionParams 设置 connectionAttributes 时,OceanBase MySQL wire 路径必须把 // 这些 attribute 透传到 go-sql-driver/mysql DSN,让 driver 在握手响应里发 CLIENT_CONNECT_ATTRS。 diff --git a/internal/db/oracle_i18n_test.go b/internal/db/oracle_i18n_test.go index 052655c9..52d9e2a3 100644 --- a/internal/db/oracle_i18n_test.go +++ b/internal/db/oracle_i18n_test.go @@ -5,11 +5,9 @@ import ( "database/sql/driver" "errors" "io" - "os" "strings" "sync" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -178,38 +176,6 @@ func TestOracleMetadataErrorsUseCurrentLanguage(t *testing.T) { }) } -func TestOracleUserVisibleMetadataErrorsDoNotReintroduceInlineChinese(t *testing.T) { - sourceBytes, err := os.ReadFile("oracle_impl.go") - if err != nil { - t.Fatalf("read oracle_impl.go: %v", err) - } - source := string(sourceBytes) - - getIndexesSource := oracleFunctionSource(t, source, "func (o *OracleDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error)") - if strings.Contains(getIndexesSource, `fmt.Errorf("表名不能为空")`) { - t.Fatal("GetIndexes still contains raw Chinese table-name-required text") - } - if !strings.Contains(getIndexesSource, "db.backend.error.table_name_required") { - t.Fatal("GetIndexes does not reference db.backend.error.table_name_required") - } - - getCreateStatementSource := oracleFunctionSource(t, source, "func (o *OracleDB) GetCreateStatement(dbName, tableName string) (string, error)") - rawCreateStatementMessage := `fmt.Errorf("` + rawOracleCreateStatementNotFoundText + `")` - if strings.Contains(getCreateStatementSource, rawCreateStatementMessage) { - t.Fatalf("GetCreateStatement still contains raw create-statement text %q", rawCreateStatementMessage) - } - if !strings.Contains(getCreateStatementSource, "db.backend.error.create_table_statement_not_found") { - t.Fatal("GetCreateStatement does not reference db.backend.error.create_table_statement_not_found") - } - - loadColumnTypeMapSource := oracleFunctionSource(t, source, "func (o *OracleDB) loadColumnTypeMap(tableName string) (map[string]string, error)") - if strings.Contains(loadColumnTypeMapSource, `fmt.Errorf("加载列元数据失败(表=%s):%w;请检查 ALL_TAB_COLUMNS 查询权限与表是否存在", tableName, err)`) { - t.Fatal("loadColumnTypeMap still contains raw Chinese Oracle column-metadata failure text") - } - if !strings.Contains(loadColumnTypeMapSource, "db.backend.error.oracle_column_metadata_load_failed") { - t.Fatal("loadColumnTypeMap does not reference db.backend.error.oracle_column_metadata_load_failed") - } -} func TestOracleMetadataCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/postgres_i18n_test.go b/internal/db/postgres_i18n_test.go index 72d69f36..aabc254d 100644 --- a/internal/db/postgres_i18n_test.go +++ b/internal/db/postgres_i18n_test.go @@ -1,10 +1,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -67,18 +65,3 @@ func TestPostgresMetadataErrorsUseCurrentLanguage(t *testing.T) { } } -func TestPostgresMetadataErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("postgres_impl.go") - if err != nil { - t.Fatalf("read postgres_impl.go: %v", err) - } - source := string(sourceBytes) - rawMessage := `fmt.Errorf("` + rawPostgresTableNameRequiredText + `")` - - if strings.Contains(source, rawMessage) { - t.Fatalf("postgres_impl.go still contains raw PostgreSQL metadata text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.table_name_required") { - t.Fatal("postgres_impl.go does not reference db.backend.error.table_name_required") - } -} diff --git a/internal/db/sqlite_i18n_test.go b/internal/db/sqlite_i18n_test.go index a4363c0a..a194385f 100644 --- a/internal/db/sqlite_i18n_test.go +++ b/internal/db/sqlite_i18n_test.go @@ -6,11 +6,9 @@ import ( "database/sql" "database/sql/driver" "io" - "os" "strings" "sync" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -203,55 +201,7 @@ func TestSQLiteDSNValidationErrorsUseCurrentLanguage(t *testing.T) { } } -func TestSQLiteMetadataErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("sqlite_impl.go") - if err != nil { - t.Fatalf("read sqlite_impl.go: %v", err) - } - source := string(sourceBytes) - for _, rawMessage := range []string{ - `fmt.Errorf("` + rawSQLiteCreateStatementNotFoundText + `")`, - `fmt.Errorf("` + rawSQLiteTableNameRequiredText + `")`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("sqlite_impl.go still contains raw SQLite metadata text %q", rawMessage) - } - } - for _, key := range []string{ - "db.backend.error.create_table_statement_not_found", - "db.backend.error.table_name_required", - } { - if !strings.Contains(source, key) { - t.Fatalf("sqlite_impl.go does not reference i18n key %q", key) - } - } -} - -func TestSQLiteDSNValidationErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("sqlite_impl.go") - if err != nil { - t.Fatalf("read sqlite_impl.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - rawSQLiteFilePathRequiredText, - rawSQLiteHostAddressHintText, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("sqlite_impl.go still contains raw SQLite DSN validation text %q", rawMessage) - } - } - for _, key := range []string{ - "db.backend.error.sqlite_file_path_required", - "db.backend.error.sqlite_host_port_not_file_path", - } { - if !strings.Contains(source, key) { - t.Fatalf("sqlite_impl.go does not reference i18n key %q", key) - } - } -} func TestSQLiteDSNValidationErrorCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/db/sqlserver_impl_test.go b/internal/db/sqlserver_impl_test.go index 15e2dba7..f36b7209 100644 --- a/internal/db/sqlserver_impl_test.go +++ b/internal/db/sqlserver_impl_test.go @@ -8,14 +8,11 @@ import ( "database/sql/driver" "errors" "io" - "os" "reflect" "strings" "sync" "testing" - "GoNavi-Wails/shared/i18n" - "github.com/golang-sql/sqlexp" _ "modernc.org/sqlite" ) @@ -345,18 +342,3 @@ func TestSQLServerMetadataErrorsUseCurrentLanguage(t *testing.T) { } } -func TestSQLServerMetadataErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("sqlserver_impl.go") - if err != nil { - t.Fatalf("read sqlserver_impl.go: %v", err) - } - source := string(sourceBytes) - rawMessage := `fmt.Errorf("` + rawSQLServerTableNameRequiredText + `")` - - if strings.Contains(source, rawMessage) { - t.Fatalf("sqlserver_impl.go still contains raw SQL Server metadata text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.table_name_required") { - t.Fatal("sqlserver_impl.go does not reference db.backend.error.table_name_required") - } -} diff --git a/internal/db/tdengine_applychanges_test.go b/internal/db/tdengine_applychanges_test.go index f4c50333..2084a7fc 100644 --- a/internal/db/tdengine_applychanges_test.go +++ b/internal/db/tdengine_applychanges_test.go @@ -8,7 +8,6 @@ import ( "database/sql/driver" "fmt" "io" - "os" "reflect" "strings" "sync" @@ -337,38 +336,6 @@ func TestTDengineApplyChangesErrorsUseCurrentLanguage(t *testing.T) { }) } -func TestTDengineApplyChangesErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("tdengine_impl.go") - if err != nil { - t.Fatalf("read tdengine_impl.go: %v", err) - } - source := string(sourceBytes) - start := strings.Index(source, "func (t *TDengineDB) ApplyChanges") - if start < 0 { - t.Fatal("TDengine ApplyChanges function not found") - } - end := strings.Index(source[start:], "func execTDengineInsertBatches") - if end < 0 { - t.Fatal("TDengine ApplyChanges function end marker not found") - } - applyChangesSource := source[start : start+end] - - for _, rawMessage := range []string{ - `fmt.Errorf("` + rawTDengineConnectionNotOpenText() + `")`, - `fmt.Errorf("` + rawTDengineTableNameRequiredText() + `")`, - `fmt.Errorf("` + rawTDengineApplyChangesInsertOnlyText() + `")`, - } { - if strings.Contains(applyChangesSource, rawMessage) { - t.Fatalf("TDengine ApplyChanges still contains raw text %q", rawMessage) - } - } - for _, key := range tdengineApplyChangesI18nKeys() { - if !strings.Contains(applyChangesSource, key) { - t.Fatalf("TDengine ApplyChanges does not reference i18n key %q", key) - } - } -} - func TestTDengineApplyChangesCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() if err != nil { @@ -567,19 +534,3 @@ func TestTDengineGetCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) { } } -func TestTDengineGetCreateStatementSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("tdengine_impl.go") - if err != nil { - t.Fatalf("read tdengine_impl.go: %v", err) - } - source := string(sourceBytes) - - rawNotFoundText := "\u672a\u627e\u5230\u5efa\u8868\u8bed\u53e5" - rawNotFoundSnippet := `fmt.Errorf("` + rawNotFoundText + `")` - if strings.Contains(source, rawNotFoundSnippet) { - t.Fatalf("TDengine GetCreateStatement still contains raw CREATE TABLE not found text") - } - if !strings.Contains(source, "db.backend.error.create_table_statement_not_found") { - t.Fatal("TDengine GetCreateStatement does not reference db.backend.error.create_table_statement_not_found") - } -} diff --git a/internal/db/tdengine_i18n_test.go b/internal/db/tdengine_i18n_test.go index bfffb3d6..79dfbd59 100644 --- a/internal/db/tdengine_i18n_test.go +++ b/internal/db/tdengine_i18n_test.go @@ -3,10 +3,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -32,18 +30,3 @@ func TestTDengineGetAllColumnsDatabaseRequiredUsesCurrentLanguage(t *testing.T) } } -func TestTDengineGetAllColumnsDatabaseRequiredSourceUsesI18nKey(t *testing.T) { - sourceBytes, err := os.ReadFile("tdengine_impl.go") - if err != nil { - t.Fatalf("read tdengine_impl.go: %v", err) - } - source := string(sourceBytes) - - rawMessage := `fmt.Errorf("` + rawTDengineAllColumnsDatabaseRequiredText + `")` - if strings.Contains(source, rawMessage) { - t.Fatalf("tdengine_impl.go still contains raw database-name text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.database_name_required") { - t.Fatal("tdengine_impl.go does not reference db.backend.error.database_name_required") - } -} diff --git a/internal/db/vastbase_i18n_test.go b/internal/db/vastbase_i18n_test.go index e09eb8f4..db1a0826 100644 --- a/internal/db/vastbase_i18n_test.go +++ b/internal/db/vastbase_i18n_test.go @@ -3,10 +3,8 @@ package db import ( - "os" "strings" "testing" - "GoNavi-Wails/shared/i18n" ) @@ -69,18 +67,3 @@ func TestVastbaseMetadataErrorsUseCurrentLanguage(t *testing.T) { } } -func TestVastbaseMetadataErrorSourcesUseI18nKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("vastbase_impl.go") - if err != nil { - t.Fatalf("read vastbase_impl.go: %v", err) - } - source := string(sourceBytes) - rawMessage := `fmt.Errorf("` + rawVastbaseTableNameRequiredText + `")` - - if strings.Contains(source, rawMessage) { - t.Fatalf("vastbase_impl.go still contains raw Vastbase metadata text %q", rawMessage) - } - if !strings.Contains(source, "db.backend.error.table_name_required") { - t.Fatal("vastbase_impl.go does not reference db.backend.error.table_name_required") - } -} diff --git a/internal/jvm/connection_error_message_test.go b/internal/jvm/connection_error_message_test.go index 30a277e3..e65494a2 100644 --- a/internal/jvm/connection_error_message_test.go +++ b/internal/jvm/connection_error_message_test.go @@ -2,173 +2,14 @@ package jvm import ( "errors" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) -func TestConnectionErrorMessageSourceUsesLocalizedTextForGenericWrapperAndEndpoint(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_error_message.go") - if err != nil { - t.Fatalf("read connection_error_message.go: %v", err) - } - source := string(sourceBytes) - for _, rawText := range []string{ - `return "JVM 连接失败"`, - `return "Endpoint 连接失败:未填写 Endpoint Base URL。"`, - `"Endpoint 连接失败:Endpoint Base URL 格式不合法。"`, - `"请填写完整的 ` + "`http://` 或 `https://` 地址,并指向实现 GoNavi JVM HTTP 合约的管理接口根路径,例如 `http://127.0.0.1:19090/manage/jvm`。" + ``, - `"Endpoint 连接失败:当前只支持 HTTP 或 HTTPS 协议。"`, - `"请把 Endpoint Base URL 改成 ` + "`http://` 或 `https://` 开头的地址。" + ``, - `"Endpoint 连接失败:目标地址已响应,但没有找到 GoNavi JVM 管理接口。"`, - `"请确认 Base URL 指向的是 JVM 管理接口根路径,而不是普通业务接口、健康检查地址或网关首页。"`, - `"Endpoint 连接失败:目标管理接口未监听,或当前地址不可达。"`, - `"请确认 Base URL 指向实现 GoNavi JVM HTTP 合约的管理接口,并检查服务监听、端口映射和防火墙。"`, - `"Endpoint 连接失败:目标管理接口已响应,但当前 API Key 无效或缺失。"`, - `"请检查连接中的 Endpoint API Key 是否与目标服务配置一致。"`, - `"Endpoint 连接失败:当前请求被目标管理接口拒绝。"`, - `"请确认当前客户端来源、鉴权配置和访问策略允许 GoNavi 访问该管理接口。"`, - `"Endpoint 连接失败:访问目标管理接口超时。"`, - `"请确认 Base URL 可达、目标服务已完成启动,并适当增加连接超时时间。"`, - `"建议:"+trimmed`, - `"技术细节:"+trimmed`, - } { - if strings.Contains(source, rawText) { - t.Fatalf("connection_error_message.go still contains raw localized endpoint text %q", rawText) - } - } - for _, key := range []string{ - "jvm.backend.connection_error.generic", - "jvm.backend.connection_error.suggestion", - "jvm.backend.connection_error.technical_detail", - "jvm.backend.connection_error.endpoint.base_url_required", - "jvm.backend.connection_error.endpoint.base_url_invalid.summary", - "jvm.backend.connection_error.endpoint.base_url_invalid.help", - "jvm.backend.connection_error.endpoint.scheme_unsupported.summary", - "jvm.backend.connection_error.endpoint.scheme_unsupported.help", - "jvm.backend.connection_error.endpoint.not_found.summary", - "jvm.backend.connection_error.endpoint.not_found.help", - "jvm.backend.connection_error.endpoint.connection_refused.summary", - "jvm.backend.connection_error.endpoint.connection_refused.help", - "jvm.backend.connection_error.endpoint.unauthorized.summary", - "jvm.backend.connection_error.endpoint.unauthorized.help", - "jvm.backend.connection_error.endpoint.forbidden.summary", - "jvm.backend.connection_error.endpoint.forbidden.help", - "jvm.backend.connection_error.endpoint.timeout.summary", - "jvm.backend.connection_error.endpoint.timeout.help", - } { - if !strings.Contains(source, key) { - t.Fatalf("connection_error_message.go does not reference i18n key %q", key) - } - } -} - -func TestConnectionErrorMessageSourceUsesLocalizedTextForAgent(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_error_message.go") - if err != nil { - t.Fatalf("read connection_error_message.go: %v", err) - } - source := string(sourceBytes) - - for _, rawText := range []string{ - `return "Agent 连接失败:未填写 Agent Base URL。"`, - `"Agent 连接失败:Agent Base URL 格式不合法。"`, - `"请填写完整的 ` + "`http://` 或 `https://` 地址,例如 `http://127.0.0.1:19090/gonavi/agent/jvm`。" + ``, - `"Agent 连接失败:当前只支持 HTTP 或 HTTPS 协议。"`, - `"请把 Agent Base URL 改成 ` + "`http://` 或 `https://` 开头的地址。" + ``, - `"Agent 连接失败:目标 Agent 管理端口未监听,或当前地址不可达。"`, - `"请确认 Java 服务已通过 ` + "`-javaagent`" + ` 启动 GoNavi Agent,并检查 Base URL、端口映射和防火墙。"`, - `"Agent 连接失败:Agent 已响应,但当前 API Key 无效或缺失。"`, - `"请检查连接中的 Agent API Key 是否与目标服务启动参数一致。"`, - `"Agent 连接失败:当前请求被 Agent 拒绝。"`, - `"请确认当前客户端来源、鉴权配置和 Agent 访问策略允许 GoNavi 访问。"`, - `"Agent 连接失败:访问 Agent 管理端口超时。"`, - `"请确认目标地址可达、Agent 已完成启动,并适当增加连接超时时间。"`, - } { - if strings.Contains(source, rawText) { - t.Fatalf("connection_error_message.go still contains raw localized agent text %q", rawText) - } - } - - for _, key := range []string{ - "jvm.backend.connection_error.agent.base_url_required", - "jvm.backend.connection_error.agent.base_url_invalid.summary", - "jvm.backend.connection_error.agent.base_url_invalid.help", - "jvm.backend.connection_error.agent.scheme_unsupported.summary", - "jvm.backend.connection_error.agent.scheme_unsupported.help", - "jvm.backend.connection_error.agent.connection_refused.summary", - "jvm.backend.connection_error.agent.connection_refused.help", - "jvm.backend.connection_error.agent.unauthorized.summary", - "jvm.backend.connection_error.agent.unauthorized.help", - "jvm.backend.connection_error.agent.forbidden.summary", - "jvm.backend.connection_error.agent.forbidden.help", - "jvm.backend.connection_error.agent.timeout.summary", - "jvm.backend.connection_error.agent.timeout.help", - } { - if !strings.Contains(source, key) { - t.Fatalf("connection_error_message.go does not reference i18n key %q", key) - } - } -} - -func TestConnectionErrorMessageSourceUsesLocalizedTextForJMX(t *testing.T) { - sourceBytes, err := os.ReadFile("connection_error_message.go") - if err != nil { - t.Fatalf("read connection_error_message.go: %v", err) - } - source := string(sourceBytes) - - for _, rawText := range []string{ - `return "JMX 连接失败:未填写主机地址。"`, - `return "JMX 连接失败:端口无效,请填写 1-65535 之间的有效端口。"`, - `"JMX 连接失败:当前机器未找到 ` + "`java`" + ` 运行时,GoNavi 无法启动 JMX helper。"`, - `"请先安装 JRE/JDK,或通过环境变量 ` + "`GONAVI_JMX_JAVA_BIN`" + ` 指向正确的 ` + "`java`" + ` 可执行文件。"`, - `fmt.Sprintf("JMX 连接失败:%s 不是标准 JMX 远程管理端口,当前更像普通业务端口或 HTTP 端口。", target)`, - `"请改填应用实际暴露的 JMX 端口,而不是业务 ` + "`server.port`" + `。如果服务只开启了 ` + "`-Dcom.sun.management.jmxremote`" + `,但没有配置 ` + "`jmxremote.port`" + `,也无法直接远程连接。"`, - `fmt.Sprintf("JMX 连接失败:%s 上虽然有 RMI 服务,但不是可用的 JMX RMIServer 端口。", target)`, - `"这通常意味着填到了 RMI 注册端口、调试端口或其他 Java 服务端口。请检查 ` + "`jmxremote.port`" + ` 和 ` + "`jmxremote.rmi.port`" + ` 配置是否正确。"`, - `fmt.Sprintf("JMX 连接失败:%s 上的服务主动断开了连接,当前端口不是兼容的标准 JMX RMI 端口。", target)`, - `"请确认填写的是 JVM 真正对外暴露的 JMX 端口,而不是业务端口、调试端口或被代理转发的端口。"`, - `fmt.Sprintf("JMX 连接失败:无法连接到 %s,对应端口没有监听或当前网络不可达。", target)`, - `"请确认目标 JVM 已开启远程 JMX,并检查主机、防火墙、端口映射和 SSH/代理配置。"`, - `fmt.Sprintf("JMX 连接失败:%s 需要认证,或当前凭据不可用。", target)`, - `"请确认目标 JMX 是否关闭认证;如果必须认证,需要补充用户名/密码后再连接。"`, - `fmt.Sprintf("JMX 连接失败:连接 %s 超时。", target)`, - `"请确认端口可达、网络未被拦截,并适当增加连接超时时间。"`, - } { - if strings.Contains(source, rawText) { - t.Fatalf("connection_error_message.go still contains raw localized JMX text %q", rawText) - } - } - - for _, key := range []string{ - "jvm.backend.connection_error.jmx.host_required", - "jvm.backend.connection_error.jmx.port_invalid", - "jvm.backend.connection_error.jmx.java_missing.summary", - "jvm.backend.connection_error.jmx.java_missing.help", - "jvm.backend.connection_error.jmx.non_jrmp.summary", - "jvm.backend.connection_error.jmx.non_jrmp.help", - "jvm.backend.connection_error.jmx.no_such_object.summary", - "jvm.backend.connection_error.jmx.no_such_object.help", - "jvm.backend.connection_error.jmx.connection_reset.summary", - "jvm.backend.connection_error.jmx.connection_reset.help", - "jvm.backend.connection_error.jmx.connection_refused.summary", - "jvm.backend.connection_error.jmx.connection_refused.help", - "jvm.backend.connection_error.jmx.auth.summary", - "jvm.backend.connection_error.jmx.auth.help", - "jvm.backend.connection_error.jmx.timeout.summary", - "jvm.backend.connection_error.jmx.timeout.help", - } { - if !strings.Contains(source, key) { - t.Fatalf("connection_error_message.go does not reference i18n key %q", key) - } - } -} func TestConnectionErrorMessageCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index a390350a..29f31b6a 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -2,10 +2,8 @@ package proxy import ( "context" - "os" "strings" "testing" - "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" ) @@ -46,51 +44,6 @@ func TestForwarderCacheKeyIncludesCredentialFingerprint(t *testing.T) { } } -func TestProxyUserVisibleErrorsUseLocalizedText(t *testing.T) { - sourceBytes, err := os.ReadFile("proxy.go") - if err != nil { - t.Fatalf("read proxy.go: %v", err) - } - source := string(sourceBytes) - - rawMessages := []string{ - "不支持的代理类型", - "代理主机为空", - "代理端口无效", - "无效的远端地址", - "创建本地代理监听失败", - "创建 SOCKS5 代理拨号器失败", - "SOCKS5 代理连接失败", - "连接 HTTP 代理失败", - "发送 HTTP CONNECT 请求失败", - "读取 HTTP CONNECT 响应失败", - "HTTP 代理 CONNECT 失败", - } - for _, raw := range rawMessages { - if strings.Contains(source, raw) { - t.Fatalf("proxy.go still contains raw user-visible message %q", raw) - } - } - - keys := []string{ - "proxy.backend.error.unsupported_type", - "proxy.backend.error.host_empty", - "proxy.backend.error.port_invalid", - "proxy.backend.error.remote_addr_invalid", - "proxy.backend.error.listen_failed", - "proxy.backend.error.socks5_dialer_failed", - "proxy.backend.error.socks5_connect_failed", - "proxy.backend.error.http_connect_failed", - "proxy.backend.error.http_connect_write_failed", - "proxy.backend.error.http_connect_read_failed", - "proxy.backend.error.http_connect_status_failed", - } - for _, key := range keys { - if !strings.Contains(source, key) { - t.Fatalf("proxy.go should reference localized key %q", key) - } - } -} func TestNormalizeConfigUsesCurrentLanguageForValidationErrors(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) diff --git a/internal/redis/redis_impl_test.go b/internal/redis/redis_impl_test.go index d655a257..f32441ee 100644 --- a/internal/redis/redis_impl_test.go +++ b/internal/redis/redis_impl_test.go @@ -11,12 +11,10 @@ import ( "math" "math/big" "net" - "os" "sort" "strconv" "strings" "testing" - goredis "github.com/redis/go-redis/v9" ) @@ -531,55 +529,6 @@ func TestRedisConnectFailureWrappersUseEnglishPrefixes(t *testing.T) { } } -func TestRedisConnectSourceUsesLocalizedValidationKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("redis_impl.go") - if err != nil { - t.Fatalf("read redis_impl.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("Redis 节点地址不能为空")`, - `fmt.Errorf("无效 Redis 节点地址: %s", addr)`, - `fmt.Errorf("无效 Redis 端口: %s", addr)`, - `fmt.Errorf("Redis 连接地址不能为空")`, - `return "集群"`, - `return "多节点"`, - `fmt.Errorf("Redis %s模式暂不支持 SSH 隧道,请关闭 SSH 后重试", redisTopologyDisplayName(topology))`, - `fmt.Errorf("Redis Sentinel 模式需要填写 master 名称")`, - `fmt.Sprintf("第%d次 TLS 配置失败: %v", idx+1, err)`, - `fmt.Sprintf("第%d次连接失败: %v", idx+1, pingErr)`, - `fmt.Errorf("Redis Sentinel 连接失败: %s", strings.Join(failures, ";"))`, - `fmt.Errorf("Redis 集群连接失败: %s", strings.Join(failures, ";"))`, - `fmt.Errorf("创建 SSH 隧道失败: %w", err)`, - `fmt.Errorf("Redis 连接失败: %s", strings.Join(failures, ";"))`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("redis_impl.go still contains raw Redis connect validation text %q", rawMessage) - } - } - - for _, key := range []string{ - "redis.backend.error.node_address_required", - "redis.backend.error.invalid_node_address", - "redis.backend.error.invalid_port", - "redis.backend.error.address_required", - "redis.backend.label.topology_cluster", - "redis.backend.label.topology_multi_node", - "redis.backend.error.topology_ssh_tunnel_unsupported", - "redis.backend.error.sentinel_master_required", - "redis.backend.error.connect_tls_setup_failed", - "redis.backend.error.connect_attempt_failed", - "redis.backend.error.sentinel_connect_failed", - "redis.backend.error.cluster_connect_failed", - "redis.backend.error.ssh_tunnel_create_failed", - "redis.backend.error.connect_failed", - } { - if !strings.Contains(source, key) { - t.Fatalf("redis_impl.go does not reference Redis i18n key %q", key) - } - } -} func TestRedisExecuteCommandClusterSelectValidationUsesEnglishMessages(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) @@ -638,33 +587,6 @@ func TestRedisExecuteCommandClusterSelectValidationUsesEnglishMessages(t *testin } } -func TestRedisExecuteCommandSourceUsesLocalizedClusterSelectValidationKeys(t *testing.T) { - sourceBytes, err := os.ReadFile("redis_impl.go") - if err != nil { - t.Fatalf("read redis_impl.go: %v", err) - } - source := string(sourceBytes) - - for _, rawMessage := range []string{ - `fmt.Errorf("SELECT 命令缺少数据库索引")`, - `fmt.Errorf("无效数据库索引: %s", args[1])`, - `fmt.Errorf("数据库索引必须在 0-%d 之间", redisClusterLogicalDBCount-1)`, - } { - if strings.Contains(source, rawMessage) { - t.Fatalf("redis_impl.go still contains raw Redis cluster SELECT validation text %q", rawMessage) - } - } - - for _, key := range []string{ - "redis.backend.error.select_db_index_required", - "redis.backend.error.select_db_index_invalid", - "redis.backend.error.select_db_index_out_of_range", - } { - if !strings.Contains(source, key) { - t.Fatalf("redis_impl.go does not reference Redis cluster SELECT i18n key %q", key) - } - } -} func TestRedisSelectDBClusterRangeUsesEnglishMessage(t *testing.T) { SetBackendLanguage(i18n.LanguageEnUS) diff --git a/internal/ssh/ssh_i18n_test.go b/internal/ssh/ssh_i18n_test.go index 16ee69d9..472d10f6 100644 --- a/internal/ssh/ssh_i18n_test.go +++ b/internal/ssh/ssh_i18n_test.go @@ -2,11 +2,9 @@ package ssh import ( "context" - "os" "strings" "testing" "unicode" - "GoNavi-Wails/internal/connection" ) @@ -67,22 +65,6 @@ func TestSSHConnectionFailureWrappersUseEnglish(t *testing.T) { } } -func TestSSHSourceDoesNotKeepLegacyChineseErrorWrappers(t *testing.T) { - source, err := os.ReadFile("ssh.go") - if err != nil { - t.Fatalf("read ssh.go: %v", err) - } - text := string(source) - for _, legacy := range []string{ - "fmt.Errorf(\"\u5efa\u7acb SSH \u8fde\u63a5\u5931\u8d25\uff1a%w\", err)", - "fmt.Errorf(\"\u901a\u8fc7 SSH \u96a7\u9053\u8fde\u63a5\u5230 %s \u5931\u8d25\uff1a%w\", address, err)", - "fmt.Errorf(\"\u521b\u5efa\u672c\u5730\u76d1\u542c\u5668\u5931\u8d25\uff1a%w\", err)", - } { - if strings.Contains(text, legacy) { - t.Fatalf("legacy Chinese SSH wrapper still exists: %s", legacy) - } - } -} func containsHan(text string) bool { for _, r := range text { diff --git a/internal/sync/analyze_i18n_test.go b/internal/sync/analyze_i18n_test.go index 94abdb45..f48aa49e 100644 --- a/internal/sync/analyze_i18n_test.go +++ b/internal/sync/analyze_i18n_test.go @@ -5,7 +5,6 @@ import ( "GoNavi-Wails/internal/db" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -43,65 +42,6 @@ func assertNoLegacyAnalyzeChinese(t *testing.T, text string) { } } -func TestAnalyzeUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("analyze.go") - if err != nil { - t.Fatalf("read analyze.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - `"差异分析开始"`, - `fmt.Sprintf("分析表(%d/%d)", i+1, totalTables)`, - `"差异分析完成"`, - `fmt.Sprintf("已完成 %d 张表的差异分析", len(result.Tables))`, - `"初始化源数据库驱动失败: " + err.Error()`, - `"初始化目标数据库驱动失败: " + err.Error()`, - `"源数据库连接失败: " + err.Error()`, - `"目标数据库连接失败: " + err.Error()`, - `"目标表不存在,无法执行同步"`, - `fmt.Sprintf("检测到 %d 条结构变更", summary.SchemaDiffCount)`, - `"仅同步结构,未执行数据差异分析"`, - `"读取源表失败: " + err.Error()`, - `"目标表不存在,执行时将自动建表并导入全部源数据"`, - `"当前模式无需差异对比,将按源表数据执行导入"`, - `"无主键,不支持差异对比同步;如需直接导入请使用仅插入或全量覆盖模式"`, - `fmt.Sprintf("复合主键(%s),暂不支持差异对比同步", strings.Join(pkCols, ","))`, - `"读取目标表失败: " + err.Error()`, - `"差异分析完成"`, - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("analyze.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.progress.stage.analysis_started", - "data_sync.progress.stage.analysis_completed", - "data_sync.progress.stage.analyzing_table", - "data_sync.backend.error.init_source_driver_failed", - "data_sync.backend.error.init_target_driver_failed", - "data_sync.backend.error.connect_source_failed", - "data_sync.backend.error.connect_target_failed", - "data_sync.backend.error.read_source_table_failed", - "data_sync.backend.error.read_target_table_failed", - "data_sync.backend.error.diff_pk_required", - "data_sync.backend.error.diff_composite_pk_unsupported", - "data_sync.backend.result.analyzed_tables", - "data_sync.backend.summary.diff_completed", - "data_sync.plan.target_missing_cannot_sync", - "data_sync.plan.schema_changes_detected", - "data_sync.plan.schema_only_no_data_diff", - "data_sync.plan.target_missing_auto_create_all", - "data_sync.plan.data_import_without_diff", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("analyze.go should reference localized key %q", key) - } - } -} func TestAnalyzeCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/migration_clickhouse_i18n_test.go b/internal/sync/migration_clickhouse_i18n_test.go index cb7b86f2..4cbe618f 100644 --- a/internal/sync/migration_clickhouse_i18n_test.go +++ b/internal/sync/migration_clickhouse_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -59,35 +58,6 @@ func assertNoLegacyClickHouseMigrationChinese(t *testing.T, text string) { } } -func TestClickHouseMigrationUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("migration_clickhouse.go") - if err != nil { - t.Fatalf("read migration_clickhouse.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u83b7\u53d6\u6e90\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u8868\u4e0d\u5b58\u5728\u6216\u65e0\u5217\u5b9a\u4e49: %s\", tableName)", - "fmt.Errorf(\"\u83b7\u53d6\u76ee\u6807\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("migration_clickhouse.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.source_table_columns_failed", - "data_sync.backend.error.source_table_missing_or_no_columns", - "data_sync.backend.error.target_table_columns_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("migration_clickhouse.go should reference localized key %q", key) - } - } -} func TestClickHouseMigrationCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/migration_mongodb_i18n_test.go b/internal/sync/migration_mongodb_i18n_test.go index 20936b05..af6adf06 100644 --- a/internal/sync/migration_mongodb_i18n_test.go +++ b/internal/sync/migration_mongodb_i18n_test.go @@ -5,7 +5,6 @@ import ( "GoNavi-Wails/internal/db" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -63,41 +62,6 @@ func assertNoLegacyMongoMigrationChinese(t *testing.T, text string) { } } -func TestMongoMigrationUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("migration_mongodb.go") - if err != nil { - t.Fatalf("read migration_mongodb.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u83b7\u53d6\u6e90\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u8868\u4e0d\u5b58\u5728\u6216\u65e0\u5217\u5b9a\u4e49: %s\", tableName)", - "fmt.Errorf(\"\u68c0\u67e5\u76ee\u6807\u96c6\u5408\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u96c6\u5408\u672a\u63a8\u65ad\u51fa\u53ef\u8fc1\u79fb\u5b57\u6bb5: %s\", tableName)", - "fmt.Errorf(\"\u83b7\u53d6\u76ee\u6807\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u8bfb\u53d6\u6e90\u96c6\u5408\u6837\u672c\u5931\u8d25: %w\", err)", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("migration_mongodb.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.source_table_columns_failed", - "data_sync.backend.error.source_table_missing_or_no_columns", - "data_sync.backend.error.target_collection_check_failed", - "data_sync.backend.error.source_collection_no_migratable_fields", - "data_sync.backend.error.target_table_columns_failed", - "data_sync.backend.error.mongo_read_source_samples_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("migration_mongodb.go should reference localized key %q", key) - } - } -} func TestMongoMigrationCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/migration_tdengine_i18n_test.go b/internal/sync/migration_tdengine_i18n_test.go index ddb6a172..08bac1bd 100644 --- a/internal/sync/migration_tdengine_i18n_test.go +++ b/internal/sync/migration_tdengine_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -38,35 +37,6 @@ func assertNoLegacyTDengineMigrationChinese(t *testing.T, text string) { } } -func TestTDengineMigrationUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("migration_tdengine.go") - if err != nil { - t.Fatalf("read migration_tdengine.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u83b7\u53d6\u6e90\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u8868\u4e0d\u5b58\u5728\u6216\u65e0\u5217\u5b9a\u4e49: %s\", tableName)", - "fmt.Errorf(\"\u83b7\u53d6\u76ee\u6807\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("migration_tdengine.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.source_table_columns_failed", - "data_sync.backend.error.source_table_missing_or_no_columns", - "data_sync.backend.error.target_table_columns_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("migration_tdengine.go should reference localized key %q", key) - } - } -} func TestTDengineMigrationCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/migration_tdengine_target_i18n_test.go b/internal/sync/migration_tdengine_target_i18n_test.go index 2f7b7543..10606dca 100644 --- a/internal/sync/migration_tdengine_target_i18n_test.go +++ b/internal/sync/migration_tdengine_target_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -52,35 +51,6 @@ func assertNoLegacyTDengineTargetMigrationChinese(t *testing.T, text string) { } } -func TestTDengineTargetMigrationUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("migration_tdengine_target.go") - if err != nil { - t.Fatalf("read migration_tdengine_target.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u83b7\u53d6\u6e90\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u8868\u4e0d\u5b58\u5728\u6216\u65e0\u5217\u5b9a\u4e49: %s\", tableName)", - "fmt.Errorf(\"\u83b7\u53d6\u76ee\u6807\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("migration_tdengine_target.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.source_table_columns_failed", - "data_sync.backend.error.source_table_missing_or_no_columns", - "data_sync.backend.error.target_table_columns_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("migration_tdengine_target.go should reference localized key %q", key) - } - } -} func TestTDengineTargetMigrationCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/preview_i18n_test.go b/internal/sync/preview_i18n_test.go index aa372a6f..d8266cfb 100644 --- a/internal/sync/preview_i18n_test.go +++ b/internal/sync/preview_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -35,43 +34,6 @@ func assertNoLegacyPreviewChinese(t *testing.T, text string) { } } -func TestPreviewUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("preview.go") - if err != nil { - t.Fatalf("read preview.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u521d\u59cb\u5316\u6e90\u6570\u636e\u5e93\u9a71\u52a8\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u521d\u59cb\u5316\u76ee\u6807\u6570\u636e\u5e93\u9a71\u52a8\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u6570\u636e\u5e93\u8fde\u63a5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u76ee\u6807\u6570\u636e\u5e93\u8fde\u63a5\u5931\u8d25: %w\", err)", - "errors.New(firstNonEmpty(plan.PlannedAction, \"\u76ee\u6807\u8868\u4e0d\u5b58\u5728\uff0c\u65e0\u6cd5\u9884\u89c8\u5dee\u5f02\"))", - "fmt.Errorf(\"\u65e0\u4e3b\u952e\uff0c\u4e0d\u652f\u6301\u6570\u636e\u9884\u89c8\")", - "fmt.Errorf(\"\u590d\u5408\u4e3b\u952e\uff08%s\uff09\uff0c\u6682\u4e0d\u652f\u6301\u6570\u636e\u9884\u89c8\", strings.Join(pkCols, \",\"))", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("preview.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.init_source_driver_failed", - "data_sync.backend.error.init_target_driver_failed", - "data_sync.backend.error.connect_source_failed", - "data_sync.backend.error.connect_target_failed", - "data_sync.plan.target_missing_preview_unavailable", - "data_sync.backend.error.preview_pk_required", - "data_sync.backend.error.preview_composite_pk_unsupported", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("preview.go should reference localized key %q", key) - } - } -} func TestPreviewCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/schema_migration_i18n_test.go b/internal/sync/schema_migration_i18n_test.go index 1a664912..9293c396 100644 --- a/internal/sync/schema_migration_i18n_test.go +++ b/internal/sync/schema_migration_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -59,35 +58,6 @@ func assertNoLegacySchemaMigrationChinese(t *testing.T, text string) { } } -func TestSchemaMigrationUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("schema_migration.go") - if err != nil { - t.Fatalf("read schema_migration.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - "fmt.Errorf(\"\u83b7\u53d6\u6e90\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - "fmt.Errorf(\"\u6e90\u8868\u4e0d\u5b58\u5728\u6216\u65e0\u5217\u5b9a\u4e49: %s\", tableName)", - "fmt.Errorf(\"\u83b7\u53d6\u76ee\u6807\u8868\u5b57\u6bb5\u5931\u8d25: %w\", err)", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("schema_migration.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.error.source_table_columns_failed", - "data_sync.backend.error.source_table_missing_or_no_columns", - "data_sync.backend.error.target_table_columns_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("schema_migration.go should reference localized key %q", key) - } - } -} func TestSchemaMigrationCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/source_query_sync_test.go b/internal/sync/source_query_sync_test.go index 0c010362..bf1f803b 100644 --- a/internal/sync/source_query_sync_test.go +++ b/internal/sync/source_query_sync_test.go @@ -6,7 +6,6 @@ import ( "GoNavi-Wails/shared/i18n" "errors" "fmt" - "os" "reflect" "strings" "testing" @@ -136,71 +135,6 @@ func assertNoLegacySourceQueryChinese(t *testing.T, text string) { } } -func TestSourceQuerySyncUsesLocalizedBackendTextSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("source_query_sync.go") - if err != nil { - t.Fatalf("read source_query_sync.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - `fmt.Errorf("源查询 SQL 不能为空")`, - `fmt.Errorf("SQL 结果集同步当前仅支持“仅同步数据”")`, - `fmt.Errorf("SQL 结果集同步要求且仅允许选择一个目标表")`, - `fmt.Errorf("目标表不能为空")`, - `fmt.Errorf("目标表无主键,不支持基于 SQL 结果集的差异分析")`, - `fmt.Errorf("目标表为复合主键(%s),暂不支持基于 SQL 结果集的差异分析", strings.Join(pkCols, ","))`, - `fmt.Errorf("获取目标表字段失败: %w", err)`, - `fmt.Errorf("目标表 %s 不存在或未读取到字段定义", tableName)`, - `fmt.Errorf("执行源查询失败: %w", err)`, - `fmt.Errorf("读取目标表失败: %w", err)`, - "\u521d\u59cb\u5316\u6e90\u6570\u636e\u5e93\u9a71\u52a8\u5931\u8d25: ", - "\u521d\u59cb\u5316\u76ee\u6807\u6570\u636e\u5e93\u9a71\u52a8\u5931\u8d25: ", - "\u6e90\u6570\u636e\u5e93\u8fde\u63a5\u5931\u8d25: ", - "\u76ee\u6807\u6570\u636e\u5e93\u8fde\u63a5\u5931\u8d25: ", - "已完成 1 个目标表的差异分析", - "SQL 结果集差异分析完成", - "SQL 结果集同步预览", - "差异分析开始", - "差异分析完成", - "开始同步", - "同步来源:SQL 结果集 -> 目标表", - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("source_query_sync.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.backend.validation.source_query_required", - "data_sync.backend.validation.query_mode_data_only", - "data_sync.backend.validation.single_target_table_required", - "data_sync.backend.validation.target_table_required", - "data_sync.backend.error.target_pk_required_for_query_diff", - "data_sync.backend.error.target_composite_pk_query_diff_unsupported", - "data_sync.backend.error.load_target_columns_failed", - "data_sync.backend.error.target_table_columns_missing", - "data_sync.backend.error.execute_source_query_failed", - "data_sync.backend.error.read_target_table_failed", - "data_sync.backend.error.init_source_driver_failed", - "data_sync.backend.error.init_target_driver_failed", - "data_sync.backend.error.connect_source_failed", - "data_sync.backend.error.connect_target_failed", - "data_sync.backend.result.analyzed_target_tables", - "data_sync.backend.summary.source_query_diff_completed", - "data_sync.plan.source_query_preview", - "data_sync.progress.stage.analysis_started", - "data_sync.progress.stage.analysis_completed", - "data_sync.progress.stage.sync_started", - "data_sync.backend.log.source_query_sync_source", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("source_query_sync.go should reference localized key %q", key) - } - } -} func TestSourceQuerySyncCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs() diff --git a/internal/sync/sync_engine_i18n_test.go b/internal/sync/sync_engine_i18n_test.go index 11d13716..8619cd1f 100644 --- a/internal/sync/sync_engine_i18n_test.go +++ b/internal/sync/sync_engine_i18n_test.go @@ -4,7 +4,6 @@ import ( "GoNavi-Wails/internal/connection" "GoNavi-Wails/shared/i18n" "errors" - "os" "strings" "testing" ) @@ -20,51 +19,6 @@ func baseRunSyncI18nConfig() SyncConfig { } } -func TestSyncEngineUsesLocalizedProgressAndFailureSourceGuard(t *testing.T) { - sourceBytes, err := os.ReadFile("sync_engine.go") - if err != nil { - t.Fatalf("read sync_engine.go: %v", err) - } - source := string(sourceBytes) - - legacyMessages := []string{ - `"开始同步"`, - `"连接源数据库"`, - `"连接目标数据库"`, - `fmt.Sprintf("同步表(%d/%d)", i+1, totalTables)`, - `"表处理完成"`, - `"同步完成"`, - `"同步失败"`, - `"初始化源数据库驱动失败: "+err.Error()`, - `"初始化目标数据库驱动失败: "+err.Error()`, - `"源数据库连接失败: "+err.Error()`, - `"目标数据库连接失败: "+err.Error()`, - } - for _, legacy := range legacyMessages { - if strings.Contains(source, legacy) { - t.Fatalf("sync_engine.go still contains legacy raw user-visible message %q", legacy) - } - } - - requiredKeys := []string{ - "data_sync.progress.stage.sync_started", - "data_sync.progress.stage.connecting_source", - "data_sync.progress.stage.connecting_target", - "data_sync.progress.stage.syncing_table", - "data_sync.progress.stage.table_completed", - "data_sync.progress.stage.completed", - "data_sync.progress.stage.failed", - "data_sync.backend.error.init_source_driver_failed", - "data_sync.backend.error.init_target_driver_failed", - "data_sync.backend.error.connect_source_failed", - "data_sync.backend.error.connect_target_failed", - } - for _, key := range requiredKeys { - if !strings.Contains(source, key) { - t.Fatalf("sync_engine.go should reference localized key %q", key) - } - } -} func TestSyncEngineProgressAndFailureCatalogKeysExist(t *testing.T) { catalogs, err := i18n.LoadCatalogs()