Files
srdb/schema_test.go

1131 lines
29 KiB
Go
Raw Normal View History

package srdb
import (
"strings"
"testing"
)
// Package-level test schemas
var (
UserSchema *Schema
LogSchema *Schema
OrderSchema *Schema
)
func init() {
var err error
// UserSchema 用户表 Schema
UserSchema, err = NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
{Name: "age", Type: Int64, Indexed: true, Comment: "年龄"},
{Name: "email", Type: String, Indexed: true, Comment: "邮箱"},
{Name: "description", Type: String, Indexed: false, Comment: "描述"},
})
if err != nil {
panic("Failed to create UserSchema: " + err.Error())
}
// LogSchema 日志表 Schema
LogSchema, err = NewSchema("logs", []Field{
{Name: "level", Type: String, Indexed: true, Comment: "日志级别"},
{Name: "message", Type: String, Indexed: false, Comment: "日志消息"},
{Name: "source", Type: String, Indexed: true, Comment: "来源"},
{Name: "error_code", Type: Int64, Indexed: true, Comment: "错误码"},
})
if err != nil {
panic("Failed to create LogSchema: " + err.Error())
}
// OrderSchema 订单表 Schema
OrderSchema, err = NewSchema("orders", []Field{
{Name: "order_id", Type: String, Indexed: true, Comment: "订单ID"},
{Name: "user_id", Type: Int64, Indexed: true, Comment: "用户ID"},
{Name: "amount", Type: Float64, Indexed: true, Comment: "金额"},
{Name: "status", Type: String, Indexed: true, Comment: "状态"},
{Name: "paid", Type: Bool, Indexed: true, Comment: "是否支付"},
})
if err != nil {
panic("Failed to create OrderSchema: " + err.Error())
}
}
func TestSchema(t *testing.T) {
// 创建 Schema
schema, err := NewSchema("test", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "名称"},
{Name: "age", Type: Int64, Indexed: true, Comment: "年龄"},
{Name: "score", Type: Float64, Indexed: false, Comment: "分数"},
})
if err != nil {
t.Fatal(err)
}
// 测试数据
data := map[string]any{
"name": "Alice",
"age": 25,
"score": 95.5,
}
// 验证
err = schema.Validate(data)
if err != nil {
t.Errorf("Validation failed: %v", err)
}
// 获取索引字段
indexedFields := schema.GetIndexedFields()
if len(indexedFields) != 2 {
t.Errorf("Expected 2 indexed fields, got %d", len(indexedFields))
}
t.Log("Schema test passed!")
}
func TestSchemaValidation(t *testing.T) {
schema, err := NewSchema("test", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "名称"},
{Name: "age", Type: Int64, Indexed: true, Comment: "年龄"},
})
if err != nil {
t.Fatal(err)
}
// 正确的数据
validData := map[string]any{
"name": "Bob",
"age": 30,
}
err = schema.Validate(validData)
if err != nil {
t.Errorf("Valid data failed validation: %v", err)
}
// 错误的数据类型
invalidData := map[string]any{
"name": "Charlie",
"age": "thirty", // 应该是 int64
}
err = schema.Validate(invalidData)
if err == nil {
t.Error("Invalid data should fail validation")
}
t.Log("Schema validation test passed!")
}
// TestNewSchemaValidation 测试 NewSchema 的各种验证场景
func TestNewSchemaValidation(t *testing.T) {
tests := []struct {
name string
schemaName string
fields []Field
shouldError bool
errorMsg string
}{
{
name: "Valid schema",
schemaName: "users",
fields: []Field{
{Name: "id", Type: Int64},
{Name: "name", Type: String},
},
shouldError: false,
},
{
name: "Empty schema name",
schemaName: "",
fields: []Field{{Name: "id", Type: Int64}},
shouldError: true,
errorMsg: "schema name cannot be empty",
},
{
name: "Empty fields array",
schemaName: "users",
fields: []Field{},
shouldError: true,
errorMsg: "schema must have at least one field",
},
{
name: "Nil fields array",
schemaName: "users",
fields: nil,
shouldError: true,
errorMsg: "schema must have at least one field",
},
{
name: "Empty field name at index 0",
schemaName: "users",
fields: []Field{
{Name: "", Type: Int64},
},
shouldError: true,
errorMsg: "field at index 0 has empty name",
},
{
name: "Empty field name at index 1",
schemaName: "users",
fields: []Field{
{Name: "id", Type: Int64},
{Name: "", Type: String},
},
shouldError: true,
errorMsg: "field at index 1 has empty name",
},
{
name: "Duplicate field name",
schemaName: "users",
fields: []Field{
{Name: "id", Type: Int64},
{Name: "name", Type: String},
{Name: "id", Type: String}, // Duplicate
},
shouldError: true,
errorMsg: "duplicate field name: id",
},
{
name: "Valid schema with single field",
schemaName: "logs",
fields: []Field{
{Name: "message", Type: String},
},
shouldError: false,
},
{
name: "Valid schema with indexed field",
schemaName: "users",
fields: []Field{
{Name: "id", Type: Int64, Indexed: true},
{Name: "email", Type: String, Indexed: true},
{Name: "age", Type: Int64},
},
shouldError: false,
},
{
name: "Valid schema with comments",
schemaName: "products",
fields: []Field{
{Name: "id", Type: Int64, Comment: "产品ID"},
{Name: "name", Type: String, Comment: "产品名称"},
{Name: "price", Type: Float64, Comment: "价格"},
},
shouldError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
schema, err := NewSchema(tt.schemaName, tt.fields)
if tt.shouldError {
if err == nil {
t.Errorf("Expected error but got none")
return
}
if !strings.Contains(err.Error(), tt.errorMsg) {
t.Errorf("Expected error to contain %q, got %q", tt.errorMsg, err.Error())
}
// 验证错误码是 ErrCodeSchemaInvalid
if GetErrorCode(err) != ErrCodeSchemaInvalid {
t.Errorf("Expected error code %d, got %d", ErrCodeSchemaInvalid, GetErrorCode(err))
}
} else {
if err != nil {
t.Errorf("Expected no error but got: %v", err)
return
}
// 验证返回的 schema 是正确的
if schema == nil {
t.Errorf("Expected schema, got nil")
return
}
if schema.Name != tt.schemaName {
t.Errorf("Expected schema name %q, got %q", tt.schemaName, schema.Name)
}
if len(schema.Fields) != len(tt.fields) {
t.Errorf("Expected %d fields, got %d", len(tt.fields), len(schema.Fields))
}
}
})
}
}
// TestNewSchemaFieldValidation 测试字段级别的验证
func TestNewSchemaFieldValidation(t *testing.T) {
t.Run("Multiple duplicate field names", func(t *testing.T) {
schema, err := NewSchema("test", []Field{
{Name: "id", Type: Int64},
{Name: "name", Type: String},
{Name: "id", Type: String}, // First duplicate
{Name: "name", Type: String}, // Second duplicate
})
if err == nil {
t.Errorf("Expected error for duplicate field names")
return
}
// 应该在第一个重复处就停止
if !strings.Contains(err.Error(), "duplicate field name: id") {
t.Errorf("Expected error about duplicate field 'id', got: %v", err)
}
if schema != nil {
t.Errorf("Expected nil schema on error, got %+v", schema)
}
})
t.Run("Case sensitive field names", func(t *testing.T) {
// 大小写敏感ID 和 id 应该是不同的字段
schema, err := NewSchema("test", []Field{
{Name: "id", Type: Int64},
{Name: "ID", Type: Int64},
{Name: "Id", Type: Int64},
})
if err != nil {
t.Errorf("Expected no error for case-sensitive field names, got: %v", err)
return
}
if len(schema.Fields) != 3 {
t.Errorf("Expected 3 fields (case sensitive), got %d", len(schema.Fields))
}
})
t.Run("Fields with all types", func(t *testing.T) {
schema, err := NewSchema("test", []Field{
{Name: "int_field", Type: Int64},
{Name: "string_field", Type: String},
{Name: "float_field", Type: Float64},
{Name: "bool_field", Type: Bool},
})
if err != nil {
t.Errorf("Expected no error, got: %v", err)
return
}
if len(schema.Fields) != 4 {
t.Errorf("Expected 4 fields, got %d", len(schema.Fields))
}
// 验证每个字段的类型
expectedTypes := map[string]FieldType{
"int_field": Int64,
"string_field": String,
"float_field": Float64,
"bool_field": Bool,
}
for _, field := range schema.Fields {
expectedType, exists := expectedTypes[field.Name]
if !exists {
t.Errorf("Unexpected field name: %s", field.Name)
continue
}
if field.Type != expectedType {
t.Errorf("Field %s: expected type %v, got %v", field.Name, expectedType, field.Type)
}
}
})
}
// TestNewSchemaEdgeCases 测试边界情况
func TestNewSchemaEdgeCases(t *testing.T) {
t.Run("Very long schema name", func(t *testing.T) {
longName := strings.Repeat("a", 1000)
schema, err := NewSchema(longName, []Field{
{Name: "id", Type: Int64},
})
if err != nil {
t.Errorf("Expected no error for long schema name, got: %v", err)
return
}
if schema.Name != longName {
t.Errorf("Expected schema name to be preserved")
}
})
t.Run("Very long field name", func(t *testing.T) {
longFieldName := strings.Repeat("b", 1000)
schema, err := NewSchema("test", []Field{
{Name: longFieldName, Type: Int64},
})
if err != nil {
t.Errorf("Expected no error for long field name, got: %v", err)
return
}
if schema.Fields[0].Name != longFieldName {
t.Errorf("Expected field name to be preserved")
}
})
t.Run("Many fields", func(t *testing.T) {
fields := make([]Field, 100)
for i := 0; i < 100; i++ {
fields[i] = Field{
Name: strings.Repeat("field", 1) + string(rune('a'+i)),
Type: Int64,
}
}
schema, err := NewSchema("test", fields)
if err != nil {
t.Errorf("Expected no error for many fields, got: %v", err)
return
}
if len(schema.Fields) != 100 {
t.Errorf("Expected 100 fields, got %d", len(schema.Fields))
}
})
t.Run("Field with special characters", func(t *testing.T) {
schema, err := NewSchema("test", []Field{
{Name: "field_with_underscore", Type: Int64},
{Name: "field123", Type: Int64},
{Name: "字段名", Type: String}, // 中文字段名
})
if err != nil {
t.Errorf("Expected no error for special characters, got: %v", err)
return
}
if len(schema.Fields) != 3 {
t.Errorf("Expected 3 fields with special characters, got %d", len(schema.Fields))
}
})
}
// TestNewSchemaConsistency 测试创建后的一致性
func TestNewSchemaConsistency(t *testing.T) {
t.Run("Field order preserved", func(t *testing.T) {
fields := []Field{
{Name: "zebra", Type: String},
{Name: "alpha", Type: Int64},
{Name: "beta", Type: Float64},
}
schema, err := NewSchema("test", fields)
if err != nil {
t.Errorf("Expected no error, got: %v", err)
return
}
// 字段顺序应该保持不变
for i, field := range schema.Fields {
if field.Name != fields[i].Name {
t.Errorf("Field order not preserved at index %d: expected %s, got %s",
i, fields[i].Name, field.Name)
}
}
})
t.Run("Field properties preserved", func(t *testing.T) {
fields := []Field{
{Name: "id", Type: Int64, Indexed: true, Comment: "Primary key"},
{Name: "name", Type: String, Indexed: false, Comment: "User name"},
}
schema, err := NewSchema("users", fields)
if err != nil {
t.Errorf("Expected no error, got: %v", err)
return
}
// 验证所有属性都被保留
if schema.Fields[0].Indexed != true {
t.Errorf("Expected field 0 to be indexed")
}
if schema.Fields[1].Indexed != false {
t.Errorf("Expected field 1 to not be indexed")
}
if schema.Fields[0].Comment != "Primary key" {
t.Errorf("Expected field 0 comment to be preserved")
}
if schema.Fields[1].Comment != "User name" {
t.Errorf("Expected field 1 comment to be preserved")
}
})
}
func TestExtractIndexValue(t *testing.T) {
schema, err := NewSchema("test", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "名称"},
{Name: "age", Type: Int64, Indexed: true, Comment: "年龄"},
})
if err != nil {
t.Fatal(err)
}
data := map[string]any{
"name": "David",
"age": float64(35), // JSON 解析后是 float64
}
// 提取 name
name, err := schema.ExtractIndexValue("name", data)
if err != nil {
t.Errorf("Failed to extract name: %v", err)
}
if name != "David" {
t.Errorf("Expected 'David', got %v", name)
}
// 提取 age (float64 → int64)
age, err := schema.ExtractIndexValue("age", data)
if err != nil {
t.Errorf("Failed to extract age: %v", err)
}
if age != int64(35) {
t.Errorf("Expected 35, got %v", age)
}
t.Log("Extract index value test passed!")
}
func TestPredefinedSchemas(t *testing.T) {
// 测试 UserSchema
userData := map[string]any{
"name": "Alice",
"age": 25,
"email": "alice@example.com",
"description": "Test user",
}
err := UserSchema.Validate(userData)
if err != nil {
t.Errorf("UserSchema validation failed: %v", err)
}
// 测试 LogSchema
logData := map[string]any{
"level": "ERROR",
"message": "Something went wrong",
"source": "api",
"error_code": 500,
}
err = LogSchema.Validate(logData)
if err != nil {
t.Errorf("LogSchema validation failed: %v", err)
}
t.Log("Predefined schemas test passed!")
}
// TestChecksumDeterminism 测试 checksum 的确定性
func TestChecksumDeterminism(t *testing.T) {
// 创建相同的 Schema 多次
for i := range 10 {
s1, err := NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
{Name: "age", Type: Int64, Indexed: false, Comment: "年龄"},
})
if err != nil {
t.Fatal(err)
}
s2, err := NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
{Name: "age", Type: Int64, Indexed: false, Comment: "年龄"},
})
if err != nil {
t.Fatal(err)
}
checksum1, err := s1.ComputeChecksum()
if err != nil {
t.Fatal(err)
}
checksum2, err := s2.ComputeChecksum()
if err != nil {
t.Fatal(err)
}
if checksum1 != checksum2 {
t.Errorf("Iteration %d: checksums should be equal, got %s and %s", i, checksum1, checksum2)
}
}
t.Log("✅ Checksum is deterministic")
}
// TestChecksumFieldOrderIndependent 测试字段顺序不影响 checksum
func TestChecksumFieldOrderIndependent(t *testing.T) {
s1, err := NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
{Name: "age", Type: Int64, Indexed: false, Comment: "年龄"},
})
if err != nil {
t.Fatal(err)
}
s2, err := NewSchema("users", []Field{
{Name: "age", Type: Int64, Indexed: false, Comment: "年龄"},
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
})
if err != nil {
t.Fatal(err)
}
checksum1, _ := s1.ComputeChecksum()
checksum2, _ := s2.ComputeChecksum()
if checksum1 != checksum2 {
t.Errorf("Checksums should be equal regardless of field order, got %s and %s", checksum1, checksum2)
} else {
t.Logf("✅ Field order does not affect checksum (expected behavior)")
t.Logf(" checksum: %s", checksum1)
}
}
// TestChecksumDifferentData 测试不同 Schema 的 checksum 应该不同
func TestChecksumDifferentData(t *testing.T) {
s1, err := NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: true, Comment: "用户名"},
})
if err != nil {
t.Fatal(err)
}
s2, err := NewSchema("users", []Field{
{Name: "name", Type: String, Indexed: false, Comment: "用户名"}, // Indexed 不同
})
if err != nil {
t.Fatal(err)
}
checksum1, _ := s1.ComputeChecksum()
checksum2, _ := s2.ComputeChecksum()
if checksum1 == checksum2 {
t.Error("Different schemas should have different checksums")
} else {
t.Log("✅ Different schemas have different checksums")
}
}
// TestChecksumMultipleFieldOrders 测试多个字段的各种排列组合都产生相同 checksum
func TestChecksumMultipleFieldOrders(t *testing.T) {
// 定义 4 个字段
fieldA := Field{Name: "id", Type: Int64, Indexed: true, Comment: "ID"}
fieldB := Field{Name: "name", Type: String, Indexed: false, Comment: "名称"}
fieldC := Field{Name: "age", Type: Int64, Indexed: false, Comment: "年龄"}
fieldD := Field{Name: "email", Type: String, Indexed: true, Comment: "邮箱"}
// 创建不同顺序的 Schema
mustNewSchema := func(name string, fields []Field) *Schema {
s, err := NewSchema(name, fields)
if err != nil {
t.Fatalf("Failed to create schema: %v", err)
}
return s
}
schemas := []*Schema{
mustNewSchema("test", []Field{fieldA, fieldB, fieldC, fieldD}), // 原始顺序
mustNewSchema("test", []Field{fieldD, fieldC, fieldB, fieldA}), // 完全反转
mustNewSchema("test", []Field{fieldB, fieldD, fieldA, fieldC}), // 随机顺序 1
mustNewSchema("test", []Field{fieldC, fieldA, fieldD, fieldB}), // 随机顺序 2
mustNewSchema("test", []Field{fieldD, fieldA, fieldC, fieldB}), // 随机顺序 3
}
// 计算所有 checksum
checksums := make([]string, len(schemas))
for i, s := range schemas {
checksum, err := s.ComputeChecksum()
if err != nil {
t.Fatalf("Failed to compute checksum for schema %d: %v", i, err)
}
checksums[i] = checksum
}
// 验证所有 checksum 都相同
expectedChecksum := checksums[0]
for i := 1; i < len(checksums); i++ {
if checksums[i] != expectedChecksum {
t.Errorf("Schema %d has different checksum: expected %s, got %s", i, expectedChecksum, checksums[i])
}
}
t.Logf("✅ All %d field permutations produce the same checksum", len(schemas))
t.Logf(" checksum: %s", expectedChecksum)
}
// TestStructToFields 测试从结构体生成 Field 列表
func TestStructToFields(t *testing.T) {
// 定义测试结构体
type User struct {
Name string `srdb:"name;indexed;comment:用户名"`
Age int64 `srdb:"age;comment:年龄"`
Email string `srdb:"email;indexed;comment:邮箱"`
Score float64 `srdb:"score;comment:分数"`
Active bool `srdb:"active;comment:是否激活"`
}
// 生成 Field 列表
fields, err := StructToFields(User{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
// 验证字段数量
if len(fields) != 5 {
t.Errorf("Expected 5 fields, got %d", len(fields))
}
// 验证每个字段
expectedFields := map[string]struct {
Type FieldType
Indexed bool
Comment string
}{
"name": {String, true, "用户名"},
"age": {Int64, false, "年龄"},
"email": {String, true, "邮箱"},
"score": {Float64, false, "分数"},
"active": {Bool, false, "是否激活"},
}
for _, field := range fields {
expected, exists := expectedFields[field.Name]
if !exists {
t.Errorf("Unexpected field: %s", field.Name)
continue
}
if field.Type != expected.Type {
t.Errorf("Field %s: expected type %v, got %v", field.Name, expected.Type, field.Type)
}
if field.Indexed != expected.Indexed {
t.Errorf("Field %s: expected indexed=%v, got %v", field.Name, expected.Indexed, field.Indexed)
}
if field.Comment != expected.Comment {
t.Errorf("Field %s: expected comment=%s, got %s", field.Name, expected.Comment, field.Comment)
}
}
t.Log("✓ StructToFields basic test passed")
}
// TestStructToFieldsDefaultName 测试默认字段名 (snake_case)
func TestStructToFieldsDefaultName(t *testing.T) {
type Product struct {
ProductName string // 没有 tag应该使用 snake_case: product_name
Price int64 // 没有 tag应该使用 snake_case: price
}
fields, err := StructToFields(Product{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
if len(fields) != 2 {
t.Errorf("Expected 2 fields, got %d", len(fields))
}
// 验证默认字段名snake_case
if fields[0].Name != "product_name" {
t.Errorf("Expected field name 'product_name', got '%s'", fields[0].Name)
}
if fields[1].Name != "price" {
t.Errorf("Expected field name 'price', got '%s'", fields[1].Name)
}
t.Log("✓ Default field name (snake_case) test passed")
}
// TestStructToFieldsIgnore 测试忽略字段
func TestStructToFieldsIgnore(t *testing.T) {
type Order struct {
OrderID string `srdb:"order_id;comment:订单ID"`
Internal string `srdb:"-"` // 应该被忽略
CreatedAt int64 `srdb:"created_at;comment:创建时间"`
}
fields, err := StructToFields(Order{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
// 应该只有 2 个字段Internal 被忽略)
if len(fields) != 2 {
t.Errorf("Expected 2 fields (excluding ignored field), got %d", len(fields))
}
// 验证没有 Internal 字段
for _, field := range fields {
if field.Name == "internal" || field.Name == "Internal" {
t.Errorf("Field 'Internal' should have been ignored")
}
}
t.Log("✓ Ignore field test passed")
}
// TestStructToFieldsPointer 测试指针类型
func TestStructToFieldsPointer(t *testing.T) {
type Item struct {
Name string `srdb:"name;comment:名称"`
}
// 使用指针
fields, err := StructToFields(&Item{})
if err != nil {
t.Fatalf("StructToFields with pointer failed: %v", err)
}
if len(fields) != 1 {
t.Errorf("Expected 1 field, got %d", len(fields))
}
if fields[0].Name != "name" {
t.Errorf("Expected field name 'name', got '%s'", fields[0].Name)
}
t.Log("✓ Pointer type test passed")
}
// TestStructToFieldsAllTypes 测试所有支持的类型
func TestStructToFieldsAllTypes(t *testing.T) {
type AllTypes struct {
Int int `srdb:"int"`
Int64 int64 `srdb:"int64"`
Int32 int32 `srdb:"int32"`
Int16 int16 `srdb:"int16"`
Int8 int8 `srdb:"int8"`
Uint uint `srdb:"uint"`
Uint64 uint64 `srdb:"uint64"`
Uint32 uint32 `srdb:"uint32"`
Uint16 uint16 `srdb:"uint16"`
Uint8 uint8 `srdb:"uint8"`
String string `srdb:"string"`
Float64 float64 `srdb:"float64"`
Float32 float32 `srdb:"float32"`
Bool bool `srdb:"bool"`
}
fields, err := StructToFields(AllTypes{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
if len(fields) != 14 {
t.Errorf("Expected 14 fields, got %d", len(fields))
}
// 验证所有类型都精确映射到对应的 FieldType
expectedTypes := map[string]FieldType{
"int": Int,
"int64": Int64,
"int32": Int32,
"int16": Int16,
"int8": Int8,
"uint": Uint,
"uint64": Uint64,
"uint32": Uint32,
"uint16": Uint16,
"uint8": Uint8,
"string": String,
"float64": Float64,
"float32": Float32,
"bool": Bool,
}
for _, field := range fields {
expectedType, exists := expectedTypes[field.Name]
if !exists {
t.Errorf("Unexpected field: %s", field.Name)
continue
}
if field.Type != expectedType {
t.Errorf("Field %s: expected %v, got %v", field.Name, expectedType, field.Type)
}
}
t.Log("✓ All types test passed")
}
// TestStructToFieldsWithSchema 测试完整的使用流程
func TestStructToFieldsWithSchema(t *testing.T) {
// 定义结构体
type Customer struct {
CustomerID string `srdb:"customer_id;indexed;comment:客户ID"`
Name string `srdb:"name;comment:客户名称"`
Email string `srdb:"email;indexed;comment:邮箱"`
Balance int64 `srdb:"balance;comment:余额"`
}
// 生成 Field 列表
fields, err := StructToFields(Customer{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
// 创建 Schema
schema, err := NewSchema("customers", fields)
if err != nil {
t.Fatal(err)
}
// 验证 Schema
if schema.Name != "customers" {
t.Errorf("Expected schema name 'customers', got '%s'", schema.Name)
}
if len(schema.Fields) != 4 {
t.Errorf("Expected 4 fields in schema, got %d", len(schema.Fields))
}
// 验证索引字段
indexedFields := schema.GetIndexedFields()
if len(indexedFields) != 2 {
t.Errorf("Expected 2 indexed fields, got %d", len(indexedFields))
}
// 测试数据验证
validData := map[string]any{
"customer_id": "C001",
"name": "张三",
"email": "zhangsan@example.com",
"balance": int64(1000),
}
err = schema.Validate(validData)
if err != nil {
t.Errorf("Valid data should pass validation: %v", err)
}
// 测试无效数据
invalidData := map[string]any{
"customer_id": "C002",
"name": "李四",
"email": 123, // 错误类型
"balance": int64(2000),
}
err = schema.Validate(invalidData)
if err == nil {
t.Error("Invalid data should fail validation")
}
t.Log("✓ Complete workflow test passed")
}
// TestStructToFieldsTagVariations 测试各种 tag 组合
func TestStructToFieldsTagVariations(t *testing.T) {
type TestStruct struct {
// 只有字段名
Field1 string `srdb:"field1"`
// 字段名 + indexed
Field2 string `srdb:"field2;indexed"`
// 字段名 + comment
Field3 string `srdb:"field3;comment:字段3"`
// 完整格式
Field4 string `srdb:"field4;indexed;comment:字段4"`
// 只有 indexed使用默认字段名
Field5 string `srdb:";indexed"`
// 只有 comment使用默认字段名
Field6 string `srdb:";comment:字段6"`
// 空 tag使用默认字段名
Field7 string
// indexed + comment使用默认字段名
Field8 string `srdb:";indexed;comment:字段8"`
}
fields, err := StructToFields(TestStruct{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
if len(fields) != 8 {
t.Errorf("Expected 8 fields, got %d", len(fields))
}
// 验证各个字段
tests := []struct {
name string
indexed bool
comment string
}{
{"field1", false, ""},
{"field2", true, ""},
{"field3", false, "字段3"},
{"field4", true, "字段4"},
{"field5", true, ""},
{"field6", false, "字段6"},
{"field7", false, ""},
{"field8", true, "字段8"},
}
for i, test := range tests {
if fields[i].Name != test.name {
t.Errorf("Field %d: expected name %s, got %s", i+1, test.name, fields[i].Name)
}
if fields[i].Indexed != test.indexed {
t.Errorf("Field %s: expected indexed=%v, got %v", test.name, test.indexed, fields[i].Indexed)
}
if fields[i].Comment != test.comment {
t.Errorf("Field %s: expected comment=%s, got %s", test.name, test.comment, fields[i].Comment)
}
}
t.Log("✓ Tag variations test passed")
}
// TestStructToFieldsErrors 测试错误情况
func TestStructToFieldsErrors(t *testing.T) {
// 测试非结构体类型
_, err := StructToFields("not a struct")
if err == nil {
t.Error("Expected error for non-struct type")
}
// 测试 nil
_, err = StructToFields(nil)
if err == nil {
t.Error("Expected error for nil")
}
// 测试没有导出字段的结构体
type Empty struct {
private string // 未导出
}
_, err = StructToFields(Empty{})
if err == nil {
t.Error("Expected error for struct with no exported fields")
}
t.Log("✓ Error handling test passed")
}
// TestCamelToSnake 测试驼峰命名转 snake_case
func TestCamelToSnake(t *testing.T) {
tests := []struct {
input string
expected string
}{
// 基本测试
{"UserName", "user_name"},
{"EmailAddress", "email_address"},
{"IsActive", "is_active"},
// 单个单词
{"Name", "name"},
{"ID", "id"},
// 连续大写字母
{"HTTPServer", "http_server"},
{"XMLParser", "xml_parser"},
{"HTMLContent", "html_content"},
{"URLPath", "url_path"},
// 带数字
{"User2Name", "user2_name"},
{"Address1", "address1"},
// 全小写
{"username", "username"},
// 全大写
{"HTTP", "http"},
{"API", "api"},
// 混合情况
{"getUserByID", "get_user_by_id"},
{"HTTPSConnection", "https_connection"},
{"createHTMLFile", "create_html_file"},
// 边界情况
{"A", "a"},
{"AB", "ab"},
{"AbC", "ab_c"},
}
for _, test := range tests {
result := camelToSnake(test.input)
if result != test.expected {
t.Errorf("camelToSnake(%q) = %q, expected %q", test.input, result, test.expected)
}
}
t.Log("✓ camelToSnake test passed")
}
// TestStructToFieldsSnakeCase 测试默认使用 snake_case
func TestStructToFieldsSnakeCase(t *testing.T) {
type User struct {
UserName string // 应该转为 user_name
EmailAddress string // 应该转为 email_address
IsActive bool // 应该转为 is_active
HTTPEndpoint string // 应该转为 http_endpoint
ID int64 // 应该转为 id
}
fields, err := StructToFields(User{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
expected := []string{"user_name", "email_address", "is_active", "http_endpoint", "id"}
if len(fields) != len(expected) {
t.Fatalf("Expected %d fields, got %d", len(expected), len(fields))
}
for i, exp := range expected {
if fields[i].Name != exp {
t.Errorf("Field %d: expected name %s, got %s", i, exp, fields[i].Name)
}
}
t.Log("✓ Default snake_case test passed")
}
// TestStructToFieldsOverrideSnakeCase 测试可以覆盖默认 snake_case
func TestStructToFieldsOverrideSnakeCase(t *testing.T) {
type User struct {
UserName string `srdb:"username"` // 覆盖默认的 user_name
IsActive bool `srdb:"active;comment:激活"` // 覆盖默认的 is_active
}
fields, err := StructToFields(User{})
if err != nil {
t.Fatalf("StructToFields failed: %v", err)
}
if len(fields) != 2 {
t.Fatalf("Expected 2 fields, got %d", len(fields))
}
// 验证覆盖成功
if fields[0].Name != "username" {
t.Errorf("Expected field name 'username', got '%s'", fields[0].Name)
}
if fields[1].Name != "active" {
t.Errorf("Expected field name 'active', got '%s'", fields[1].Name)
}
t.Log("✓ Override snake_case test passed")
}