- 将所有trustlog-sdk文件移动到trustlog/go-trustlog/目录 - 更新README中所有import路径从trustlog-sdk改为go-trustlog - 更新cookiecutter配置文件中的项目名称 - 更新根目录.lefthook.yml以引用新位置的配置 - 添加go.sum文件到版本控制 - 删除过时的示例文件 这次重构与trustlog-server保持一致的目录结构, 为未来支持多语言SDK(Python、Java等)预留空间。
609 lines
13 KiB
Go
609 lines
13 KiB
Go
package mocks
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/apache/pulsar-client-go/pulsar"
|
|
)
|
|
|
|
// MockPulsarClient is a mock implementation of pulsar.Client.
|
|
type MockPulsarClient struct {
|
|
mu sync.RWMutex
|
|
producers map[string]*MockProducer
|
|
consumers map[string]*MockConsumer
|
|
closed bool
|
|
}
|
|
|
|
// NewMockPulsarClient creates a new mock Pulsar client.
|
|
func NewMockPulsarClient() *MockPulsarClient {
|
|
return &MockPulsarClient{
|
|
producers: make(map[string]*MockProducer),
|
|
consumers: make(map[string]*MockConsumer),
|
|
}
|
|
}
|
|
|
|
// CreateProducer creates a mock producer.
|
|
func (m *MockPulsarClient) CreateProducer(options pulsar.ProducerOptions) (pulsar.Producer, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return nil, errors.New("client is closed")
|
|
}
|
|
|
|
if m.producers == nil {
|
|
m.producers = make(map[string]*MockProducer)
|
|
}
|
|
|
|
producer := NewMockProducer(options.Topic)
|
|
m.producers[options.Topic] = producer
|
|
return producer, nil
|
|
}
|
|
|
|
// Subscribe creates a mock consumer.
|
|
func (m *MockPulsarClient) Subscribe(options pulsar.ConsumerOptions) (pulsar.Consumer, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return nil, errors.New("client is closed")
|
|
}
|
|
|
|
if m.consumers == nil {
|
|
m.consumers = make(map[string]*MockConsumer)
|
|
}
|
|
|
|
consumer := NewMockConsumer(options.Topic, options.Name)
|
|
m.consumers[options.Name] = consumer
|
|
return consumer, nil
|
|
}
|
|
|
|
// CreateReader is not implemented.
|
|
func (m *MockPulsarClient) CreateReader(options pulsar.ReaderOptions) (pulsar.Reader, error) {
|
|
return nil, errors.New("CreateReader not implemented")
|
|
}
|
|
|
|
// CreateTableView is not implemented.
|
|
func (m *MockPulsarClient) CreateTableView(options pulsar.TableViewOptions) (pulsar.TableView, error) {
|
|
return nil, errors.New("CreateTableView not implemented")
|
|
}
|
|
|
|
// NewTransaction creates a new transaction.
|
|
func (m *MockPulsarClient) NewTransaction(timeout time.Duration) (pulsar.Transaction, error) {
|
|
return nil, errors.New("not implemented")
|
|
}
|
|
|
|
// TopicPartitions returns the partitions for a topic.
|
|
func (m *MockPulsarClient) TopicPartitions(topic string) ([]string, error) {
|
|
return []string{topic}, nil
|
|
}
|
|
|
|
// Close closes the mock client.
|
|
func (m *MockPulsarClient) Close() {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
m.closed = true
|
|
for _, producer := range m.producers {
|
|
producer.Close()
|
|
}
|
|
for _, consumer := range m.consumers {
|
|
consumer.Close()
|
|
}
|
|
}
|
|
|
|
// GetProducer returns a producer by topic (for testing).
|
|
func (m *MockPulsarClient) GetProducer(topic string) *MockProducer {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
return m.producers[topic]
|
|
}
|
|
|
|
// GetConsumer returns a consumer by name (for testing).
|
|
func (m *MockPulsarClient) GetConsumer(name string) *MockConsumer {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
return m.consumers[name]
|
|
}
|
|
|
|
// MockProducer is a mock implementation of pulsar.Producer.
|
|
type MockProducer struct {
|
|
topic string
|
|
name string
|
|
messages []*pulsar.ProducerMessage
|
|
mu sync.RWMutex
|
|
closed bool
|
|
}
|
|
|
|
// NewMockProducer creates a new mock producer.
|
|
func NewMockProducer(topic string) *MockProducer {
|
|
return &MockProducer{
|
|
topic: topic,
|
|
name: "mock-producer",
|
|
messages: make([]*pulsar.ProducerMessage, 0),
|
|
}
|
|
}
|
|
|
|
// Topic returns the topic name.
|
|
func (m *MockProducer) Topic() string {
|
|
return m.topic
|
|
}
|
|
|
|
// Name returns the producer name.
|
|
func (m *MockProducer) Name() string {
|
|
return m.name
|
|
}
|
|
|
|
// Send sends a message.
|
|
func (m *MockProducer) Send(ctx context.Context, msg *pulsar.ProducerMessage) (pulsar.MessageID, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return nil, errors.New("producer is closed")
|
|
}
|
|
|
|
m.messages = append(m.messages, msg)
|
|
return &MockMessageID{id: len(m.messages)}, nil
|
|
}
|
|
|
|
// SendAsync sends a message asynchronously.
|
|
func (m *MockProducer) SendAsync(
|
|
ctx context.Context,
|
|
msg *pulsar.ProducerMessage,
|
|
callback func(pulsar.MessageID, *pulsar.ProducerMessage, error),
|
|
) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
callback(nil, msg, errors.New("producer is closed"))
|
|
return
|
|
}
|
|
|
|
m.messages = append(m.messages, msg)
|
|
callback(&MockMessageID{id: len(m.messages)}, msg, nil)
|
|
}
|
|
|
|
// LastSequenceID returns the last sequence ID.
|
|
func (m *MockProducer) LastSequenceID() int64 {
|
|
return 0
|
|
}
|
|
|
|
// Flush flushes pending messages.
|
|
func (m *MockProducer) Flush() error {
|
|
return nil
|
|
}
|
|
|
|
// FlushWithCtx flushes pending messages with context.
|
|
func (m *MockProducer) FlushWithCtx(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// Close closes the producer.
|
|
func (m *MockProducer) Close() {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
m.closed = true
|
|
}
|
|
|
|
// GetMessages returns all sent messages (for testing).
|
|
func (m *MockProducer) GetMessages() []*pulsar.ProducerMessage {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
result := make([]*pulsar.ProducerMessage, len(m.messages))
|
|
copy(result, m.messages)
|
|
return result
|
|
}
|
|
|
|
// MockConsumer is a mock implementation of pulsar.Consumer.
|
|
type MockConsumer struct {
|
|
topic string
|
|
name string
|
|
messageChan chan pulsar.ConsumerMessage
|
|
mu sync.RWMutex
|
|
closed bool
|
|
}
|
|
|
|
const (
|
|
// defaultMessageChannelSize 定义消息通道的默认缓冲大小.
|
|
defaultMessageChannelSize = 10
|
|
)
|
|
|
|
// NewMockConsumer creates a new mock consumer.
|
|
func NewMockConsumer(topic, name string) *MockConsumer {
|
|
return &MockConsumer{
|
|
topic: topic,
|
|
name: name,
|
|
messageChan: make(chan pulsar.ConsumerMessage, defaultMessageChannelSize),
|
|
}
|
|
}
|
|
|
|
// Subscription returns the subscription name.
|
|
func (m *MockConsumer) Subscription() string {
|
|
return m.name
|
|
}
|
|
|
|
// Topic returns the topic name.
|
|
func (m *MockConsumer) Topic() string {
|
|
return m.topic
|
|
}
|
|
|
|
// Chan returns the message channel.
|
|
func (m *MockConsumer) Chan() <-chan pulsar.ConsumerMessage {
|
|
return m.messageChan
|
|
}
|
|
|
|
// Ack acknowledges a message.
|
|
func (m *MockConsumer) Ack(msg pulsar.Message) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Nack negatively acknowledges a message.
|
|
func (m *MockConsumer) Nack(msg pulsar.Message) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
// Mock implementation: 实际不做任何操作
|
|
_ = msg
|
|
}
|
|
|
|
// NackID negatively acknowledges a message by ID.
|
|
func (m *MockConsumer) NackID(msgID pulsar.MessageID) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
// Mock implementation: 实际不做任何操作
|
|
_ = msgID
|
|
}
|
|
|
|
// Unsubscribe unsubscribes the consumer.
|
|
func (m *MockConsumer) Unsubscribe() error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnsubscribeForce forcefully unsubscribes the consumer.
|
|
func (m *MockConsumer) UnsubscribeForce() error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Receive receives a single message.
|
|
func (m *MockConsumer) Receive(ctx context.Context) (pulsar.Message, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return nil, errors.New("consumer is closed")
|
|
}
|
|
|
|
select {
|
|
case msg := <-m.messageChan:
|
|
return msg.Message, nil
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
}
|
|
|
|
// AckCumulative acknowledges all messages up to and including the provided message.
|
|
func (m *MockConsumer) AckCumulative(msg pulsar.Message) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AckID acknowledges a message by ID.
|
|
func (m *MockConsumer) AckID(msgID pulsar.MessageID) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AckIDCumulative acknowledges all messages up to and including the provided message ID.
|
|
func (m *MockConsumer) AckIDCumulative(msgID pulsar.MessageID) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AckIDList acknowledges a list of message IDs.
|
|
func (m *MockConsumer) AckIDList(msgIDs []pulsar.MessageID) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AckWithTxn acknowledges a message with transaction.
|
|
func (m *MockConsumer) AckWithTxn(msg pulsar.Message, txn pulsar.Transaction) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetLastMessageIDs returns the last message IDs.
|
|
func (m *MockConsumer) GetLastMessageIDs() ([]pulsar.TopicMessageID, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return nil, errors.New("consumer is closed")
|
|
}
|
|
return []pulsar.TopicMessageID{}, nil
|
|
}
|
|
|
|
// ReconsumeLater reconsumes a message later with delay.
|
|
func (m *MockConsumer) ReconsumeLater(msg pulsar.Message, delay time.Duration) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
// Mock implementation: 实际不做任何操作
|
|
_, _ = msg, delay
|
|
}
|
|
|
|
// ReconsumeLaterWithCustomProperties reconsumes a message later with custom properties.
|
|
func (m *MockConsumer) ReconsumeLaterWithCustomProperties(
|
|
msg pulsar.Message,
|
|
customProperties map[string]string,
|
|
delay time.Duration,
|
|
) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
// Mock implementation: 实际不做任何操作
|
|
_, _, _ = msg, customProperties, delay
|
|
}
|
|
|
|
// Seek seeks to a message ID.
|
|
func (m *MockConsumer) Seek(msgID pulsar.MessageID) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SeekByTime seeks to a time.
|
|
func (m *MockConsumer) SeekByTime(t time.Time) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Name returns the consumer name.
|
|
func (m *MockConsumer) Name() string {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
return m.name
|
|
}
|
|
|
|
// Close closes the consumer.
|
|
func (m *MockConsumer) Close() {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return
|
|
}
|
|
m.closed = true
|
|
close(m.messageChan)
|
|
}
|
|
|
|
// SendMessage sends a message to the consumer channel (for testing).
|
|
func (m *MockConsumer) SendMessage(msg pulsar.ConsumerMessage) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.closed {
|
|
return errors.New("consumer is closed")
|
|
}
|
|
|
|
select {
|
|
case m.messageChan <- msg:
|
|
return nil
|
|
default:
|
|
return errors.New("channel full")
|
|
}
|
|
}
|
|
|
|
// MockMessageID is a mock implementation of pulsar.MessageID.
|
|
type MockMessageID struct {
|
|
id int
|
|
}
|
|
|
|
// Serialize serializes the message ID.
|
|
func (m *MockMessageID) Serialize() []byte {
|
|
return []byte{byte(m.id)}
|
|
}
|
|
|
|
// BatchIdx returns the batch index.
|
|
func (m *MockMessageID) BatchIdx() int32 {
|
|
return 0
|
|
}
|
|
|
|
// BatchSize returns the batch size.
|
|
func (m *MockMessageID) BatchSize() int32 {
|
|
return 1
|
|
}
|
|
|
|
// String returns the string representation of the message ID.
|
|
func (m *MockMessageID) String() string {
|
|
return fmt.Sprintf("mock-message-id-%d", m.id)
|
|
}
|
|
|
|
// EntryID returns the entry ID.
|
|
func (m *MockMessageID) EntryID() int64 {
|
|
return int64(m.id)
|
|
}
|
|
|
|
// LedgerID returns the ledger ID.
|
|
func (m *MockMessageID) LedgerID() int64 {
|
|
return int64(m.id)
|
|
}
|
|
|
|
// PartitionIdx returns the partition index.
|
|
func (m *MockMessageID) PartitionIdx() int32 {
|
|
return 0
|
|
}
|
|
|
|
// MockMessage is a mock implementation of pulsar.Message.
|
|
type MockMessage struct {
|
|
key string
|
|
payload []byte
|
|
id pulsar.MessageID
|
|
}
|
|
|
|
// NewMockMessage creates a new mock message.
|
|
func NewMockMessage(key string, payload []byte) *MockMessage {
|
|
return &MockMessage{
|
|
key: key,
|
|
payload: payload,
|
|
id: &MockMessageID{id: 1},
|
|
}
|
|
}
|
|
|
|
// Topic returns the topic name.
|
|
func (m *MockMessage) Topic() string {
|
|
return "mock-topic"
|
|
}
|
|
|
|
// Properties returns message properties.
|
|
func (m *MockMessage) Properties() map[string]string {
|
|
return make(map[string]string)
|
|
}
|
|
|
|
// Payload returns the message payload.
|
|
func (m *MockMessage) Payload() []byte {
|
|
return m.payload
|
|
}
|
|
|
|
// ID returns the message ID.
|
|
func (m *MockMessage) ID() pulsar.MessageID {
|
|
return m.id
|
|
}
|
|
|
|
// PublishTime returns the publish time.
|
|
func (m *MockMessage) PublishTime() time.Time {
|
|
return time.Now()
|
|
}
|
|
|
|
// EventTime returns the event time.
|
|
func (m *MockMessage) EventTime() time.Time {
|
|
return time.Time{}
|
|
}
|
|
|
|
// Key returns the message key.
|
|
func (m *MockMessage) Key() string {
|
|
return m.key
|
|
}
|
|
|
|
// OrderingKey returns the ordering key.
|
|
func (m *MockMessage) OrderingKey() string {
|
|
return ""
|
|
}
|
|
|
|
// RedeliveryCount returns the redelivery count.
|
|
func (m *MockMessage) RedeliveryCount() uint32 {
|
|
return 0
|
|
}
|
|
|
|
// IsReplicated returns whether the message is replicated.
|
|
func (m *MockMessage) IsReplicated() bool {
|
|
return false
|
|
}
|
|
|
|
// GetReplicatedFrom returns the replication source.
|
|
func (m *MockMessage) GetReplicatedFrom() string {
|
|
return ""
|
|
}
|
|
|
|
// GetSchemaValue returns the schema value.
|
|
func (m *MockMessage) GetSchemaValue(v interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
// GetEncryptionContext returns the encryption context.
|
|
func (m *MockMessage) GetEncryptionContext() *pulsar.EncryptionContext {
|
|
return nil
|
|
}
|
|
|
|
// Index returns the message index.
|
|
func (m *MockMessage) Index() *uint64 {
|
|
return nil
|
|
}
|
|
|
|
// BrokerPublishTime returns the broker publish time.
|
|
func (m *MockMessage) BrokerPublishTime() *time.Time {
|
|
return nil
|
|
}
|
|
|
|
// ProducerName returns the producer name.
|
|
func (m *MockMessage) ProducerName() string {
|
|
return "mock-producer"
|
|
}
|
|
|
|
// SchemaVersion returns the schema version.
|
|
func (m *MockMessage) SchemaVersion() []byte {
|
|
return nil
|
|
}
|
|
|
|
// ReplicatedFrom returns the replication source.
|
|
func (m *MockMessage) ReplicatedFrom() string {
|
|
return ""
|
|
}
|
|
|
|
// NewMockConsumerMessage creates a new mock consumer message.
|
|
func NewMockConsumerMessage(key string, payload []byte) pulsar.ConsumerMessage {
|
|
return pulsar.ConsumerMessage{
|
|
Message: NewMockMessage(key, payload),
|
|
Consumer: nil,
|
|
}
|
|
}
|