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 }