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 }