github.com/matrixorigin/matrixone@v1.2.0/pkg/stream/connector/connector_test.go (about)

     1  // Copyright 2021 Matrix Origin
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package moconnector
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/confluentinc/confluent-kafka-go/v2/kafka"
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    27  	ie "github.com/matrixorigin/matrixone/pkg/util/internalExecutor"
    28  )
    29  
    30  type MockSQLExecutor struct {
    31  	execCount    int
    32  	executedSQLs []string
    33  	wg           *sync.WaitGroup
    34  }
    35  
    36  func (m *MockSQLExecutor) Exec(ctx context.Context, sql string, opts ie.SessionOverrideOptions) error {
    37  	m.execCount++
    38  	m.executedSQLs = append(m.executedSQLs, sql)
    39  	m.wg.Done() // Decrement the WaitGroup counter after processing a message
    40  	return nil
    41  }
    42  
    43  type MysqlResultSet struct {
    44  	//column information
    45  	Columns []string
    46  
    47  	//column name --> column index
    48  	Name2Index map[string]uint64
    49  
    50  	//data
    51  	Data [][]interface{}
    52  }
    53  type internalExecResult struct {
    54  	affectedRows uint64
    55  	resultSet    *MysqlResultSet
    56  	err          error
    57  }
    58  
    59  func (res *internalExecResult) Error() error {
    60  	return res.err
    61  }
    62  
    63  func (res *internalExecResult) ColumnCount() uint64 {
    64  	return 1
    65  }
    66  
    67  func (res *internalExecResult) Column(ctx context.Context, i uint64) (name string, typ uint8, signed bool, err error) {
    68  	return "test", 1, true, nil
    69  }
    70  
    71  func (res *internalExecResult) RowCount() uint64 {
    72  	return 1
    73  }
    74  
    75  func (res *internalExecResult) Row(ctx context.Context, i uint64) ([]interface{}, error) {
    76  	return nil, nil
    77  }
    78  
    79  func (res *internalExecResult) Value(ctx context.Context, ridx uint64, cidx uint64) (interface{}, error) {
    80  	return nil, nil
    81  }
    82  
    83  func (res *internalExecResult) ValueByName(ctx context.Context, ridx uint64, col string) (interface{}, error) {
    84  	return nil, nil
    85  }
    86  
    87  func (res *internalExecResult) StringValueByName(ctx context.Context, ridx uint64, col string) (string, error) {
    88  	return "test", nil
    89  }
    90  
    91  func (res *internalExecResult) Float64ValueByName(ctx context.Context, ridx uint64, col string) (float64, error) {
    92  	return 0, nil
    93  }
    94  func (m *MockSQLExecutor) Query(ctx context.Context, sql string, pts ie.SessionOverrideOptions) ie.InternalExecResult {
    95  	return &internalExecResult{affectedRows: 1, resultSet: nil, err: moerr.NewInternalError(context.TODO(), "random")}
    96  }
    97  
    98  func (m *MockSQLExecutor) ApplySessionOverride(opts ie.SessionOverrideOptions) {}
    99  
   100  func TestKafkaMoConnector(t *testing.T) {
   101  	// Setup mock Kafka cluster
   102  	mockCluster, err := kafka.NewMockCluster(1)
   103  	if err != nil {
   104  		t.Fatalf("Failed to create MockCluster: %s", err)
   105  	}
   106  	defer mockCluster.Close()
   107  
   108  	broker := mockCluster.BootstrapServers()
   109  	topic := "testTopic"
   110  
   111  	var wg sync.WaitGroup
   112  	mockExecutor := &MockSQLExecutor{wg: &wg}
   113  
   114  	// Create KafkaMoConnector instance
   115  	options := map[string]string{
   116  		"type":              "kafka",
   117  		"topic":             topic,
   118  		"database":          "testDB",
   119  		"table":             "testTable",
   120  		"value":             "json",
   121  		"bootstrap.servers": broker,
   122  		"sql":               "select * from testDB.testStream",
   123  	}
   124  	rt := runtime.DefaultRuntime()
   125  	connector, err := NewKafkaMoConnector(rt.Logger().RawLogger(), options, mockExecutor, 1)
   126  	if err != nil {
   127  		t.Fatalf("Failed to create KafkaMoConnector: %s", err)
   128  	}
   129  
   130  	// Produce mock data
   131  	p, err := kafka.NewProducer(&kafka.ConfigMap{"bootstrap.servers": broker})
   132  	if err != nil {
   133  		t.Fatalf("Failed to create producer: %s", err)
   134  	}
   135  	type MessagePayload struct {
   136  		Name string `json:"name"`
   137  		Age  int32  `json:"age"`
   138  	}
   139  	payload := MessagePayload{
   140  		Name: "test_name",
   141  		Age:  100,
   142  	}
   143  	value, _ := json.Marshal(payload)
   144  
   145  	msg_num := 10
   146  
   147  	// produce 10 messages
   148  	for i := 0; i < msg_num; i++ {
   149  		wg.Add(1) // Increment the WaitGroup counter for each message
   150  		p.Produce(&kafka.Message{
   151  			TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
   152  			Value:          value,
   153  		}, nil)
   154  	}
   155  	// Start the connector in a goroutine
   156  	go func() {
   157  		connector.Start(context.Background())
   158  	}()
   159  
   160  	// Create a channel to signal when all messages are processed
   161  	done := make(chan bool)
   162  	go func() {
   163  		wg.Wait()
   164  		done <- true
   165  	}()
   166  
   167  	// Wait for all messages to be processed or timeout
   168  	select {
   169  	case <-done:
   170  		// All messages processed
   171  	case <-time.After(30 * time.Second):
   172  		t.Error("Timed out waiting for messages to be processed")
   173  	}
   174  
   175  	// Stop the connector
   176  	if err := connector.Cancel(); err != nil {
   177  		t.Errorf("Error in Close: %s", err)
   178  	}
   179  
   180  	// Verify that the MockSQLExecutor has executed the correct SQL for 10 times
   181  	if mockExecutor.execCount != msg_num {
   182  		t.Errorf("Expected SQL to be executed 10 times, but got %d", mockExecutor.execCount)
   183  	}
   184  }