package db import ( . "code.hoteas.com/golang/hotime/common" "fmt" "strings" "testing" ) // TestDialectQuoteIdentifier 测试方言的 QuoteIdentifier 方法 func TestDialectQuoteIdentifier(t *testing.T) { tests := []struct { name string dialect Dialect input string expected string }{ // MySQL 方言测试 {"MySQL simple", &MySQLDialect{}, "name", "`name`"}, {"MySQL with backticks", &MySQLDialect{}, "`name`", "`name`"}, {"MySQL with quotes", &MySQLDialect{}, "\"name\"", "`name`"}, // PostgreSQL 方言测试 {"PostgreSQL simple", &PostgreSQLDialect{}, "name", "\"name\""}, {"PostgreSQL with backticks", &PostgreSQLDialect{}, "`name`", "\"name\""}, {"PostgreSQL with quotes", &PostgreSQLDialect{}, "\"name\"", "\"name\""}, // SQLite 方言测试 {"SQLite simple", &SQLiteDialect{}, "name", "\"name\""}, {"SQLite with backticks", &SQLiteDialect{}, "`name`", "\"name\""}, {"SQLite with quotes", &SQLiteDialect{}, "\"name\"", "\"name\""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.dialect.QuoteIdentifier(tt.input) if result != tt.expected { t.Errorf("QuoteIdentifier(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } // TestDialectQuoteChar 测试方言的 QuoteChar 方法 func TestDialectQuoteChar(t *testing.T) { tests := []struct { name string dialect Dialect expected string }{ {"MySQL", &MySQLDialect{}, "`"}, {"PostgreSQL", &PostgreSQLDialect{}, "\""}, {"SQLite", &SQLiteDialect{}, "\""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.dialect.QuoteChar() if result != tt.expected { t.Errorf("QuoteChar() = %q, want %q", result, tt.expected) } }) } } // TestIdentifierProcessorTableName 测试表名处理 func TestIdentifierProcessorTableName(t *testing.T) { tests := []struct { name string dialect Dialect prefix string input string expected string }{ // MySQL 无前缀 {"MySQL no prefix", &MySQLDialect{}, "", "order", "`order`"}, {"MySQL no prefix with backticks", &MySQLDialect{}, "", "`order`", "`order`"}, // MySQL 有前缀 {"MySQL with prefix", &MySQLDialect{}, "app_", "order", "`app_order`"}, {"MySQL with prefix and backticks", &MySQLDialect{}, "app_", "`order`", "`app_order`"}, // PostgreSQL 无前缀 {"PostgreSQL no prefix", &PostgreSQLDialect{}, "", "order", "\"order\""}, // PostgreSQL 有前缀 {"PostgreSQL with prefix", &PostgreSQLDialect{}, "app_", "order", "\"app_order\""}, {"PostgreSQL with prefix and quotes", &PostgreSQLDialect{}, "app_", "\"order\"", "\"app_order\""}, // SQLite 有前缀 {"SQLite with prefix", &SQLiteDialect{}, "app_", "user", "\"app_user\""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { processor := NewIdentifierProcessor(tt.dialect, tt.prefix) result := processor.ProcessTableName(tt.input) if result != tt.expected { t.Errorf("ProcessTableName(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } // TestIdentifierProcessorColumn 测试列名处理(包括 table.column 格式) func TestIdentifierProcessorColumn(t *testing.T) { tests := []struct { name string dialect Dialect prefix string input string expected string }{ // 单独列名 {"MySQL simple column", &MySQLDialect{}, "", "name", "`name`"}, {"MySQL simple column with prefix", &MySQLDialect{}, "app_", "name", "`name`"}, // table.column 格式 {"MySQL table.column no prefix", &MySQLDialect{}, "", "order.name", "`order`.`name`"}, {"MySQL table.column with prefix", &MySQLDialect{}, "app_", "order.name", "`app_order`.`name`"}, {"MySQL table.column with backticks", &MySQLDialect{}, "app_", "`order`.name", "`app_order`.`name`"}, // PostgreSQL {"PostgreSQL table.column with prefix", &PostgreSQLDialect{}, "app_", "order.name", "\"app_order\".\"name\""}, {"PostgreSQL table.column with quotes", &PostgreSQLDialect{}, "app_", "\"order\".name", "\"app_order\".\"name\""}, // SQLite {"SQLite table.column with prefix", &SQLiteDialect{}, "app_", "user.email", "\"app_user\".\"email\""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { processor := NewIdentifierProcessor(tt.dialect, tt.prefix) result := processor.ProcessColumn(tt.input) if result != tt.expected { t.Errorf("ProcessColumn(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } // TestIdentifierProcessorConditionString 测试条件字符串处理 func TestIdentifierProcessorConditionString(t *testing.T) { tests := []struct { name string dialect Dialect prefix string input string contains []string // 结果应该包含这些字符串 }{ // MySQL 简单条件 { "MySQL simple condition", &MySQLDialect{}, "app_", "user.id = order.user_id", []string{"`app_user`", "`app_order`"}, }, // MySQL 复杂条件 { "MySQL complex condition", &MySQLDialect{}, "app_", "user.id = order.user_id AND order.status = 1", []string{"`app_user`", "`app_order`"}, }, // PostgreSQL { "PostgreSQL condition", &PostgreSQLDialect{}, "app_", "user.id = order.user_id", []string{"\"app_user\"", "\"app_order\""}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { processor := NewIdentifierProcessor(tt.dialect, tt.prefix) result := processor.ProcessConditionString(tt.input) for _, expected := range tt.contains { if !strings.Contains(result, expected) { t.Errorf("ProcessConditionString(%q) = %q, should contain %q", tt.input, result, expected) } } }) } } // TestHoTimeDBHelperMethods 测试 HoTimeDB 的辅助方法 T() 和 C() func TestHoTimeDBHelperMethods(t *testing.T) { // 创建 MySQL 数据库实例 mysqlDB := &HoTimeDB{ Type: "mysql", Prefix: "app_", } mysqlDB.initDialect() // 测试 T() 方法 t.Run("MySQL T() method", func(t *testing.T) { result := mysqlDB.T("order") expected := "`app_order`" if result != expected { t.Errorf("T(\"order\") = %q, want %q", result, expected) } }) // 测试 C() 方法(两个参数) t.Run("MySQL C() method with two args", func(t *testing.T) { result := mysqlDB.C("order", "name") expected := "`app_order`.`name`" if result != expected { t.Errorf("C(\"order\", \"name\") = %q, want %q", result, expected) } }) // 测试 C() 方法(一个参数,点号格式) t.Run("MySQL C() method with dot notation", func(t *testing.T) { result := mysqlDB.C("order.name") expected := "`app_order`.`name`" if result != expected { t.Errorf("C(\"order.name\") = %q, want %q", result, expected) } }) // 创建 PostgreSQL 数据库实例 pgDB := &HoTimeDB{ Type: "postgres", Prefix: "app_", } pgDB.initDialect() // 测试 PostgreSQL 的 T() 方法 t.Run("PostgreSQL T() method", func(t *testing.T) { result := pgDB.T("order") expected := "\"app_order\"" if result != expected { t.Errorf("T(\"order\") = %q, want %q", result, expected) } }) // 测试 PostgreSQL 的 C() 方法 t.Run("PostgreSQL C() method", func(t *testing.T) { result := pgDB.C("order", "name") expected := "\"app_order\".\"name\"" if result != expected { t.Errorf("C(\"order\", \"name\") = %q, want %q", result, expected) } }) } // TestWhereWithORCondition 测试 OR 条件处理是否正确添加括号 func TestWhereWithORCondition(t *testing.T) { // 创建 MySQL 数据库实例 mysqlDB := &HoTimeDB{ Type: "mysql", Prefix: "", } mysqlDB.initDialect() // 测试 OR 与普通条件组合 (假设 A: 顺序问题) t.Run("OR with normal condition", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, "state": 0, } where, params := mysqlDB.where(data) fmt.Println("Test 1 - OR with normal condition:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) // 检查 OR 条件是否被括号包裹 if !strings.Contains(where, "(") || !strings.Contains(where, ")") { t.Errorf("OR condition should be wrapped with parentheses, got: %s", where) } // 检查是否有 AND 连接 if !strings.Contains(where, "AND") { t.Errorf("OR condition and normal condition should be connected with AND, got: %s", where) } }) // 测试纯 OR 条件(无其他普通条件) t.Run("Pure OR condition", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, } where, params := mysqlDB.where(data) fmt.Println("Test 2 - Pure OR condition:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) // 检查 OR 条件内部应该用 OR 连接 if !strings.Contains(where, "OR") { t.Errorf("OR condition should contain OR keyword, got: %s", where) } }) // 测试多个普通条件与 OR 组合 (假设 A) t.Run("OR with multiple normal conditions", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, "state": 0, "status": 1, } where, params := mysqlDB.where(data) fmt.Println("Test 3 - OR with multiple normal conditions:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) // 应该有括号 if !strings.Contains(where, "(") { t.Errorf("OR condition should be wrapped with parentheses, got: %s", where) } }) // 测试嵌套 AND/OR 条件 (假设 B, E) t.Run("Nested AND/OR conditions", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "AND": Map{ "phone": "123", "status": 1, }, }, "state": 0, } where, params := mysqlDB.where(data) fmt.Println("Test 4 - Nested AND/OR conditions:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) }) // 测试空 OR 条件 (假设 C) t.Run("Empty OR condition", func(t *testing.T) { data := Map{ "OR": Map{}, "state": 0, } where, params := mysqlDB.where(data) fmt.Println("Test 5 - Empty OR condition:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) }) // 测试 OR 与 LIMIT, ORDER 组合 (假设 D) t.Run("OR with LIMIT and ORDER", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, "state": 0, "ORDER": "id DESC", "LIMIT": 10, } where, params := mysqlDB.where(data) fmt.Println("Test 6 - OR with LIMIT and ORDER:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) }) // 测试同时有 OR 和 AND 关键字 (假设 E) t.Run("Both OR and AND keywords", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, "AND": Map{ "type": 1, "source": "web", }, "state": 0, } where, params := mysqlDB.where(data) fmt.Println("Test 7 - Both OR and AND keywords:") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) }) // 测试普通条件在 OR 之前(排序后)(假设 A) t.Run("Normal condition before OR alphabetically", func(t *testing.T) { data := Map{ "OR": Map{ "username": "test", "phone": "123", }, "active": 1, // 'a' 在 'O' 之前 } where, params := mysqlDB.where(data) fmt.Println("Test 8 - Normal condition before OR (alphabetically):") fmt.Println(" Generated WHERE:", where) fmt.Println(" Params count:", len(params)) }) } // 打印测试结果(用于调试) func ExampleIdentifierProcessor() { // MySQL 示例 mysqlProcessor := NewIdentifierProcessor(&MySQLDialect{}, "app_") fmt.Println("MySQL:") fmt.Println(" Table:", mysqlProcessor.ProcessTableName("order")) fmt.Println(" Column:", mysqlProcessor.ProcessColumn("order.name")) fmt.Println(" Condition:", mysqlProcessor.ProcessConditionString("user.id = order.user_id")) // PostgreSQL 示例 pgProcessor := NewIdentifierProcessor(&PostgreSQLDialect{}, "app_") fmt.Println("PostgreSQL:") fmt.Println(" Table:", pgProcessor.ProcessTableName("order")) fmt.Println(" Column:", pgProcessor.ProcessColumn("order.name")) fmt.Println(" Condition:", pgProcessor.ProcessConditionString("user.id = order.user_id")) // Output: // MySQL: // Table: `app_order` // Column: `app_order`.`name` // Condition: `app_user`.`id` = `app_order`.`user_id` // PostgreSQL: // Table: "app_order" // Column: "app_order"."name" // Condition: "app_user"."id" = "app_order"."user_id" }