github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sink/kafka/mock_factory.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package kafka
    15  
    16  import (
    17  	"context"
    18  	"testing"
    19  
    20  	"github.com/IBM/sarama"
    21  	"github.com/IBM/sarama/mocks"
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/tiflow/cdc/model"
    24  	cerror "github.com/pingcap/tiflow/pkg/errors"
    25  	"github.com/pingcap/tiflow/pkg/util"
    26  )
    27  
    28  // MockFactory is a mock implementation of Factory interface.
    29  type MockFactory struct {
    30  	o            *Options
    31  	changefeedID model.ChangeFeedID
    32  }
    33  
    34  // NewMockFactory constructs a Factory with mock implementation.
    35  func NewMockFactory(
    36  	o *Options, changefeedID model.ChangeFeedID,
    37  ) (Factory, error) {
    38  	return &MockFactory{
    39  		o:            o,
    40  		changefeedID: changefeedID,
    41  	}, nil
    42  }
    43  
    44  // AdminClient return a mocked admin client
    45  func (f *MockFactory) AdminClient(_ context.Context) (ClusterAdminClient, error) {
    46  	return NewClusterAdminClientMockImpl(), nil
    47  }
    48  
    49  // SyncProducer creates a sync producer
    50  func (f *MockFactory) SyncProducer(ctx context.Context) (SyncProducer, error) {
    51  	config, err := NewSaramaConfig(ctx, f.o)
    52  	if err != nil {
    53  		return nil, errors.Trace(err)
    54  	}
    55  
    56  	t := ctx.Value("testing.T").(*testing.T)
    57  	syncProducer := mocks.NewSyncProducer(t, config)
    58  	return &MockSaramaSyncProducer{
    59  		Producer: syncProducer,
    60  	}, nil
    61  }
    62  
    63  // AsyncProducer creates an async producer
    64  func (f *MockFactory) AsyncProducer(
    65  	ctx context.Context,
    66  	failpointCh chan error,
    67  ) (AsyncProducer, error) {
    68  	config, err := NewSaramaConfig(ctx, f.o)
    69  	if err != nil {
    70  		return nil, errors.Trace(err)
    71  	}
    72  	t := ctx.Value("testing.T").(*testing.T)
    73  	asyncProducer := mocks.NewAsyncProducer(t, config)
    74  	return &MockSaramaAsyncProducer{
    75  		AsyncProducer: asyncProducer,
    76  		failpointCh:   failpointCh,
    77  	}, nil
    78  }
    79  
    80  // MetricsCollector returns the metric collector
    81  func (f *MockFactory) MetricsCollector(
    82  	_ util.Role, _ ClusterAdminClient,
    83  ) MetricsCollector {
    84  	return &mockMetricsCollector{}
    85  }
    86  
    87  // MockSaramaSyncProducer is a mock implementation of SyncProducer interface.
    88  type MockSaramaSyncProducer struct {
    89  	Producer *mocks.SyncProducer
    90  }
    91  
    92  // SendMessage implement the SyncProducer interface.
    93  func (m *MockSaramaSyncProducer) SendMessage(
    94  	ctx context.Context,
    95  	topic string, partitionNum int32,
    96  	key []byte, value []byte,
    97  ) error {
    98  	_, _, err := m.Producer.SendMessage(&sarama.ProducerMessage{
    99  		Topic:     topic,
   100  		Key:       sarama.ByteEncoder(key),
   101  		Value:     sarama.ByteEncoder(value),
   102  		Partition: partitionNum,
   103  	})
   104  	return err
   105  }
   106  
   107  // SendMessages implement the SyncProducer interface.
   108  func (m *MockSaramaSyncProducer) SendMessages(ctx context.Context,
   109  	topic string, partitionNum int32,
   110  	key []byte, value []byte,
   111  ) error {
   112  	msgs := make([]*sarama.ProducerMessage, partitionNum)
   113  	for i := 0; i < int(partitionNum); i++ {
   114  		msgs[i] = &sarama.ProducerMessage{
   115  			Topic:     topic,
   116  			Key:       sarama.ByteEncoder(key),
   117  			Value:     sarama.ByteEncoder(value),
   118  			Partition: int32(i),
   119  		}
   120  	}
   121  	return m.Producer.SendMessages(msgs)
   122  }
   123  
   124  // Close implement the SyncProducer interface.
   125  func (m *MockSaramaSyncProducer) Close() {
   126  	m.Producer.Close()
   127  }
   128  
   129  // MockSaramaAsyncProducer is a mock implementation of AsyncProducer interface.
   130  type MockSaramaAsyncProducer struct {
   131  	AsyncProducer *mocks.AsyncProducer
   132  	failpointCh   chan error
   133  
   134  	closed bool
   135  }
   136  
   137  // AsyncRunCallback implement the AsyncProducer interface.
   138  func (p *MockSaramaAsyncProducer) AsyncRunCallback(
   139  	ctx context.Context,
   140  ) error {
   141  	for {
   142  		select {
   143  		case <-ctx.Done():
   144  			return errors.Trace(ctx.Err())
   145  		case err := <-p.failpointCh:
   146  			return errors.Trace(err)
   147  		case ack := <-p.AsyncProducer.Successes():
   148  			if ack != nil {
   149  				callback := ack.Metadata.(func())
   150  				if callback != nil {
   151  					callback()
   152  				}
   153  			}
   154  		case err := <-p.AsyncProducer.Errors():
   155  			// We should not wrap a nil pointer if the pointer
   156  			// is of a subtype of `error` because Go would store the type info
   157  			// and the resulted `error` variable would not be nil,
   158  			// which will cause the pkg/error library to malfunction.
   159  			// See: https://go.dev/doc/faq#nil_error
   160  			if err == nil {
   161  				return nil
   162  			}
   163  			return cerror.WrapError(cerror.ErrKafkaAsyncSendMessage, err)
   164  		}
   165  	}
   166  }
   167  
   168  // AsyncSend implement the AsyncProducer interface.
   169  func (p *MockSaramaAsyncProducer) AsyncSend(ctx context.Context, topic string,
   170  	partition int32, key []byte, value []byte,
   171  	callback func(),
   172  ) error {
   173  	msg := &sarama.ProducerMessage{
   174  		Topic:     topic,
   175  		Partition: partition,
   176  		Key:       sarama.StringEncoder(key),
   177  		Value:     sarama.ByteEncoder(value),
   178  		Metadata:  callback,
   179  	}
   180  	select {
   181  	case <-ctx.Done():
   182  		return errors.Trace(ctx.Err())
   183  	case p.AsyncProducer.Input() <- msg:
   184  	}
   185  	return nil
   186  }
   187  
   188  // Close implement the AsyncProducer interface.
   189  func (p *MockSaramaAsyncProducer) Close() {
   190  	if p.closed {
   191  		return
   192  	}
   193  	_ = p.AsyncProducer.Close()
   194  	p.closed = true
   195  }
   196  
   197  type mockMetricsCollector struct{}
   198  
   199  // Run implements the MetricsCollector interface.
   200  func (m *mockMetricsCollector) Run(ctx context.Context) {
   201  }