mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-25 16:04:02 +08:00
- 提交 internal/db 多驱动用户可见错误与状态文案多语言化 - 补齐数据库驱动多语言测试与六语言 catalog - 修复 frontend i18n catalog 的 4 个失效 guard
275 lines
7.4 KiB
Go
275 lines
7.4 KiB
Go
//go:build gonavi_full_drivers || gonavi_sqlite_driver
|
|
|
|
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"GoNavi-Wails/internal/connection"
|
|
"GoNavi-Wails/shared/i18n"
|
|
)
|
|
|
|
type sqliteI18nEmptyRowsDriver struct{}
|
|
|
|
type sqliteI18nEmptyRowsConn struct{}
|
|
|
|
type sqliteI18nEmptyRowsStmt struct{}
|
|
|
|
type sqliteI18nEmptyRowsRows struct{}
|
|
|
|
var registerSQLiteI18nEmptyRowsDriverOnce sync.Once
|
|
|
|
var (
|
|
rawSQLiteCreateStatementNotFoundText = string([]rune{0x672a, 0x627e, 0x5230, 0x5efa, 0x8868, 0x8bed, 0x53e5})
|
|
rawSQLiteTableNameRequiredText = string([]rune{0x8868, 0x540d, 0x4e0d, 0x80fd, 0x4e3a, 0x7a7a})
|
|
rawSQLiteFilePathRequiredText = "SQLite " + string([]rune{0x9700, 0x8981, 0x672c, 0x5730, 0x6570, 0x636e, 0x5e93, 0x6587, 0x4ef6, 0x8def, 0x5f84})
|
|
rawSQLiteHostAddressHintText = string([]rune{0x5f53, 0x524d, 0x8f93, 0x5165, 0x770b, 0x8d77, 0x6765, 0x662f, 0x4e3b, 0x673a, 0x5730, 0x5740})
|
|
)
|
|
|
|
func (sqliteI18nEmptyRowsDriver) Open(name string) (driver.Conn, error) {
|
|
return sqliteI18nEmptyRowsConn{}, nil
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsConn) Prepare(query string) (driver.Stmt, error) {
|
|
return sqliteI18nEmptyRowsStmt{}, nil
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsConn) Close() error { return nil }
|
|
|
|
func (sqliteI18nEmptyRowsConn) Begin() (driver.Tx, error) {
|
|
return nil, localizedDatabaseRuntimeError("db.backend.error.transaction_not_open", nil)
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsStmt) Close() error { return nil }
|
|
|
|
func (sqliteI18nEmptyRowsStmt) NumInput() int { return -1 }
|
|
|
|
func (sqliteI18nEmptyRowsStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|
return driver.RowsAffected(0), nil
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|
return sqliteI18nEmptyRowsRows{}, nil
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsRows) Columns() []string {
|
|
return []string{"sql"}
|
|
}
|
|
|
|
func (sqliteI18nEmptyRowsRows) Close() error { return nil }
|
|
|
|
func (sqliteI18nEmptyRowsRows) Next(dest []driver.Value) error {
|
|
return io.EOF
|
|
}
|
|
|
|
func openSQLiteI18nEmptyRowsDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
|
|
registerSQLiteI18nEmptyRowsDriverOnce.Do(func() {
|
|
sql.Register("sqlite_i18n_empty_rows", sqliteI18nEmptyRowsDriver{})
|
|
})
|
|
|
|
conn, err := sql.Open("sqlite_i18n_empty_rows", "")
|
|
if err != nil {
|
|
t.Fatalf("open sqlite_i18n_empty_rows test DB failed: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = conn.Close()
|
|
})
|
|
return conn
|
|
}
|
|
|
|
func TestSQLiteMetadataErrorsUseCurrentLanguage(t *testing.T) {
|
|
SetBackendLanguage(i18n.LanguageEnUS)
|
|
t.Cleanup(func() {
|
|
SetBackendLanguage(i18n.LanguageZhCN)
|
|
})
|
|
|
|
sqlite := &SQLiteDB{}
|
|
tests := []struct {
|
|
name string
|
|
call func() error
|
|
want string
|
|
unexpected string
|
|
}{
|
|
{
|
|
name: "create statement not found",
|
|
call: func() error {
|
|
_, err := (&SQLiteDB{conn: openSQLiteI18nEmptyRowsDB(t)}).GetCreateStatement("main", "orders")
|
|
return err
|
|
},
|
|
want: "The CREATE TABLE statement was not found",
|
|
unexpected: rawSQLiteCreateStatementNotFoundText,
|
|
},
|
|
{
|
|
name: "columns table name required",
|
|
call: func() error {
|
|
_, err := sqlite.GetColumns("", " ")
|
|
return err
|
|
},
|
|
want: "Table name is required",
|
|
unexpected: rawSQLiteTableNameRequiredText,
|
|
},
|
|
{
|
|
name: "indexes table name required",
|
|
call: func() error {
|
|
_, err := sqlite.GetIndexes("", " ")
|
|
return err
|
|
},
|
|
want: "Table name is required",
|
|
unexpected: rawSQLiteTableNameRequiredText,
|
|
},
|
|
{
|
|
name: "foreign keys table name required",
|
|
call: func() error {
|
|
_, err := sqlite.GetForeignKeys("", " ")
|
|
return err
|
|
},
|
|
want: "Table name is required",
|
|
unexpected: rawSQLiteTableNameRequiredText,
|
|
},
|
|
{
|
|
name: "triggers table name required",
|
|
call: func() error {
|
|
_, err := sqlite.GetTriggers("", " ")
|
|
return err
|
|
},
|
|
want: "Table name is required",
|
|
unexpected: rawSQLiteTableNameRequiredText,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := tc.call()
|
|
if err == nil {
|
|
t.Fatal("expected SQLite metadata call to fail")
|
|
}
|
|
if err.Error() != tc.want {
|
|
t.Fatalf("expected %q, got %q", tc.want, err.Error())
|
|
}
|
|
if strings.Contains(err.Error(), tc.unexpected) {
|
|
t.Fatalf("expected no raw Chinese SQLite metadata text, got %q", err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSQLiteDSNValidationErrorsUseCurrentLanguage(t *testing.T) {
|
|
SetBackendLanguage(i18n.LanguageEnUS)
|
|
t.Cleanup(func() {
|
|
SetBackendLanguage(i18n.LanguageZhCN)
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
config connection.ConnectionConfig
|
|
want string
|
|
unexpected string
|
|
}{
|
|
{
|
|
name: "empty path",
|
|
config: connection.ConnectionConfig{Type: "sqlite"},
|
|
want: "SQLite requires a local database file path (for example /path/to/demo.sqlite)",
|
|
unexpected: rawSQLiteFilePathRequiredText,
|
|
},
|
|
{
|
|
name: "host port",
|
|
config: connection.ConnectionConfig{Type: "sqlite", Host: "localhost:3306"},
|
|
want: "SQLite requires a local database file path; the current input looks like a host address: localhost:3306",
|
|
unexpected: rawSQLiteHostAddressHintText,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := (&SQLiteDB{}).Connect(tc.config)
|
|
if err == nil {
|
|
t.Fatal("expected SQLite DSN validation error")
|
|
}
|
|
if err.Error() != tc.want {
|
|
t.Fatalf("expected %q, got %q", tc.want, err.Error())
|
|
}
|
|
if strings.Contains(err.Error(), tc.unexpected) {
|
|
t.Fatalf("expected no raw Chinese SQLite DSN validation text, got %q", err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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()
|
|
if err != nil {
|
|
t.Fatalf("LoadCatalogs() error = %v", err)
|
|
}
|
|
|
|
keys := []string{
|
|
"db.backend.error.sqlite_file_path_required",
|
|
"db.backend.error.sqlite_host_port_not_file_path",
|
|
}
|
|
for _, language := range i18n.SupportedLanguages() {
|
|
catalog := catalogs[language]
|
|
for _, key := range keys {
|
|
if strings.TrimSpace(catalog[key]) == "" {
|
|
t.Fatalf("%s catalog missing SQLite DSN validation key %q", language, key)
|
|
}
|
|
}
|
|
}
|
|
}
|