github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/collection/collection_utils_test.go (about)

     1  /*
     2   * Copyright (c) 2021-present unTill Pro, Ltd.
     3  *
     4  * @author Michael Saigachenko
     5  */
     6  
     7  package collection
     8  
     9  import (
    10  	"context"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/voedger/voedger/pkg/appdef"
    15  	"github.com/voedger/voedger/pkg/appparts"
    16  	"github.com/voedger/voedger/pkg/istructs"
    17  	"github.com/voedger/voedger/pkg/istructsmem"
    18  	"github.com/voedger/voedger/pkg/pipeline"
    19  	queryprocessor "github.com/voedger/voedger/pkg/processors/query"
    20  )
    21  
    22  type testDataType struct {
    23  	appQName        istructs.AppQName
    24  	totalPartitions istructs.NumAppPartitions
    25  	appEngines      [appparts.ProcessorKind_Count]int
    26  
    27  	pkgName string
    28  
    29  	// common event entites
    30  	partitionIdent string
    31  	partition      istructs.PartitionID
    32  	workspace      istructs.WSID
    33  	plogStartOfs   istructs.Offset
    34  
    35  	// function
    36  	modifyCmdName       appdef.QName
    37  	modifyCmdParamsName appdef.QName
    38  	modifyCmdResultName appdef.QName
    39  
    40  	// records
    41  	tableArticles      appdef.QName
    42  	articleNameIdent   string
    43  	articleNumberIdent string
    44  	articleDeptIdent   string
    45  
    46  	tableArticlePrices        appdef.QName
    47  	articlePricesPriceIdent   string
    48  	articlePricesPriceIdIdent string
    49  
    50  	tableArticlePriceExceptions         appdef.QName
    51  	articlePriceExceptionsPeriodIdIdent string
    52  	articlePriceExceptionsPriceIdent    string
    53  
    54  	tableDepartments appdef.QName
    55  	depNameIdent     string
    56  	depNumberIdent   string
    57  
    58  	tablePrices      appdef.QName
    59  	priceNameIdent   string
    60  	priceNumberIdent string
    61  
    62  	tablePeriods      appdef.QName
    63  	periodNameIdent   string
    64  	periodNumberIdent string
    65  
    66  	// backoffice
    67  	cocaColaNumber  int32
    68  	cocaColaNumber2 int32
    69  	fantaNumber     int32
    70  }
    71  
    72  const OccursUnbounded = appdef.Occurs(0xffff)
    73  
    74  var test = testDataType{
    75  	appQName:        istructs.AppQName_test1_app1,
    76  	totalPartitions: 100,
    77  	appEngines:      appparts.PoolSize(100, 100, 100),
    78  
    79  	pkgName: "test",
    80  
    81  	partitionIdent:      "Partition",
    82  	partition:           55,
    83  	workspace:           1234,
    84  	plogStartOfs:        1,
    85  	modifyCmdName:       appdef.NewQName("test", "modify"),
    86  	modifyCmdParamsName: appdef.NewQName("test", "modifyArgs"),
    87  	modifyCmdResultName: appdef.NewQName("test", "modifyResult"),
    88  
    89  	/////
    90  	tableArticles:      appdef.NewQName("test", "articles"),
    91  	articleNameIdent:   "name",
    92  	articleNumberIdent: "number",
    93  	articleDeptIdent:   "id_department",
    94  
    95  	tableArticlePrices:        appdef.NewQName("test", "article_prices"),
    96  	articlePricesPriceIdIdent: "id_prices",
    97  	articlePricesPriceIdent:   "price",
    98  
    99  	tableArticlePriceExceptions:         appdef.NewQName("test", "article_price_exceptions"),
   100  	articlePriceExceptionsPeriodIdIdent: "id_periods",
   101  	articlePriceExceptionsPriceIdent:    "price",
   102  
   103  	tableDepartments: appdef.NewQName("test", "departments"),
   104  	depNameIdent:     "name",
   105  	depNumberIdent:   "number",
   106  
   107  	tablePrices:      appdef.NewQName("test", "prices"),
   108  	priceNameIdent:   "name",
   109  	priceNumberIdent: "number",
   110  
   111  	tablePeriods:      appdef.NewQName("test", "periods"),
   112  	periodNameIdent:   "name",
   113  	periodNumberIdent: "number",
   114  
   115  	// backoffice
   116  	cocaColaNumber:  10,
   117  	cocaColaNumber2: 11,
   118  	fantaNumber:     12,
   119  }
   120  
   121  type testCmdWorkpeace struct {
   122  	appPart appparts.IAppPartition
   123  	event   istructs.IPLogEvent
   124  }
   125  
   126  func (w testCmdWorkpeace) AppPartition() appparts.IAppPartition { return w.appPart }
   127  func (w testCmdWorkpeace) Event() istructs.IPLogEvent           { return w.event }
   128  
   129  func (w *testCmdWorkpeace) Borrow(ctx context.Context, appParts appparts.IAppPartitions) (err error) {
   130  	w.appPart, err = appParts.WaitForBorrow(ctx, test.appQName, test.partition, appparts.ProcessorKind_Command)
   131  	return err
   132  }
   133  
   134  func (w *testCmdWorkpeace) Command(e any) error {
   135  	w.event = e.(istructs.IPLogEvent)
   136  	return nil
   137  }
   138  
   139  func (w *testCmdWorkpeace) Actualizers(ctx context.Context) error {
   140  	return w.appPart.DoSyncActualizer(ctx, w)
   141  }
   142  
   143  func (w *testCmdWorkpeace) Release() error {
   144  	p := w.appPart
   145  	w.appPart = nil
   146  	if p != nil {
   147  		p.Release()
   148  	}
   149  	return nil
   150  }
   151  
   152  type testCmdProc struct {
   153  	pipeline.ISyncPipeline
   154  	appParts  appparts.IAppPartitions
   155  	ctx       context.Context
   156  	workpeace testCmdWorkpeace
   157  }
   158  
   159  func testProcessor(appParts appparts.IAppPartitions) *testCmdProc {
   160  	proc := &testCmdProc{
   161  		appParts:  appParts,
   162  		ctx:       context.Background(),
   163  		workpeace: testCmdWorkpeace{},
   164  	}
   165  	proc.ISyncPipeline = pipeline.NewSyncPipeline(proc.ctx, "partition processor",
   166  		pipeline.WireSyncOperator("Borrow", pipeline.NewSyncOp(
   167  			func(ctx context.Context, _ interface{}) error {
   168  				return proc.workpeace.Borrow(ctx, appParts)
   169  			})),
   170  		pipeline.WireSyncOperator("Command", pipeline.NewSyncOp(
   171  			func(_ context.Context, event interface{}) error {
   172  				return proc.workpeace.Command(event)
   173  			})),
   174  		pipeline.WireSyncOperator("SyncActualizers", pipeline.NewSyncOp(
   175  			func(ctx context.Context, _ interface{}) error {
   176  				return proc.workpeace.Actualizers(ctx)
   177  			})),
   178  		pipeline.WireSyncOperator("Release", pipeline.NewSyncOp(
   179  			func(context.Context, interface{}) error {
   180  				return proc.workpeace.Release()
   181  			})))
   182  	return proc
   183  }
   184  
   185  type idsGeneratorType struct {
   186  	istructs.IIDGenerator
   187  	idmap          map[istructs.RecordID]istructs.RecordID
   188  	nextPlogOffset istructs.Offset
   189  }
   190  
   191  func (me *idsGeneratorType) NextID(rawID istructs.RecordID, t appdef.IType) (storageID istructs.RecordID, err error) {
   192  	if storageID, err = me.IIDGenerator.NextID(rawID, t); err != nil {
   193  		return istructs.NullRecordID, err
   194  	}
   195  	me.idmap[rawID] = storageID
   196  	return
   197  }
   198  
   199  func (me *idsGeneratorType) nextOffset() (offset istructs.Offset) {
   200  	offset = me.nextPlogOffset
   201  	me.nextPlogOffset++
   202  	return
   203  }
   204  
   205  func (me *idsGeneratorType) decOffset() {
   206  	me.nextPlogOffset--
   207  }
   208  
   209  func newIdsGenerator() idsGeneratorType {
   210  	return idsGeneratorType{
   211  		idmap:          make(map[istructs.RecordID]istructs.RecordID),
   212  		nextPlogOffset: test.plogStartOfs,
   213  		IIDGenerator:   istructsmem.NewIDGenerator(),
   214  	}
   215  }
   216  
   217  func requireArticle(require *require.Assertions, name string, number int32, as istructs.IAppStructs, articleId istructs.RecordID) {
   218  	kb := as.ViewRecords().KeyBuilder(QNameCollectionView)
   219  	kb.PutInt32(Field_PartKey, PartitionKeyCollection)
   220  	kb.PutQName(Field_DocQName, test.tableArticles)
   221  	kb.PutRecordID(field_DocID, articleId)
   222  	kb.PutRecordID(field_ElementID, istructs.NullRecordID)
   223  	value, err := as.ViewRecords().Get(test.workspace, kb)
   224  	require.NoError(err)
   225  	recArticle := value.AsRecord(Field_Record)
   226  	require.Equal(name, recArticle.AsString(test.articleNameIdent))
   227  	require.Equal(number, recArticle.AsInt32(test.articleNumberIdent))
   228  }
   229  
   230  func requireArPrice(require *require.Assertions, priceId istructs.RecordID, price float32, as istructs.IAppStructs, articleId, articlePriceId istructs.RecordID) {
   231  	kb := as.ViewRecords().KeyBuilder(QNameCollectionView)
   232  	kb.PutInt32(Field_PartKey, PartitionKeyCollection)
   233  	kb.PutQName(Field_DocQName, test.tableArticles)
   234  	kb.PutRecordID(field_DocID, articleId)
   235  	kb.PutRecordID(field_ElementID, articlePriceId)
   236  	value, err := as.ViewRecords().Get(test.workspace, kb)
   237  	require.NoError(err)
   238  	recArticlePrice := value.AsRecord(Field_Record)
   239  	require.Equal(priceId, recArticlePrice.AsRecordID(test.articlePricesPriceIdIdent))
   240  	require.Equal(price, recArticlePrice.AsFloat32(test.articlePricesPriceIdent))
   241  }
   242  
   243  func requireArPriceException(require *require.Assertions, periodId istructs.RecordID, price float32, as istructs.IAppStructs, articleId, articlePriceExceptionId istructs.RecordID) {
   244  	kb := as.ViewRecords().KeyBuilder(QNameCollectionView)
   245  	kb.PutInt32(Field_PartKey, PartitionKeyCollection)
   246  	kb.PutQName(Field_DocQName, test.tableArticles)
   247  	kb.PutRecordID(field_DocID, articleId)
   248  	kb.PutRecordID(field_ElementID, articlePriceExceptionId)
   249  	value, err := as.ViewRecords().Get(test.workspace, kb)
   250  	require.NoError(err)
   251  	recArticlePriceException := value.AsRecord(Field_Record)
   252  	require.Equal(periodId, recArticlePriceException.AsRecordID(test.articlePriceExceptionsPeriodIdIdent))
   253  	require.Equal(price, recArticlePriceException.AsFloat32(test.articlePriceExceptionsPriceIdent))
   254  }
   255  
   256  type resultElementRow []interface{}
   257  
   258  type resultElement []resultElementRow
   259  
   260  type resultRow []resultElement
   261  
   262  type testResultSenderClosable struct {
   263  	done       chan interface{}
   264  	resultRows []resultRow
   265  	handledErr error
   266  }
   267  
   268  func (s *testResultSenderClosable) StartArraySection(sectionType string, path []string) {
   269  }
   270  func (s *testResultSenderClosable) StartMapSection(string, []string) { panic("implement me") }
   271  func (s *testResultSenderClosable) ObjectSection(sectionType string, path []string, element interface{}) (err error) {
   272  	return nil
   273  }
   274  func (s *testResultSenderClosable) SendElement(name string, sentRow interface{}) (err error) {
   275  	sentElements := sentRow.([]interface{})
   276  	resultRow := make([]resultElement, len(sentElements))
   277  
   278  	for elmIndex, sentElement := range sentElements {
   279  		sentElemRows := sentElement.([]queryprocessor.IOutputRow)
   280  		resultElm := make([]resultElementRow, len(sentElemRows))
   281  		for i, sentElemRow := range sentElemRows {
   282  			resultElm[i] = sentElemRow.Values()
   283  		}
   284  		resultRow[elmIndex] = resultElm
   285  	}
   286  
   287  	s.resultRows = append(s.resultRows, resultRow)
   288  	return nil
   289  }
   290  func (s *testResultSenderClosable) Close(err error) {
   291  	s.handledErr = err
   292  	close(s.done)
   293  }
   294  func (s *testResultSenderClosable) requireNoError(t *require.Assertions) {
   295  	if s.handledErr != nil {
   296  		t.FailNow(s.handledErr.Error())
   297  	}
   298  }
   299  
   300  func newTestSender() *testResultSenderClosable {
   301  	return &testResultSenderClosable{
   302  		done:       make(chan interface{}),
   303  		resultRows: make([]resultRow, 0), // array of elements, each element is array rows,
   304  	}
   305  }