Files
MyGoNavi/internal/db/oracle_i18n_test.go
tianqijiuyun-latiao d13c153f5e feat(i18n): 收口数据库驱动多语言代码
- 提交 internal/db 多驱动用户可见错误与状态文案多语言化

- 补齐数据库驱动多语言测试与六语言 catalog

- 修复 frontend i18n catalog 的 4 个失效 guard
2026-06-22 10:09:45 +08:00

235 lines
7.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package db
import (
"database/sql"
"database/sql/driver"
"errors"
"io"
"os"
"strings"
"sync"
"testing"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/shared/i18n"
)
type oracleI18nQueryErrorDriver struct{}
type oracleI18nQueryErrorConn struct{}
type oracleI18nEmptyRowsDriver struct{}
type oracleI18nEmptyRowsConn struct{}
type oracleI18nEmptyRowsRows struct{}
var registerOracleI18nQueryErrorDriverOnce sync.Once
var registerOracleI18nEmptyRowsDriverOnce sync.Once
var rawOracleCreateStatementNotFoundText = string([]rune{0x672a, 0x627e, 0x5230, 0x5efa, 0x8868, 0x8bed, 0x53e5})
func (oracleI18nQueryErrorDriver) Open(name string) (driver.Conn, error) {
return oracleI18nQueryErrorConn{}, nil
}
func (oracleI18nQueryErrorConn) Prepare(query string) (driver.Stmt, error) {
return nil, errors.New("prepare is not supported in oracle i18n query error test driver")
}
func (oracleI18nQueryErrorConn) Close() error { return nil }
func (oracleI18nQueryErrorConn) Begin() (driver.Tx, error) {
return nil, errors.New("transactions are not supported in oracle i18n query error test driver")
}
func (oracleI18nQueryErrorConn) Query(query string, args []driver.Value) (driver.Rows, error) {
return nil, errors.New("oracle metadata probe failed")
}
func (oracleI18nEmptyRowsDriver) Open(name string) (driver.Conn, error) {
return oracleI18nEmptyRowsConn{}, nil
}
func (oracleI18nEmptyRowsConn) Prepare(query string) (driver.Stmt, error) {
return nil, errors.New("prepare is not supported in oracle i18n empty rows test driver")
}
func (oracleI18nEmptyRowsConn) Close() error { return nil }
func (oracleI18nEmptyRowsConn) Begin() (driver.Tx, error) {
return nil, errors.New("transactions are not supported in oracle i18n empty rows test driver")
}
func (oracleI18nEmptyRowsConn) Query(query string, args []driver.Value) (driver.Rows, error) {
return oracleI18nEmptyRowsRows{}, nil
}
func (oracleI18nEmptyRowsRows) Columns() []string {
return []string{"DDL"}
}
func (oracleI18nEmptyRowsRows) Close() error { return nil }
func (oracleI18nEmptyRowsRows) Next(dest []driver.Value) error {
return io.EOF
}
func openOracleI18nQueryErrorDB(t *testing.T) *sql.DB {
t.Helper()
registerOracleI18nQueryErrorDriverOnce.Do(func() {
sql.Register("oracle_i18n_query_error", oracleI18nQueryErrorDriver{})
})
conn, err := sql.Open("oracle_i18n_query_error", "")
if err != nil {
t.Fatalf("open oracle_i18n_query_error test DB failed: %v", err)
}
t.Cleanup(func() {
_ = conn.Close()
})
return conn
}
func openOracleI18nEmptyRowsDB(t *testing.T) *sql.DB {
t.Helper()
registerOracleI18nEmptyRowsDriverOnce.Do(func() {
sql.Register("oracle_i18n_empty_rows", oracleI18nEmptyRowsDriver{})
})
conn, err := sql.Open("oracle_i18n_empty_rows", "")
if err != nil {
t.Fatalf("open oracle_i18n_empty_rows test DB failed: %v", err)
}
t.Cleanup(func() {
_ = conn.Close()
})
return conn
}
func oracleFunctionSource(t *testing.T, source string, signature string) string {
t.Helper()
start := strings.Index(source, signature)
if start < 0 {
t.Fatalf("oracle_impl.go missing function signature %q", signature)
}
rest := source[start+len(signature):]
end := strings.Index(rest, "\nfunc ")
if end < 0 {
return source[start:]
}
return source[start : start+len(signature)+end]
}
func TestOracleCreateStatementNotFoundUsesCurrentLanguage(t *testing.T) {
SetBackendLanguage(i18n.LanguageEnUS)
t.Cleanup(func() {
SetBackendLanguage(i18n.LanguageZhCN)
})
oracleDB := &OracleDB{conn: openOracleI18nEmptyRowsDB(t)}
_, err := oracleDB.GetCreateStatement("APP", "ORDERS")
if err == nil {
t.Fatal("expected Oracle GetCreateStatement to fail")
}
if err.Error() != "The CREATE TABLE statement was not found" {
t.Fatalf("expected English create-statement error, got %q", err.Error())
}
if strings.Contains(err.Error(), rawOracleCreateStatementNotFoundText) {
t.Fatalf("expected no raw Chinese create-statement text, got %q", err.Error())
}
}
func TestOracleMetadataErrorsUseCurrentLanguage(t *testing.T) {
SetBackendLanguage(i18n.LanguageEnUS)
t.Cleanup(func() {
SetBackendLanguage(i18n.LanguageZhCN)
})
t.Run("indexes table name required", func(t *testing.T) {
_, err := (&OracleDB{}).GetIndexes("", " ")
if err == nil {
t.Fatal("expected Oracle GetIndexes to reject an empty table name")
}
if err.Error() != "Table name is required" {
t.Fatalf("expected English table-name-required error, got %q", err.Error())
}
})
t.Run("apply changes wraps oracle column metadata load failure", func(t *testing.T) {
oracleDB := &OracleDB{conn: openOracleI18nQueryErrorDB(t)}
err := oracleDB.ApplyChanges("APP.USERS", connection.ChangeSet{})
if err == nil {
t.Fatal("expected Oracle ApplyChanges to surface column metadata load failure")
}
want := "Failed to load column metadata (table=APP.USERS): oracle metadata probe failed; check ALL_TAB_COLUMNS query permission and whether the table exists"
if err.Error() != want {
t.Fatalf("expected English Oracle column-metadata error %q, got %q", want, err.Error())
}
if strings.Contains(err.Error(), "加载列元数据失败") {
t.Fatalf("expected no legacy Chinese Oracle column-metadata prefix in en-US mode, got %q", err.Error())
}
})
}
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()
if err != nil {
t.Fatalf("LoadCatalogs() error = %v", err)
}
keys := []string{
"db.backend.error.create_table_statement_not_found",
"db.backend.error.table_name_required",
"db.backend.error.oracle_column_metadata_load_failed",
}
for _, language := range i18n.SupportedLanguages() {
catalog := catalogs[language]
for _, key := range keys {
if strings.TrimSpace(catalog[key]) == "" {
t.Fatalf("%s catalog missing Oracle metadata key %q", language, key)
}
}
}
}