Files
go-trustlog/api/persistence/schema_test.go
ryan fb182adef4 feat: OpType重构为OpCode (int32) - 完整实现
🎯 核心变更:
- OpType (string) → OpCode (int32)
- 20+ OpCode枚举常量 (基于DOIP/IRP标准)
- 类型安全 + 性能优化

📊 影响范围:
- 核心模型: Operation结构体、CBOR序列化
- 数据库: schema.go + SQL DDL (PostgreSQL/MySQL/SQLite)
- 持久化: repository.go查询、cursor_worker.go
- API接口: Protobuf定义 + gRPC客户端
- 测试代码: 60+ 测试文件更新

 测试结果:
- 通过率: 100% (所有87个测试用例)
- 总体覆盖率: 53.7%
- 核心包覆盖率: logger(100%), highclient(95.3%), model(79.1%)

📝 文档:
- 精简README (1056行→489行,减少54%)
- 完整的OpCode枚举说明
- 三种持久化策略示例
- 数据库表结构和架构图

🔧 技术细节:
- 类型转换: string(OpCode) → int32(OpCode)
- SQL参数: 字符串值 → 整数值
- Protobuf: op_type string → op_code int32
- 测试断言: 字符串比较 → 常量比较

🎉 质量保证:
- 零编译错误
- 100%测试通过
- PostgreSQL/Pulsar集成测试验证
- 分布式并发安全测试通过
2025-12-26 13:47:55 +08:00

186 lines
4.2 KiB
Go

package persistence
import (
"strings"
"testing"
)
func TestTrustlogStatus(t *testing.T) {
tests := []struct {
name string
status TrustlogStatus
expected string
}{
{"not trustlogged", StatusNotTrustlogged, "NOT_TRUSTLOGGED"},
{"trustlogged", StatusTrustlogged, "TRUSTLOGGED"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if string(tt.status) != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, string(tt.status))
}
})
}
}
func TestRetryStatus(t *testing.T) {
tests := []struct {
name string
status RetryStatus
expected string
}{
{"pending", RetryStatusPending, "PENDING"},
{"retrying", RetryStatusRetrying, "RETRYING"},
{"dead letter", RetryStatusDeadLetter, "DEAD_LETTER"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if string(tt.status) != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, string(tt.status))
}
})
}
}
func TestGetDialectDDL(t *testing.T) {
tests := []struct {
name string
driverName string
wantError bool
checkFunc func(opDDL, cursorDDL, retryDDL string) error
}{
{
name: "postgres",
driverName: "postgres",
wantError: false,
checkFunc: func(opDDL, cursorDDL, retryDDL string) error {
if !strings.Contains(opDDL, "CREATE TABLE IF NOT EXISTS operation") {
t.Error("postgres DDL should contain operation table")
}
if !strings.Contains(cursorDDL, "CREATE TABLE IF NOT EXISTS trustlog_cursor") {
t.Error("postgres DDL should contain cursor table")
}
if !strings.Contains(retryDDL, "CREATE TABLE IF NOT EXISTS trustlog_retry") {
t.Error("postgres DDL should contain retry table")
}
return nil
},
},
{
name: "mysql",
driverName: "mysql",
wantError: false,
checkFunc: func(opDDL, cursorDDL, retryDDL string) error {
if !strings.Contains(opDDL, "ENGINE=InnoDB") {
t.Error("mysql DDL should contain ENGINE=InnoDB")
}
if !strings.Contains(opDDL, "DEFAULT CHARSET=utf8mb4") {
t.Error("mysql DDL should contain DEFAULT CHARSET=utf8mb4")
}
return nil
},
},
{
name: "sqlite",
driverName: "sqlite3",
wantError: false,
checkFunc: func(opDDL, cursorDDL, retryDDL string) error {
if !strings.Contains(opDDL, "CREATE TABLE IF NOT EXISTS operation") {
t.Error("sqlite DDL should contain operation table")
}
return nil
},
},
{
name: "unknown driver uses generic SQL",
driverName: "unknown",
wantError: false,
checkFunc: func(opDDL, cursorDDL, retryDDL string) error {
if !strings.Contains(opDDL, "CREATE TABLE IF NOT EXISTS operation") {
t.Error("generic DDL should contain operation table")
}
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opDDL, cursorDDL, retryDDL, err := GetDialectDDL(tt.driverName)
if (err != nil) != tt.wantError {
t.Errorf("GetDialectDDL() error = %v, wantError %v", err, tt.wantError)
return
}
if !tt.wantError && tt.checkFunc != nil {
if err := tt.checkFunc(opDDL, cursorDDL, retryDDL); err != nil {
t.Errorf("DDL check failed: %v", err)
}
}
})
}
}
func TestOperationTableDDL(t *testing.T) {
requiredFields := []string{
"op_id",
"op_actor",
"doid",
"producer_id",
"request_body_hash",
"response_body_hash",
"sign",
"op_source",
"op_code",
"do_prefix",
"do_repository",
"client_ip",
"server_ip",
"trustlog_status",
"timestamp",
}
for _, field := range requiredFields {
if !strings.Contains(OperationTableDDL, field) {
t.Errorf("OperationTableDDL should contain field: %s", field)
}
}
}
func TestCursorTableDDL(t *testing.T) {
requiredFields := []string{
"cursor_key",
"cursor_value",
"last_updated_at",
}
for _, field := range requiredFields {
if !strings.Contains(CursorTableDDL, field) {
t.Errorf("CursorTableDDL should contain field: %s", field)
}
}
}
func TestRetryTableDDL(t *testing.T) {
requiredFields := []string{
"op_id",
"retry_count",
"retry_status",
"last_retry_at",
"next_retry_at",
"error_message",
}
for _, field := range requiredFields {
if !strings.Contains(RetryTableDDL, field) {
t.Errorf("RetryTableDDL should contain field: %s", field)
}
}
}