hotime/db/dialect_test.go

442 lines
12 KiB
Go
Raw Permalink Normal View History

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"
}