github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/tests/integration/topic_helpers_test.go (about) 1 //go:build integration && go1.23 2 // +build integration,go1.23 3 4 package integration 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "io" 11 "os" 12 "path" 13 "strings" 14 "testing" 15 16 "github.com/stretchr/testify/require" 17 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/version" 19 "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicoptions" 20 "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicsugar" 21 "github.com/ydb-platform/ydb-go-sdk/v3/topic/topicwriter" 22 ) 23 24 func TestMessageReaderIterator(t *testing.T) { 25 scope := newScope(t) 26 ctx := scope.Ctx 27 28 err := scope.TopicWriter().Write(ctx, 29 topicwriter.Message{Data: strings.NewReader("asd")}, 30 topicwriter.Message{Data: strings.NewReader("ddd")}, 31 topicwriter.Message{Data: strings.NewReader("ggg")}, 32 ) 33 require.NoError(t, err) 34 35 var results []string 36 for mess, err := range topicsugar.TopicMessageIterator(ctx, scope.TopicReader()) { 37 require.NoError(t, err) 38 content, err := io.ReadAll(mess) 39 require.NoError(t, err) 40 41 results = append(results, string(content)) 42 if len(results) == 3 { 43 break 44 } 45 } 46 require.Equal(t, []string{"asd", "ddd", "ggg"}, results) 47 } 48 49 func TestStringReaderIterator(t *testing.T) { 50 scope := newScope(t) 51 ctx := scope.Ctx 52 53 err := scope.TopicWriter().Write(ctx, 54 topicwriter.Message{Data: strings.NewReader("asd")}, 55 topicwriter.Message{Data: strings.NewReader("ddd")}, 56 topicwriter.Message{Data: strings.NewReader("ggg")}, 57 ) 58 require.NoError(t, err) 59 60 var results []string 61 for mess, err := range topicsugar.StringIterator(ctx, scope.TopicReader()) { 62 require.NoError(t, err) 63 64 results = append(results, mess.Data) 65 if len(results) == 3 { 66 break 67 } 68 } 69 require.Equal(t, []string{"asd", "ddd", "ggg"}, results) 70 } 71 72 func TestMessageJsonUnmarshalIterator(t *testing.T) { 73 scope := newScope(t) 74 ctx := scope.Ctx 75 76 marshal := func(d any) io.Reader { 77 content, err := json.Marshal(d) 78 require.NoError(t, err) 79 return bytes.NewReader(content) 80 } 81 82 type testStruct struct { 83 A int 84 B string 85 } 86 87 err := scope.TopicWriter().Write(ctx, 88 topicwriter.Message{Data: marshal(testStruct{A: 1, B: "asd"})}, 89 topicwriter.Message{Data: marshal(testStruct{A: 2, B: "fff"})}, 90 topicwriter.Message{Data: marshal(testStruct{A: 5, B: "qwe"})}, 91 ) 92 require.NoError(t, err) 93 94 var results []testStruct 95 expectedSeqno := int64(1) 96 expectedOffset := int64(0) 97 for mess, err := range topicsugar.JSONIterator[testStruct](ctx, scope.TopicReader()) { 98 require.NoError(t, err) 99 require.Equal(t, expectedSeqno, mess.SeqNo) 100 require.Equal(t, expectedOffset, mess.Offset) 101 102 results = append(results, mess.Data) 103 if len(results) == 3 { 104 break 105 } 106 expectedSeqno++ 107 expectedOffset++ 108 } 109 110 expectedResult := []testStruct{ 111 {A: 1, B: "asd"}, 112 {A: 2, B: "fff"}, 113 {A: 5, B: "qwe"}, 114 } 115 require.Equal(t, expectedResult, results) 116 } 117 118 func TestCDCReaderIterator(t *testing.T) { 119 if os.Getenv("YDB_VERSION") != "nightly" && version.Lt(os.Getenv("YDB_VERSION"), "24.1") { 120 t.Skip("require minimum version 24.1 for work with within yql") 121 } 122 scope := newScope(t) 123 ctx := scope.Ctx 124 125 query := fmt.Sprintf(` 126 PRAGMA TablePathPrefix("%s"); 127 128 ALTER TABLE %s 129 ADD CHANGEFEED cdc WITH ( 130 FORMAT='JSON', 131 MODE='NEW_AND_OLD_IMAGES' 132 ) 133 `, scope.Folder(), scope.TableName()) 134 135 _, err := scope.Driver().Scripting().Execute(ctx, query, nil) 136 require.NoError(t, err) 137 138 query = fmt.Sprintf(` 139 PRAGMA TablePathPrefix("%s"); 140 141 ALTER TOPIC %s 142 ADD CONSUMER %s; 143 `, scope.Folder(), "`"+scope.TableName()+"/cdc`", "`"+scope.TopicConsumerName()+"`") 144 145 _, err = scope.Driver().Scripting().Execute(ctx, query, nil) 146 require.NoError(t, err) 147 148 require.Equal(t, "table", scope.TableName()) 149 150 prefix := fmt.Sprintf(`PRAGMA TablePathPrefix("%s");`, scope.Folder()) 151 152 err = scope.Driver().Query().Exec(ctx, prefix+`UPSERT INTO table (id, val) VALUES (4124, "asd")`) 153 require.NoError(t, err) 154 155 err = scope.Driver().Query().Exec(ctx, prefix+`UPDATE table SET val="qwe"`) 156 require.NoError(t, err) 157 158 err = scope.Driver().Query().Exec(ctx, prefix+`DELETE FROM table`) 159 require.NoError(t, err) 160 161 cdcPath := path.Join(scope.TablePath(), "cdc") 162 reader, err := scope.Driver().Topic().StartReader(scope.TopicConsumerName(), topicoptions.ReadTopic(cdcPath)) 163 require.NoError(t, err) 164 165 var results []*topicsugar.TypedTopicMessage[topicsugar.YDBCDCMessage[*testCDCItem, int64]] 166 for event, err := range topicsugar.UnmarshalCDCStream[*testCDCItem, int64](ctx, reader) { 167 require.NoError(t, err) 168 results = append(results, event) 169 if len(results) == 3 { 170 break 171 } 172 } 173 174 require.Equal(t, &testCDCItem{ID: 4124, Val: "asd"}, results[0].Data.NewImage) 175 require.False(t, results[0].Data.IsErase()) 176 177 require.Equal(t, &testCDCItem{ID: 4124, Val: "asd"}, results[1].Data.OldImage) 178 require.Equal(t, &testCDCItem{ID: 4124, Val: "qwe"}, results[1].Data.NewImage) 179 require.False(t, results[0].Data.IsErase()) 180 181 require.Equal(t, &testCDCItem{ID: 4124, Val: "qwe"}, results[2].Data.OldImage) 182 require.True(t, results[2].Data.IsErase()) 183 } 184 185 type testCDCItem struct { 186 ID int64 187 Val string 188 } 189 190 func (t *testCDCItem) ParseCDCKey(messages []json.RawMessage) (int64, error) { 191 var key int64 192 err := json.Unmarshal(messages[0], &key) 193 return key, err 194 } 195 196 func (t *testCDCItem) SetPrimaryKey(k int64) { 197 t.ID = k 198 }