Files
srdb/schema_test.go
bourdon ae87c38776 Initial commit: SRDB - High-performance LSM-Tree database
- Core engine with MemTable, SST, WAL
- B+Tree indexing for SST files  
- Leveled compaction strategy
- Multi-table database management
- Schema validation and secondary indexes
- Query builder with complex conditions
- Web UI with HTMX for data visualization
- Command-line tools for diagnostics
2025-10-08 06:38:28 +08:00

268 lines
7.9 KiB
Go

package srdb
import (
"testing"
)
// UserSchema 用户表 Schema
var UserSchema = NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
{Name: "age", Type: FieldTypeInt64, Indexed: true, Comment: "年龄"},
{Name: "email", Type: FieldTypeString, Indexed: true, Comment: "邮箱"},
{Name: "description", Type: FieldTypeString, Indexed: false, Comment: "描述"},
})
// LogSchema 日志表 Schema
var LogSchema = NewSchema("logs", []Field{
{Name: "level", Type: FieldTypeString, Indexed: true, Comment: "日志级别"},
{Name: "message", Type: FieldTypeString, Indexed: false, Comment: "日志消息"},
{Name: "source", Type: FieldTypeString, Indexed: true, Comment: "来源"},
{Name: "error_code", Type: FieldTypeInt64, Indexed: true, Comment: "错误码"},
})
// OrderSchema 订单表 Schema
var OrderSchema = NewSchema("orders", []Field{
{Name: "order_id", Type: FieldTypeString, Indexed: true, Comment: "订单ID"},
{Name: "user_id", Type: FieldTypeInt64, Indexed: true, Comment: "用户ID"},
{Name: "amount", Type: FieldTypeFloat, Indexed: true, Comment: "金额"},
{Name: "status", Type: FieldTypeString, Indexed: true, Comment: "状态"},
{Name: "paid", Type: FieldTypeBool, Indexed: true, Comment: "是否支付"},
})
func TestSchema(t *testing.T) {
// 创建 Schema
schema := NewSchema("test", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "名称"},
{Name: "age", Type: FieldTypeInt64, Indexed: true, Comment: "年龄"},
{Name: "score", Type: FieldTypeFloat, Indexed: false, Comment: "分数"},
})
// 测试数据
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 := NewSchema("test", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "名称"},
{Name: "age", Type: FieldTypeInt64, Indexed: true, Comment: "年龄"},
})
// 正确的数据
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!")
}
func TestExtractIndexValue(t *testing.T) {
schema := NewSchema("test", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "名称"},
{Name: "age", Type: FieldTypeInt64, Indexed: true, Comment: "年龄"},
})
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 := 0; i < 10; i++ {
s1 := NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
{Name: "age", Type: FieldTypeInt64, Indexed: false, Comment: "年龄"},
})
s2 := NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
{Name: "age", Type: FieldTypeInt64, Indexed: false, Comment: "年龄"},
})
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 := NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
{Name: "age", Type: FieldTypeInt64, Indexed: false, Comment: "年龄"},
})
s2 := NewSchema("users", []Field{
{Name: "age", Type: FieldTypeInt64, Indexed: false, Comment: "年龄"},
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
})
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 := NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: true, Comment: "用户名"},
})
s2 := NewSchema("users", []Field{
{Name: "name", Type: FieldTypeString, Indexed: false, Comment: "用户名"}, // Indexed 不同
})
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: FieldTypeInt64, Indexed: true, Comment: "ID"}
fieldB := Field{Name: "name", Type: FieldTypeString, Indexed: false, Comment: "名称"}
fieldC := Field{Name: "age", Type: FieldTypeInt64, Indexed: false, Comment: "年龄"}
fieldD := Field{Name: "email", Type: FieldTypeString, Indexed: true, Comment: "邮箱"}
// 创建不同顺序的 Schema
schemas := []*Schema{
NewSchema("test", []Field{fieldA, fieldB, fieldC, fieldD}), // 原始顺序
NewSchema("test", []Field{fieldD, fieldC, fieldB, fieldA}), // 完全反转
NewSchema("test", []Field{fieldB, fieldD, fieldA, fieldC}), // 随机顺序 1
NewSchema("test", []Field{fieldC, fieldA, fieldD, fieldB}), // 随机顺序 2
NewSchema("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)
}