refactor: 重构trustlog-sdk目录结构到trustlog/go-trustlog
- 将所有trustlog-sdk文件移动到trustlog/go-trustlog/目录 - 更新README中所有import路径从trustlog-sdk改为go-trustlog - 更新cookiecutter配置文件中的项目名称 - 更新根目录.lefthook.yml以引用新位置的配置 - 添加go.sum文件到版本控制 - 删除过时的示例文件 这次重构与trustlog-server保持一致的目录结构, 为未来支持多语言SDK(Python、Java等)预留空间。
This commit is contained in:
156
api/highclient/client.go
Normal file
156
api/highclient/client.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package highclient
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/adapter"
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/logger"
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/model"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
publisher message.Publisher
|
||||
logger logger.Logger
|
||||
envelopeConfig model.EnvelopeConfig
|
||||
}
|
||||
|
||||
// NewClient 创建HighClient,使用Envelope序列化方式.
|
||||
// publisher可以使用任意(包含forwarder)创建的publisher,但是我们所有的订阅者必须可以处理Envelope格式的消息.
|
||||
// 参数:
|
||||
// - publisher: 消息发布器
|
||||
// - logger: 日志记录器
|
||||
// - envelopeConfig: SM2密钥配置,用于签名和序列化
|
||||
func NewClient(publisher message.Publisher, logger logger.Logger, envelopeConfig model.EnvelopeConfig) *Client {
|
||||
return &Client{
|
||||
publisher: publisher,
|
||||
logger: logger,
|
||||
envelopeConfig: envelopeConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetLow() message.Publisher {
|
||||
return c.publisher
|
||||
}
|
||||
|
||||
func (c *Client) OperationPublish(operation *model.Operation) error {
|
||||
if operation == nil {
|
||||
c.logger.Error("operation publish failed: operation is nil")
|
||||
return errors.New("operation cannot be nil")
|
||||
}
|
||||
|
||||
c.logger.Debug("publishing operation",
|
||||
"opID", operation.OpID,
|
||||
"opType", operation.OpType,
|
||||
"doPrefix", operation.DoPrefix,
|
||||
)
|
||||
|
||||
err := publish(operation, adapter.OperationTopic, c.publisher, c.envelopeConfig, c.logger)
|
||||
if err != nil {
|
||||
c.logger.Error("operation publish failed",
|
||||
"opID", operation.OpID,
|
||||
"error", err,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
c.logger.Info("operation published successfully",
|
||||
"opID", operation.OpID,
|
||||
"opType", operation.OpType,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) RecordPublish(record *model.Record) error {
|
||||
if record == nil {
|
||||
c.logger.Error("record publish failed: record is nil")
|
||||
return errors.New("record cannot be nil")
|
||||
}
|
||||
|
||||
c.logger.Debug("publishing record",
|
||||
"recordID", record.ID,
|
||||
"rcType", record.RCType,
|
||||
"doPrefix", record.DoPrefix,
|
||||
)
|
||||
|
||||
err := publish(record, adapter.RecordTopic, c.publisher, c.envelopeConfig, c.logger)
|
||||
if err != nil {
|
||||
c.logger.Error("record publish failed",
|
||||
"recordID", record.ID,
|
||||
"error", err,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
c.logger.Info("record published successfully",
|
||||
"recordID", record.ID,
|
||||
"rcType", record.RCType,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.logger.Info("closing high client")
|
||||
err := c.publisher.Close()
|
||||
if err != nil {
|
||||
c.logger.Error("failed to close publisher", "error", err)
|
||||
return err
|
||||
}
|
||||
c.logger.Info("high client closed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// publish 通用的发布函数,支持任何实现了 Trustlog 接口的类型。
|
||||
// 使用 Envelope 格式序列化并发布到指定 topic。
|
||||
func publish(
|
||||
data model.Trustlog,
|
||||
topic string,
|
||||
publisher message.Publisher,
|
||||
config model.EnvelopeConfig,
|
||||
logger logger.Logger,
|
||||
) error {
|
||||
messageKey := data.Key()
|
||||
|
||||
logger.Debug("starting envelope serialization",
|
||||
"messageKey", messageKey,
|
||||
"topic", topic,
|
||||
)
|
||||
|
||||
// 使用 Envelope 序列化(MarshalTrustlog 会自动提取 producerID)
|
||||
envelopeData, err := model.MarshalTrustlog(data, config)
|
||||
if err != nil {
|
||||
logger.Error("envelope serialization failed",
|
||||
"messageKey", messageKey,
|
||||
"error", err,
|
||||
)
|
||||
return fmt.Errorf("failed to marshal envelope: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("envelope serialized successfully",
|
||||
"messageKey", messageKey,
|
||||
"envelopeSize", len(envelopeData),
|
||||
)
|
||||
|
||||
msg := message.NewMessage(messageKey, envelopeData)
|
||||
logger.Debug("publishing message to topic",
|
||||
"messageKey", messageKey,
|
||||
"topic", topic,
|
||||
)
|
||||
|
||||
if publishErr := publisher.Publish(topic, msg); publishErr != nil {
|
||||
logger.Error("failed to publish to topic",
|
||||
"messageKey", messageKey,
|
||||
"topic", topic,
|
||||
"error", publishErr,
|
||||
)
|
||||
return fmt.Errorf("failed to publish message to topic %s: %w", topic, publishErr)
|
||||
}
|
||||
|
||||
logger.Debug("message published to topic successfully",
|
||||
"messageKey", messageKey,
|
||||
"topic", topic,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
536
api/highclient/client_test.go
Normal file
536
api/highclient/client_test.go
Normal file
@@ -0,0 +1,536 @@
|
||||
package highclient_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/adapter"
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/highclient"
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/logger"
|
||||
"go.yandata.net/iod/iod/trustlog-sdk/api/model"
|
||||
)
|
||||
|
||||
// MockPublisher 模拟 message.Publisher.
|
||||
type MockPublisher struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockPublisher) Publish(topic string, messages ...*message.Message) error {
|
||||
args := m.Called(topic, messages)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockPublisher) Close() error {
|
||||
args := m.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
// generateTestKeys 生成测试用的SM2密钥对(DER格式).
|
||||
func generateTestKeys(t testing.TB) ([]byte, []byte) {
|
||||
keyPair, err := model.GenerateSM2KeyPair()
|
||||
if err != nil {
|
||||
if t != nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 私钥:DER编码
|
||||
privateKeyDER, err := model.MarshalSM2PrivateDER(keyPair.Private)
|
||||
if err != nil {
|
||||
if t != nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 公钥:DER编码
|
||||
publicKeyDER, err := model.MarshalSM2PublicDER(keyPair.Public)
|
||||
if err != nil {
|
||||
if t != nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return privateKeyDER, publicKeyDER
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
require.NotNil(t, client)
|
||||
assert.Equal(t, mockPublisher, client.GetLow())
|
||||
}
|
||||
|
||||
func TestClient_GetLow(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
lowLevelPublisher := client.GetLow()
|
||||
assert.Equal(t, mockPublisher, lowLevelPublisher)
|
||||
}
|
||||
|
||||
func TestClient_OperationPublish(t *testing.T) { //nolint:dupl // 测试代码中的重复模式是合理的
|
||||
tests := []struct {
|
||||
name string
|
||||
operation *model.Operation
|
||||
setupMock func(*MockPublisher)
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "成功发布Operation",
|
||||
operation: createTestOperation(t),
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).Return(nil).Once()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "发布失败",
|
||||
operation: createTestOperation(t),
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Return(errors.New("publish failed")).
|
||||
Once()
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "publish failed",
|
||||
},
|
||||
{
|
||||
name: "nil Operation应该失败",
|
||||
operation: nil,
|
||||
setupMock: func(_ *MockPublisher) {
|
||||
// nil operation不会调用Publish,因为会在之前失败
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
tt.setupMock(mockPublisher)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
err := client.OperationPublish(tt.operation)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_RecordPublish(t *testing.T) { //nolint:dupl // 测试代码中的重复模式是合理的
|
||||
tests := []struct {
|
||||
name string
|
||||
record *model.Record
|
||||
setupMock func(*MockPublisher)
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "成功发布Record",
|
||||
record: createTestRecord(t),
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).Return(nil).Once()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "发布失败",
|
||||
record: createTestRecord(t),
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Return(errors.New("record publish failed")).
|
||||
Once()
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "record publish failed",
|
||||
},
|
||||
{
|
||||
name: "nil Record应该失败",
|
||||
record: nil,
|
||||
setupMock: func(_ *MockPublisher) {
|
||||
// nil record不会调用Publish,因为会在之前失败
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
tt.setupMock(mockPublisher)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
err := client.RecordPublish(tt.record)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Close(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupMock func(*MockPublisher)
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "成功关闭",
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Close").Return(nil).Once()
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "关闭失败",
|
||||
setupMock: func(mp *MockPublisher) {
|
||||
mp.On("Close").Return(errors.New("close failed")).Once()
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "close failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
tt.setupMock(mockPublisher)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
err := client.Close()
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
if tt.errContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_MessageContent(t *testing.T) {
|
||||
// 测试发布的消息内容是否正确
|
||||
t.Run("Operation消息内容验证", func(t *testing.T) { //nolint:dupl // 测试代码中的重复模式是合理的
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
operation := createTestOperation(t)
|
||||
|
||||
// 捕获发布的消息
|
||||
var capturedMessages []*message.Message
|
||||
mockPublisher.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Run(func(args mock.Arguments) {
|
||||
messages, ok := args.Get(1).([]*message.Message)
|
||||
if ok {
|
||||
capturedMessages = messages
|
||||
}
|
||||
}).Return(nil).Once()
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
err := client.OperationPublish(operation)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 验证消息内容
|
||||
require.Len(t, capturedMessages, 1)
|
||||
msg := capturedMessages[0]
|
||||
assert.Equal(t, operation.Key(), msg.UUID)
|
||||
assert.NotEmpty(t, msg.Payload)
|
||||
|
||||
// 验证是Envelope格式,可以反序列化
|
||||
unmarshaledOp, err := model.UnmarshalOperation(msg.Payload)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, operation.OpID, unmarshaledOp.OpID)
|
||||
|
||||
// 验证签名
|
||||
verifyConfig := model.NewSM2VerifyConfig(publicKey)
|
||||
verifiedEnv, err := model.VerifyEnvelopeWithConfig(msg.Payload, verifyConfig)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, verifiedEnv)
|
||||
})
|
||||
|
||||
t.Run("Record消息内容验证", func(t *testing.T) { //nolint:dupl // 测试代码中的重复模式是合理的
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
record := createTestRecord(t)
|
||||
|
||||
// 捕获发布的消息
|
||||
var capturedMessages []*message.Message
|
||||
mockPublisher.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Run(func(args mock.Arguments) {
|
||||
messages, ok := args.Get(1).([]*message.Message)
|
||||
if ok {
|
||||
capturedMessages = messages
|
||||
}
|
||||
}).Return(nil).Once()
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
err := client.RecordPublish(record)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 验证消息内容
|
||||
require.Len(t, capturedMessages, 1)
|
||||
msg := capturedMessages[0]
|
||||
assert.Equal(t, record.Key(), msg.UUID)
|
||||
assert.NotEmpty(t, msg.Payload)
|
||||
|
||||
// 验证是Envelope格式,可以反序列化
|
||||
unmarshaledRecord, err := model.UnmarshalRecord(msg.Payload)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, record.ID, unmarshaledRecord.ID)
|
||||
|
||||
// 验证签名
|
||||
verifyConfig := model.NewSM2VerifyConfig(publicKey)
|
||||
verifiedEnv, err := model.VerifyEnvelopeWithConfig(msg.Payload, verifyConfig)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, verifiedEnv)
|
||||
})
|
||||
}
|
||||
|
||||
func TestClient_ConcurrentPublish(t *testing.T) {
|
||||
// 测试并发发布
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
|
||||
// 设置期望的调用次数
|
||||
publishCount := 100
|
||||
mockPublisher.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Return(nil).Times(publishCount)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
// 并发发布
|
||||
errChan := make(chan error, publishCount)
|
||||
for i := range publishCount {
|
||||
go func(id int) {
|
||||
//nolint:testifylint // 在goroutine中创建测试数据,使用panic处理错误
|
||||
operation := createTestOperationWithID(nil, fmt.Sprintf("concurrent-test-%d", id))
|
||||
errChan <- client.OperationPublish(operation)
|
||||
}(i)
|
||||
}
|
||||
|
||||
// 收集结果
|
||||
for range publishCount {
|
||||
err := <-errChan
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestClient_EdgeCases(t *testing.T) {
|
||||
t.Run("发布大型Operation", func(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
mockPublisher.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).
|
||||
Return(nil).
|
||||
Once()
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
// 创建包含大量数据的Operation
|
||||
operation := createTestOperation(t)
|
||||
operation.OpActor = string(make([]byte, 1000)) // 1KB数据
|
||||
|
||||
err := client.OperationPublish(operation)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("发布大型Record", func(t *testing.T) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
mockPublisher.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).Return(nil).Once()
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
// 创建包含大量数据的Record
|
||||
record := createTestRecord(t)
|
||||
record.WithExtra(make([]byte, 500)) // 500字节的额外数据
|
||||
|
||||
err := client.RecordPublish(record)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestClient_Integration(t *testing.T) {
|
||||
// 集成测试 - 测试完整的工作流程
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(t)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
|
||||
// 设置期望:发布Operation -> 发布Record -> 关闭
|
||||
mockPublisher.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).Return(nil).Once()
|
||||
mockPublisher.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).Return(nil).Once()
|
||||
mockPublisher.On("Close").Return(nil).Once()
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
|
||||
// 发布Operation
|
||||
operation := createTestOperation(t)
|
||||
err := client.OperationPublish(operation)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 发布Record
|
||||
record := createTestRecord(t)
|
||||
err = client.RecordPublish(record)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 关闭客户端
|
||||
err = client.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
mockPublisher.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// 性能基准测试.
|
||||
func BenchmarkClient_OperationPublish(b *testing.B) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(b)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
mockPublisher.On("Publish", adapter.OperationTopic, mock.AnythingOfType("[]*message.Message")).Return(nil)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
operation := createTestOperation(b)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
err := client.OperationPublish(operation)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkClient_RecordPublish(b *testing.B) {
|
||||
mockPublisher := &MockPublisher{}
|
||||
testLogger := logger.NewLogger(logr.Discard())
|
||||
privateKey, publicKey := generateTestKeys(b)
|
||||
config := model.NewSM2EnvelopeConfig(privateKey, publicKey)
|
||||
mockPublisher.On("Publish", adapter.RecordTopic, mock.AnythingOfType("[]*message.Message")).Return(nil)
|
||||
|
||||
client := highclient.NewClient(mockPublisher, testLogger, config)
|
||||
record := createTestRecord(b)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
err := client.RecordPublish(record)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 测试辅助函数.
|
||||
func createTestOperation(t testing.TB) *model.Operation {
|
||||
return createTestOperationWithID(t, "test-operation-001")
|
||||
}
|
||||
|
||||
func createTestOperationWithID(t testing.TB, id string) *model.Operation {
|
||||
// 在并发测试中,t可能为nil,这是正常的
|
||||
errorHandler := func(err error) {
|
||||
if t != nil {
|
||||
require.NoError(t, err)
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
operation, err := model.NewFullOperation(
|
||||
model.OpSourceDOIP,
|
||||
model.OpTypeRetrieve,
|
||||
"test-prefix",
|
||||
"test-repo",
|
||||
"test-prefix/test-repo/test-object",
|
||||
"test-producer-id",
|
||||
"test-actor",
|
||||
"test request body",
|
||||
"test response body",
|
||||
time.Now(),
|
||||
)
|
||||
errorHandler(err)
|
||||
operation.OpID = id // 设置自定义ID
|
||||
return operation
|
||||
}
|
||||
|
||||
func createTestRecord(t testing.TB) *model.Record {
|
||||
record, err := model.NewFullRecord(
|
||||
"test-prefix",
|
||||
"test-producer-id",
|
||||
time.Now(),
|
||||
"test-operator",
|
||||
[]byte("test extra data"),
|
||||
"test-type",
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return record
|
||||
}
|
||||
Reference in New Issue
Block a user