Files
srdb/schema_test.go
bourdon 23843493b8 重构代码结构并添加完整功能
主要改动:
- 重构目录结构:合并子目录到根目录,简化项目结构
- 添加完整的查询 API:支持复杂条件查询、字段选择、游标模式
- 实现 LSM-Tree Compaction:7层结构、Score-based策略、后台异步合并
- 添加 Web UI:基于 Lit 的现代化管理界面,支持数据浏览和 Manifest 查看
- 完善文档:添加 README.md 和 examples/webui/README.md

新增功能:
- Query Builder:链式查询 API,支持 Eq/Lt/Gt/In/Between/Contains 等操作符
- Web UI 组件:srdb-app、srdb-table-list、srdb-data-view、srdb-manifest-view 等
- 列选择持久化:自动保存到 localStorage
- 刷新按钮:一键刷新当前视图
- 主题切换:深色/浅色主题支持

代码优化:
- 使用 Go 1.24 新特性:range 7、min()、maps.Copy()、slices.Sort()
- 统一组件命名:所有 Web Components 使用 srdb-* 前缀
- CSS 优化:提取共享样式,减少重复代码
- 清理遗留代码:删除未使用的方法和样式
2025-10-08 23:04:47 +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 := range 10 {
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)
}