1
2
3
4
5
6
7 package mtest
8
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "strings"
14 "sync"
15 "sync/atomic"
16 "testing"
17 "time"
18
19 "go.mongodb.org/mongo-driver/bson"
20 "go.mongodb.org/mongo-driver/event"
21 "go.mongodb.org/mongo-driver/internal/assert"
22 "go.mongodb.org/mongo-driver/internal/csfle"
23 "go.mongodb.org/mongo-driver/mongo"
24 "go.mongodb.org/mongo-driver/mongo/options"
25 "go.mongodb.org/mongo-driver/mongo/readconcern"
26 "go.mongodb.org/mongo-driver/mongo/readpref"
27 "go.mongodb.org/mongo-driver/mongo/writeconcern"
28 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
29 "go.mongodb.org/mongo-driver/x/mongo/driver"
30 )
31
32 var (
33
34 MajorityWc = writeconcern.New(writeconcern.WMajority())
35
36 PrimaryRp = readpref.Primary()
37
38 SecondaryRp = readpref.Secondary()
39
40 LocalRc = readconcern.Local()
41
42 MajorityRc = readconcern.Majority()
43 )
44
45 const (
46 namespaceExistsErrCode int32 = 48
47 )
48
49
50
51
52 type FailPoint struct {
53 ConfigureFailPoint string `bson:"configureFailPoint"`
54
55 Mode interface{} `bson:"mode"`
56 Data FailPointData `bson:"data"`
57 }
58
59
60 type FailPointMode struct {
61 Times int32 `bson:"times"`
62 Skip int32 `bson:"skip"`
63 }
64
65
66 type FailPointData struct {
67 FailCommands []string `bson:"failCommands,omitempty"`
68 CloseConnection bool `bson:"closeConnection,omitempty"`
69 ErrorCode int32 `bson:"errorCode,omitempty"`
70 FailBeforeCommitExceptionCode int32 `bson:"failBeforeCommitExceptionCode,omitempty"`
71 ErrorLabels *[]string `bson:"errorLabels,omitempty"`
72 WriteConcernError *WriteConcernErrorData `bson:"writeConcernError,omitempty"`
73 BlockConnection bool `bson:"blockConnection,omitempty"`
74 BlockTimeMS int32 `bson:"blockTimeMS,omitempty"`
75 AppName string `bson:"appName,omitempty"`
76 }
77
78
79 type WriteConcernErrorData struct {
80 Code int32 `bson:"code"`
81 Name string `bson:"codeName"`
82 Errmsg string `bson:"errmsg"`
83 ErrorLabels *[]string `bson:"errorLabels,omitempty"`
84 ErrInfo bson.Raw `bson:"errInfo,omitempty"`
85 }
86
87
88 type T struct {
89
90
91
92
93 connsCheckedOut int64
94
95 *testing.T
96
97
98 createClient *bool
99 createCollection *bool
100 runOn []RunOnBlock
101 mockDeployment *mockDeployment
102 mockResponses []bson.D
103 createdColls []*Collection
104 proxyDialer *proxyDialer
105 dbName, collName string
106 failPointNames []string
107 minServerVersion string
108 maxServerVersion string
109 validTopologies []TopologyKind
110 auth *bool
111 enterprise *bool
112 dataLake *bool
113 ssl *bool
114 collCreateOpts *options.CreateCollectionOptions
115 requireAPIVersion *bool
116
117
118 clientType ClientType
119 clientOpts *options.ClientOptions
120 collOpts *options.CollectionOptions
121 shareClient *bool
122
123 baseOpts *Options
124
125
126 monitorLock sync.Mutex
127 started []*event.CommandStartedEvent
128 succeeded []*event.CommandSucceededEvent
129 failed []*event.CommandFailedEvent
130
131 Client *mongo.Client
132 DB *mongo.Database
133 Coll *mongo.Collection
134 }
135
136 func newT(wrapped *testing.T, opts ...*Options) *T {
137 t := &T{
138 T: wrapped,
139 }
140 for _, opt := range opts {
141 for _, optFn := range opt.optFuncs {
142 optFn(t)
143 }
144 }
145
146 if err := t.verifyConstraints(); err != nil {
147 t.Skipf("skipping due to environmental constraints: %v", err)
148 }
149
150 if t.collName == "" {
151 t.collName = t.Name()
152 }
153 if t.dbName == "" {
154 t.dbName = TestDb
155 }
156 t.collName = sanitizeCollectionName(t.dbName, t.collName)
157
158
159 t.baseOpts = NewOptions().ClientOptions(t.clientOpts).CollectionOptions(t.collOpts).ClientType(t.clientType)
160 if t.shareClient != nil {
161 t.baseOpts.ShareClient(*t.shareClient)
162 }
163
164 return t
165 }
166
167
168
169 func New(wrapped *testing.T, opts ...*Options) *T {
170
171
172 if testing.Short() {
173 wrapped.Skip("skipping mtest integration test in short mode")
174 }
175
176 t := newT(wrapped, opts...)
177
178
179
180 if t.shareClient != nil && *t.shareClient {
181 t.createTestClient()
182 }
183
184 wrapped.Cleanup(t.cleanup)
185
186 return t
187 }
188
189
190
191 func (t *T) cleanup() {
192 if t.Client == nil {
193 return
194 }
195
196
197 if t.clientType != Mock {
198 t.ClearCollections()
199 t.ClearFailPoints()
200 }
201
202
203
204 _ = t.Client.Disconnect(context.Background())
205 }
206
207
208
209
210 func (t *T) Run(name string, callback func(mt *T)) {
211 t.RunOpts(name, NewOptions(), callback)
212 }
213
214
215
216
217
218 func (t *T) RunOpts(name string, opts *Options, callback func(mt *T)) {
219 t.T.Run(name, func(wrapped *testing.T) {
220 sub := newT(wrapped, t.baseOpts, opts)
221
222
223 if sub.clientType == Mock && len(sub.mockResponses) > 0 {
224 sub.AddMockResponses(sub.mockResponses...)
225 }
226
227
228 if sub.shareClient != nil && *sub.shareClient && sub.clientType == t.clientType {
229 sub.Client = t.Client
230 }
231
232 if sub.Client == nil {
233 if sub.createClient == nil || *sub.createClient {
234 sub.createTestClient()
235 }
236 }
237
238 if sub.Client != nil {
239 sub.createTestCollection()
240 }
241
242
243 defer func() {
244 if sub.Client == nil {
245 return
246 }
247
248
249
250 sessions := sub.Client.NumberSessionsInProgress()
251 conns := sub.NumberConnectionsCheckedOut()
252
253 if sub.clientType != Mock {
254 sub.ClearFailPoints()
255 sub.ClearCollections()
256 }
257
258 if sub.shareClient == nil || !*sub.shareClient {
259 _ = sub.Client.Disconnect(context.Background())
260 }
261 assert.Equal(sub, 0, sessions, "%v sessions checked out", sessions)
262 assert.Equal(sub, 0, conns, "%v connections checked out", conns)
263 }()
264
265
266 sub.ClearEvents()
267 callback(sub)
268 })
269 }
270
271
272
273 func (t *T) AddMockResponses(responses ...bson.D) {
274 t.mockDeployment.addResponses(responses...)
275 }
276
277
278 func (t *T) ClearMockResponses() {
279 t.mockDeployment.clearResponses()
280 }
281
282
283
284 func (t *T) GetStartedEvent() *event.CommandStartedEvent {
285
286
287 if len(t.started) == 0 {
288 return nil
289 }
290 e := t.started[0]
291 t.started = t.started[1:]
292 return e
293 }
294
295
296
297 func (t *T) GetSucceededEvent() *event.CommandSucceededEvent {
298
299
300 if len(t.succeeded) == 0 {
301 return nil
302 }
303 e := t.succeeded[0]
304 t.succeeded = t.succeeded[1:]
305 return e
306 }
307
308
309
310 func (t *T) GetFailedEvent() *event.CommandFailedEvent {
311
312
313 if len(t.failed) == 0 {
314 return nil
315 }
316 e := t.failed[0]
317 t.failed = t.failed[1:]
318 return e
319 }
320
321
322
323 func (t *T) GetAllStartedEvents() []*event.CommandStartedEvent {
324 return t.started
325 }
326
327
328
329 func (t *T) GetAllSucceededEvents() []*event.CommandSucceededEvent {
330 return t.succeeded
331 }
332
333
334
335 func (t *T) GetAllFailedEvents() []*event.CommandFailedEvent {
336 return t.failed
337 }
338
339
340
341
342 func (t *T) FilterStartedEvents(filter func(*event.CommandStartedEvent) bool) {
343 var newEvents []*event.CommandStartedEvent
344 for _, evt := range t.started {
345 if filter(evt) {
346 newEvents = append(newEvents, evt)
347 }
348 }
349 t.started = newEvents
350 }
351
352
353
354
355 func (t *T) FilterSucceededEvents(filter func(*event.CommandSucceededEvent) bool) {
356 var newEvents []*event.CommandSucceededEvent
357 for _, evt := range t.succeeded {
358 if filter(evt) {
359 newEvents = append(newEvents, evt)
360 }
361 }
362 t.succeeded = newEvents
363 }
364
365
366
367
368 func (t *T) FilterFailedEvents(filter func(*event.CommandFailedEvent) bool) {
369 var newEvents []*event.CommandFailedEvent
370 for _, evt := range t.failed {
371 if filter(evt) {
372 newEvents = append(newEvents, evt)
373 }
374 }
375 t.failed = newEvents
376 }
377
378
379
380 func (t *T) GetProxiedMessages() []*ProxyMessage {
381 if t.proxyDialer == nil {
382 return nil
383 }
384 return t.proxyDialer.Messages()
385 }
386
387
388 func (t *T) NumberConnectionsCheckedOut() int {
389 return int(atomic.LoadInt64(&t.connsCheckedOut))
390 }
391
392
393 func (t *T) ClearEvents() {
394 t.started = t.started[:0]
395 t.succeeded = t.succeeded[:0]
396 t.failed = t.failed[:0]
397 }
398
399
400
401
402
403 func (t *T) ResetClient(opts *options.ClientOptions) {
404 if opts != nil {
405 t.clientOpts = opts
406 }
407
408 _ = t.Client.Disconnect(context.Background())
409 t.createTestClient()
410 t.DB = t.Client.Database(t.dbName)
411 t.Coll = t.DB.Collection(t.collName, t.collOpts)
412
413 for _, coll := range t.createdColls {
414
415 if coll.hasDifferentClient {
416 continue
417 }
418
419
420 if coll.created.Name() == t.collName && coll.created.Database().Name() == t.dbName {
421 coll.created = t.Coll
422 continue
423 }
424
425
426 coll.created = t.Client.Database(coll.DB).Collection(coll.Name, coll.Opts)
427 }
428 }
429
430
431 type Collection struct {
432 Name string
433 DB string
434 Client *mongo.Client
435 Opts *options.CollectionOptions
436 CreateOpts *options.CreateCollectionOptions
437 ViewOn string
438 ViewPipeline interface{}
439 hasDifferentClient bool
440 created *mongo.Collection
441 }
442
443
444
445
446 func (t *T) CreateCollection(coll Collection, createOnServer bool) *mongo.Collection {
447 if coll.DB == "" {
448 coll.DB = t.DB.Name()
449 }
450 if coll.Client == nil {
451 coll.Client = t.Client
452 }
453 coll.hasDifferentClient = coll.Client != t.Client
454
455 db := coll.Client.Database(coll.DB)
456
457 if coll.CreateOpts != nil && coll.CreateOpts.EncryptedFields != nil {
458
459
460
461 DropEncryptedCollection(t, db.Collection(coll.Name), coll.CreateOpts.EncryptedFields)
462 }
463
464 if createOnServer && t.clientType != Mock {
465 var err error
466 if coll.ViewOn != "" {
467 err = db.CreateView(context.Background(), coll.Name, coll.ViewOn, coll.ViewPipeline)
468 } else {
469 err = db.CreateCollection(context.Background(), coll.Name, coll.CreateOpts)
470 }
471
472
473 if err != nil && !errors.Is(err, driver.ErrUnacknowledgedWrite) {
474
475
476 var cmdErr mongo.CommandError
477 if !errors.As(err, &cmdErr) || cmdErr.Code != namespaceExistsErrCode {
478 t.Fatalf("error creating collection or view: %v on server: %v", coll.Name, err)
479 }
480 }
481 }
482
483 coll.created = db.Collection(coll.Name, coll.Opts)
484 t.createdColls = append(t.createdColls, &coll)
485 return coll.created
486 }
487
488
489
490 func DropEncryptedCollection(t *T, coll *mongo.Collection, encryptedFields interface{}) {
491 t.Helper()
492
493 var efBSON bsoncore.Document
494 efBSON, err := bson.Marshal(encryptedFields)
495 assert.Nil(t, err, "error in Marshal: %v", err)
496
497
498
499 escCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.Name(), csfle.EncryptedStateCollection)
500 assert.Nil(t, err, "error in getEncryptedStateCollectionName: %v", err)
501 err = coll.Database().Collection(escCollection).Drop(context.Background())
502 assert.Nil(t, err, "error in Drop: %v", err)
503
504
505 ecocCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.Name(), csfle.EncryptedCompactionCollection)
506 assert.Nil(t, err, "error in getEncryptedStateCollectionName: %v", err)
507 err = coll.Database().Collection(ecocCollection).Drop(context.Background())
508 assert.Nil(t, err, "error in Drop: %v", err)
509
510
511 err = coll.Drop(context.Background())
512 assert.Nil(t, err, "error in Drop: %v", err)
513 }
514
515
516 func (t *T) ClearCollections() {
517
518 if !testContext.dataLake {
519 for _, coll := range t.createdColls {
520 if coll.CreateOpts != nil && coll.CreateOpts.EncryptedFields != nil {
521 DropEncryptedCollection(t, coll.created, coll.CreateOpts.EncryptedFields)
522 }
523
524 err := coll.created.Drop(context.Background())
525 if errors.Is(err, mongo.ErrUnacknowledgedWrite) || errors.Is(err, driver.ErrUnacknowledgedWrite) {
526
527
528
529 collname := coll.created.Name()
530 wcm := writeconcern.New(writeconcern.WMajority(), writeconcern.WTimeout(1*time.Second))
531 wccoll := t.DB.Collection(collname, options.Collection().SetWriteConcern(wcm))
532 _ = wccoll.Drop(context.Background())
533
534 }
535 }
536 }
537 t.createdColls = t.createdColls[:0]
538 }
539
540
541
542 func (t *T) SetFailPoint(fp FailPoint) {
543
544 if modeMap, ok := fp.Mode.(map[string]interface{}); ok {
545 var key string
546 var err error
547
548 if times, ok := modeMap["times"]; ok {
549 key = "times"
550 modeMap["times"], err = t.interfaceToInt32(times)
551 }
552 if skip, ok := modeMap["skip"]; ok {
553 key = "skip"
554 modeMap["skip"], err = t.interfaceToInt32(skip)
555 }
556
557 if err != nil {
558 t.Fatalf("error converting %s to int32: %v", key, err)
559 }
560 }
561
562 if err := SetFailPoint(fp, t.Client); err != nil {
563 t.Fatal(err)
564 }
565 t.failPointNames = append(t.failPointNames, fp.ConfigureFailPoint)
566 }
567
568
569
570
571
572 func (t *T) SetFailPointFromDocument(fp bson.Raw) {
573 if err := SetRawFailPoint(fp, t.Client); err != nil {
574 t.Fatal(err)
575 }
576
577 name := fp.Index(0).Value().StringValue()
578 t.failPointNames = append(t.failPointNames, name)
579 }
580
581
582
583 func (t *T) TrackFailPoint(fpName string) {
584 t.failPointNames = append(t.failPointNames, fpName)
585 }
586
587
588 func (t *T) ClearFailPoints() {
589 db := t.Client.Database("admin")
590 for _, fp := range t.failPointNames {
591 cmd := bson.D{
592 {"configureFailPoint", fp},
593 {"mode", "off"},
594 }
595 err := db.RunCommand(context.Background(), cmd).Err()
596 if err != nil {
597 t.Fatalf("error clearing fail point %s: %v", fp, err)
598 }
599 }
600 t.failPointNames = t.failPointNames[:0]
601 }
602
603
604 func (t *T) CloneDatabase(opts *options.DatabaseOptions) {
605 t.DB = t.Client.Database(t.dbName, opts)
606 }
607
608
609 func (t *T) CloneCollection(opts *options.CollectionOptions) {
610 var err error
611 t.Coll, err = t.Coll.Clone(opts)
612 assert.Nil(t, err, "error cloning collection: %v", err)
613 }
614
615 func sanitizeCollectionName(db string, coll string) string {
616
617 coll = strings.Replace(coll, "$", "%", -1)
618
619
620 if len(db+"."+coll) >= 120 {
621
622 remaining := 120 - (len(db) + 1)
623 coll = coll[len(coll)-remaining:]
624 }
625 return coll
626 }
627
628 func (t *T) createTestClient() {
629 clientOpts := t.clientOpts
630 if clientOpts == nil {
631
632 clientOpts = options.Client().SetWriteConcern(MajorityWc).SetReadPreference(PrimaryRp)
633 }
634
635 if clientOpts.Deployment == nil && t.clientType != Mock && clientOpts.ServerAPIOptions == nil && testContext.requireAPIVersion {
636 clientOpts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))
637 }
638
639
640 var customMonitor = clientOpts.Monitor
641 clientOpts.SetMonitor(&event.CommandMonitor{
642 Started: func(_ context.Context, cse *event.CommandStartedEvent) {
643 if customMonitor != nil && customMonitor.Started != nil {
644 customMonitor.Started(context.Background(), cse)
645 }
646 t.monitorLock.Lock()
647 defer t.monitorLock.Unlock()
648 t.started = append(t.started, cse)
649 },
650 Succeeded: func(_ context.Context, cse *event.CommandSucceededEvent) {
651 if customMonitor != nil && customMonitor.Succeeded != nil {
652 customMonitor.Succeeded(context.Background(), cse)
653 }
654 t.monitorLock.Lock()
655 defer t.monitorLock.Unlock()
656 t.succeeded = append(t.succeeded, cse)
657 },
658 Failed: func(_ context.Context, cfe *event.CommandFailedEvent) {
659 if customMonitor != nil && customMonitor.Failed != nil {
660 customMonitor.Failed(context.Background(), cfe)
661 }
662 t.monitorLock.Lock()
663 defer t.monitorLock.Unlock()
664 t.failed = append(t.failed, cfe)
665 },
666 })
667
668 if clientOpts.Deployment == nil {
669 previousPoolMonitor := clientOpts.PoolMonitor
670
671 clientOpts.SetPoolMonitor(&event.PoolMonitor{
672 Event: func(evt *event.PoolEvent) {
673 if previousPoolMonitor != nil {
674 previousPoolMonitor.Event(evt)
675 }
676
677 switch evt.Type {
678 case event.GetSucceeded:
679 atomic.AddInt64(&t.connsCheckedOut, 1)
680 case event.ConnectionReturned:
681 atomic.AddInt64(&t.connsCheckedOut, -1)
682 }
683 },
684 })
685 }
686
687 var err error
688 switch t.clientType {
689 case Pinned:
690
691 pinnedHostList := []string{testContext.connString.Hosts[0]}
692 uriOpts := options.Client().ApplyURI(testContext.connString.Original).SetHosts(pinnedHostList)
693 t.Client, err = mongo.NewClient(uriOpts, clientOpts)
694 case Mock:
695
696 clientOpts.PoolMonitor = nil
697 t.mockDeployment = newMockDeployment()
698 clientOpts.Deployment = t.mockDeployment
699 t.Client, err = mongo.NewClient(clientOpts)
700 case Proxy:
701 t.proxyDialer = newProxyDialer()
702 clientOpts.SetDialer(t.proxyDialer)
703
704
705 fallthrough
706 case Default:
707
708
709 var uriOpts *options.ClientOptions
710 if clientOpts.Deployment == nil {
711
712
713 uriOpts = options.Client().ApplyURI(testContext.connString.Original)
714 }
715
716
717 t.Client, err = mongo.NewClient(uriOpts, clientOpts)
718 }
719 if err != nil {
720 t.Fatalf("error creating client: %v", err)
721 }
722 if err := t.Client.Connect(context.Background()); err != nil {
723 t.Fatalf("error connecting client: %v", err)
724 }
725 }
726
727 func (t *T) createTestCollection() {
728 t.DB = t.Client.Database(t.dbName)
729 t.createdColls = t.createdColls[:0]
730
731
732
733 createOnServer := (t.createCollection == nil || *t.createCollection) && !testContext.dataLake
734 t.Coll = t.CreateCollection(Collection{
735 Name: t.collName,
736 CreateOpts: t.collCreateOpts,
737 Opts: t.collOpts,
738 }, createOnServer)
739 }
740
741
742
743 func verifyVersionConstraints(min, max string) error {
744 if min != "" && CompareServerVersions(testContext.serverVersion, min) < 0 {
745 return fmt.Errorf("server version %q is lower than min required version %q", testContext.serverVersion, min)
746 }
747 if max != "" && CompareServerVersions(testContext.serverVersion, max) > 0 {
748 return fmt.Errorf("server version %q is higher than max version %q", testContext.serverVersion, max)
749 }
750 return nil
751 }
752
753
754
755 func verifyTopologyConstraints(topologies []TopologyKind) error {
756 if len(topologies) == 0 {
757 return nil
758 }
759
760 for _, topo := range topologies {
761
762
763 if topo == testContext.topoKind || (topo == ShardedReplicaSet && testContext.shardedReplicaSet) {
764 return nil
765 }
766 }
767 return fmt.Errorf("topology kind %q does not match any of the required kinds %q", testContext.topoKind, topologies)
768 }
769
770 func verifyServerParametersConstraints(serverParameters map[string]bson.RawValue) error {
771 for param, expected := range serverParameters {
772 actual, err := testContext.serverParameters.LookupErr(param)
773 if err != nil {
774 return fmt.Errorf("server does not support parameter %q", param)
775 }
776 if !expected.Equal(actual) {
777 return fmt.Errorf("mismatched values for server parameter %q; expected %s, got %s", param, expected, actual)
778 }
779 }
780 return nil
781 }
782
783 func verifyAuthConstraint(expected *bool) error {
784 if expected != nil && *expected != testContext.authEnabled {
785 return fmt.Errorf("test requires auth value: %v, cluster auth value: %v", *expected, testContext.authEnabled)
786 }
787 return nil
788 }
789
790 func verifyServerlessConstraint(expected string) error {
791 switch expected {
792 case "require":
793 if !testContext.serverless {
794 return fmt.Errorf("test requires serverless")
795 }
796 case "forbid":
797 if testContext.serverless {
798 return fmt.Errorf("test forbids serverless")
799 }
800 case "allow", "":
801 default:
802 return fmt.Errorf("invalid value for serverless: %s", expected)
803 }
804 return nil
805 }
806
807
808 func verifyRunOnBlockConstraint(rob RunOnBlock) error {
809 if err := verifyVersionConstraints(rob.MinServerVersion, rob.MaxServerVersion); err != nil {
810 return err
811 }
812 if err := verifyTopologyConstraints(rob.Topology); err != nil {
813 return err
814 }
815
816
817
818
819
820 auth := rob.Auth
821 if rob.AuthEnabled != nil {
822 if auth != nil {
823 return fmt.Errorf("runOnBlock cannot specify both auth and authEnabled")
824 }
825 auth = rob.AuthEnabled
826 }
827 if err := verifyAuthConstraint(auth); err != nil {
828 return err
829 }
830
831 if err := verifyServerlessConstraint(rob.Serverless); err != nil {
832 return err
833 }
834 if err := verifyServerParametersConstraints(rob.ServerParameters); err != nil {
835 return err
836 }
837
838 if rob.CSFLE != nil {
839 if *rob.CSFLE && !IsCSFLEEnabled() {
840 return fmt.Errorf("runOnBlock requires CSFLE to be enabled. Build with the cse tag to enable")
841 } else if !*rob.CSFLE && IsCSFLEEnabled() {
842 return fmt.Errorf("runOnBlock requires CSFLE to be disabled. Build without the cse tag to disable")
843 }
844 if *rob.CSFLE {
845 if err := verifyVersionConstraints("4.2", ""); err != nil {
846 return err
847 }
848 }
849 }
850 return nil
851 }
852
853
854 func (t *T) verifyConstraints() error {
855
856 if err := verifyVersionConstraints(t.minServerVersion, t.maxServerVersion); err != nil {
857 return err
858 }
859 if err := verifyTopologyConstraints(t.validTopologies); err != nil {
860 return err
861 }
862 if err := verifyAuthConstraint(t.auth); err != nil {
863 return err
864 }
865 if t.ssl != nil && *t.ssl != testContext.sslEnabled {
866 return fmt.Errorf("test requires ssl value: %v, cluster ssl value: %v", *t.ssl, testContext.sslEnabled)
867 }
868 if t.enterprise != nil && *t.enterprise != testContext.enterpriseServer {
869 return fmt.Errorf("test requires enterprise value: %v, cluster enterprise value: %v", *t.enterprise,
870 testContext.enterpriseServer)
871 }
872 if t.dataLake != nil && *t.dataLake != testContext.dataLake {
873 return fmt.Errorf("test requires cluster to be data lake: %v, cluster is data lake: %v", *t.dataLake,
874 testContext.dataLake)
875 }
876 if t.requireAPIVersion != nil && *t.requireAPIVersion != testContext.requireAPIVersion {
877 return fmt.Errorf("test requires RequireAPIVersion value: %v, local RequireAPIVersion value: %v", *t.requireAPIVersion,
878 testContext.requireAPIVersion)
879 }
880
881
882
883 if len(t.runOn) == 0 {
884 return nil
885 }
886
887
888
889 runOnErrors := make([]error, 0, len(t.runOn))
890 for _, runOn := range t.runOn {
891 err := verifyRunOnBlockConstraint(runOn)
892 if err == nil {
893 return nil
894 }
895
896 runOnErrors = append(runOnErrors, err)
897 }
898 return fmt.Errorf("no matching RunOnBlock; comparison errors: %v", runOnErrors)
899 }
900
901 func (t *T) interfaceToInt32(i interface{}) (int32, error) {
902 switch conv := i.(type) {
903 case int:
904 return int32(conv), nil
905 case int32:
906 return conv, nil
907 case int64:
908 return int32(conv), nil
909 case float64:
910 return int32(conv), nil
911 }
912
913 return 0, fmt.Errorf("type %T cannot be converted to int32", i)
914 }
915
View as plain text