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  }