Files
go-trustlog/api/adapter/tcp_integration_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

231 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package adapter
import (
"context"
"sync"
"testing"
"time"
"github.com/ThreeDotsLabs/watermill/message"
)
// 简单的测试日志适配器
type testLogger struct{}
func (t *testLogger) InfoContext(ctx context.Context, msg string, args ...interface{}) {}
func (t *testLogger) DebugContext(ctx context.Context, msg string, args ...interface{}) {}
func (t *testLogger) WarnContext(ctx context.Context, msg string, args ...interface{}) {}
func (t *testLogger) ErrorContext(ctx context.Context, msg string, args ...interface{}) {}
func (t *testLogger) Info(msg string, args ...interface{}) {}
func (t *testLogger) Debug(msg string, args ...interface{}) {}
func (t *testLogger) Warn(msg string, args ...interface{}) {}
func (t *testLogger) Error(msg string, args ...interface{}) {}
func TestTCPPublisherSubscriber_Integration(t *testing.T) {
testLogger := &testLogger{}
// 创建 Subscriber
subscriberConfig := TCPSubscriberConfig{
ListenAddr: "127.0.0.1:18080",
}
subscriber, err := NewTCPSubscriber(subscriberConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create subscriber: %v", err)
}
defer subscriber.Close()
// 等待服务器启动
time.Sleep(100 * time.Millisecond)
// 订阅 topic
ctx := context.Background()
topic := "test-topic"
msgChan, err := subscriber.Subscribe(ctx, topic)
if err != nil {
t.Fatalf("Failed to subscribe: %v", err)
}
// 创建 Publisher
publisherConfig := TCPPublisherConfig{
ServerAddr: "127.0.0.1:18080",
ConnectTimeout: 5 * time.Second,
}
publisher, err := NewTCPPublisher(publisherConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create publisher: %v", err)
}
defer publisher.Close()
// 测试发送和接收消息
testPayload := []byte("Hello, TCP Watermill!")
testMsg := message.NewMessage("test-msg-1", testPayload)
// 启动接收协程
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
select {
case receivedMsg := <-msgChan:
if string(receivedMsg.Payload) != string(testPayload) {
t.Errorf("Payload mismatch: got %s, want %s", receivedMsg.Payload, testPayload)
}
if receivedMsg.UUID != testMsg.UUID {
t.Errorf("UUID mismatch: got %s, want %s", receivedMsg.UUID, testMsg.UUID)
}
// ACK 消息
receivedMsg.Ack()
case <-time.After(5 * time.Second):
t.Error("Timeout waiting for message")
}
}()
// 发送消息
err = publisher.Publish(topic, testMsg)
if err != nil {
t.Fatalf("Failed to publish message: %v", err)
}
// 等待接收完成
wg.Wait()
}
func TestTCPPublisherSubscriber_MultipleMessages(t *testing.T) {
testLogger := &testLogger{}
// 创建 Subscriber
subscriberConfig := TCPSubscriberConfig{
ListenAddr: "127.0.0.1:18081",
}
subscriber, err := NewTCPSubscriber(subscriberConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create subscriber: %v", err)
}
defer subscriber.Close()
time.Sleep(100 * time.Millisecond)
// 订阅
ctx := context.Background()
topic := "test-topic-multi"
msgChan, err := subscriber.Subscribe(ctx, topic)
if err != nil {
t.Fatalf("Failed to subscribe: %v", err)
}
// 创建 Publisher
publisherConfig := TCPPublisherConfig{
ServerAddr: "127.0.0.1:18081",
ConnectTimeout: 5 * time.Second,
}
publisher, err := NewTCPPublisher(publisherConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create publisher: %v", err)
}
defer publisher.Close()
// 准备多条消息
messageCount := 10
messages := make([]*message.Message, messageCount)
for i := 0; i < messageCount; i++ {
payload := []byte("Message " + string(rune('0'+i)))
messages[i] = message.NewMessage("msg-"+string(rune('0'+i)), payload)
}
// 启动接收协程
receivedCount := 0
var mu sync.Mutex
var wg sync.WaitGroup
for i := 0; i < messageCount; i++ {
wg.Add(1)
go func() {
defer wg.Done()
select {
case receivedMsg := <-msgChan:
mu.Lock()
receivedCount++
mu.Unlock()
receivedMsg.Ack()
case <-time.After(10 * time.Second):
t.Error("Timeout waiting for message")
}
}()
}
// 发送消息(并发发送)
err = publisher.Publish(topic, messages...)
if err != nil {
t.Fatalf("Failed to publish messages: %v", err)
}
// 等待接收完成
wg.Wait()
if receivedCount != messageCount {
t.Errorf("Received count mismatch: got %d, want %d", receivedCount, messageCount)
}
}
func TestTCPPublisherSubscriber_Nack(t *testing.T) {
testLogger := &testLogger{}
// 创建 Subscriber
subscriberConfig := TCPSubscriberConfig{
ListenAddr: "127.0.0.1:18082",
}
subscriber, err := NewTCPSubscriber(subscriberConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create subscriber: %v", err)
}
defer subscriber.Close()
time.Sleep(100 * time.Millisecond)
// 订阅
ctx := context.Background()
topic := "test-topic-nack"
msgChan, err := subscriber.Subscribe(ctx, topic)
if err != nil {
t.Fatalf("Failed to subscribe: %v", err)
}
// 创建 Publisher
publisherConfig := TCPPublisherConfig{
ServerAddr: "127.0.0.1:18082",
ConnectTimeout: 5 * time.Second,
}
publisher, err := NewTCPPublisher(publisherConfig, testLogger)
if err != nil {
t.Fatalf("Failed to create publisher: %v", err)
}
defer publisher.Close()
// 准备消息
testMsg := message.NewMessage("nack-test", []byte("This will be nacked"))
// 启动接收协程,这次 NACK
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
select {
case receivedMsg := <-msgChan:
// NACK 消息
receivedMsg.Nack()
case <-time.After(5 * time.Second):
t.Error("Timeout waiting for message")
}
}()
// 发送消息由于不等待ACK应该立即返回成功
// 注意即使消费者NACK发布者也会返回成功
err = publisher.Publish(topic, testMsg)
if err != nil {
t.Errorf("Expected no error (fire-and-forget), got: %v", err)
}
wg.Wait()
}