...

Source file src/go.mongodb.org/mongo-driver/mongo/cursor_test.go

Documentation: go.mongodb.org/mongo-driver/mongo

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package mongo
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"testing"
    13  	"time"
    14  
    15  	"go.mongodb.org/mongo-driver/bson"
    16  	"go.mongodb.org/mongo-driver/internal/assert"
    17  	"go.mongodb.org/mongo-driver/internal/require"
    18  	"go.mongodb.org/mongo-driver/mongo/options"
    19  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    20  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    21  )
    22  
    23  type testBatchCursor struct {
    24  	batches []*bsoncore.DocumentSequence
    25  	batch   *bsoncore.DocumentSequence
    26  	closed  bool
    27  }
    28  
    29  func newTestBatchCursor(numBatches, batchSize int) *testBatchCursor {
    30  	batches := make([]*bsoncore.DocumentSequence, 0, numBatches)
    31  
    32  	counter := 0
    33  	for batch := 0; batch < numBatches; batch++ {
    34  		var docSequence []byte
    35  
    36  		for doc := 0; doc < batchSize; doc++ {
    37  			var elem []byte
    38  			elem = bsoncore.AppendInt32Element(elem, "foo", int32(counter))
    39  			counter++
    40  
    41  			var doc []byte
    42  			doc = bsoncore.BuildDocumentFromElements(doc, elem)
    43  			docSequence = append(docSequence, doc...)
    44  		}
    45  
    46  		batches = append(batches, &bsoncore.DocumentSequence{
    47  			Style: bsoncore.SequenceStyle,
    48  			Data:  docSequence,
    49  		})
    50  	}
    51  
    52  	return &testBatchCursor{
    53  		batches: batches,
    54  	}
    55  }
    56  
    57  func (tbc *testBatchCursor) ID() int64 {
    58  	if len(tbc.batches) == 0 {
    59  		return 0 // cursor exhausted
    60  	}
    61  
    62  	return 10
    63  }
    64  
    65  func (tbc *testBatchCursor) Next(context.Context) bool {
    66  	if len(tbc.batches) == 0 {
    67  		return false
    68  	}
    69  
    70  	tbc.batch = tbc.batches[0]
    71  	tbc.batches = tbc.batches[1:]
    72  	return true
    73  }
    74  
    75  func (tbc *testBatchCursor) Batch() *bsoncore.DocumentSequence {
    76  	return tbc.batch
    77  }
    78  
    79  func (tbc *testBatchCursor) Server() driver.Server {
    80  	return nil
    81  }
    82  
    83  func (tbc *testBatchCursor) Err() error {
    84  	return nil
    85  }
    86  
    87  func (tbc *testBatchCursor) Close(context.Context) error {
    88  	tbc.closed = true
    89  	return nil
    90  }
    91  
    92  func (tbc *testBatchCursor) SetBatchSize(int32)       {}
    93  func (tbc *testBatchCursor) SetComment(interface{})   {}
    94  func (tbc *testBatchCursor) SetMaxTime(time.Duration) {}
    95  
    96  func TestCursor(t *testing.T) {
    97  	t.Run("loops until docs available", func(t *testing.T) {})
    98  	t.Run("returns false on context cancellation", func(t *testing.T) {})
    99  	t.Run("returns false if error occurred", func(t *testing.T) {})
   100  	t.Run("returns false if ID is zero and no more docs", func(t *testing.T) {})
   101  
   102  	t.Run("TestAll", func(t *testing.T) {
   103  		t.Run("errors if argument is not pointer to slice", func(t *testing.T) {
   104  			cursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)
   105  			assert.Nil(t, err, "newCursor error: %v", err)
   106  			err = cursor.All(context.Background(), []bson.D{})
   107  			assert.NotNil(t, err, "expected error, got nil")
   108  		})
   109  
   110  		t.Run("fills slice with all documents", func(t *testing.T) {
   111  			cursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)
   112  			assert.Nil(t, err, "newCursor error: %v", err)
   113  
   114  			var docs []bson.D
   115  			err = cursor.All(context.Background(), &docs)
   116  			assert.Nil(t, err, "All error: %v", err)
   117  			assert.Equal(t, 5, len(docs), "expected 5 docs, got %v", len(docs))
   118  
   119  			for index, doc := range docs {
   120  				expected := bson.D{{"foo", int32(index)}}
   121  				assert.Equal(t, expected, doc, "expected doc %v, got %v", expected, doc)
   122  			}
   123  		})
   124  
   125  		t.Run("decodes each document into slice type", func(t *testing.T) {
   126  			cursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)
   127  			assert.Nil(t, err, "newCursor error: %v", err)
   128  
   129  			type Document struct {
   130  				Foo int32 `bson:"foo"`
   131  			}
   132  			var docs []Document
   133  			err = cursor.All(context.Background(), &docs)
   134  			assert.Nil(t, err, "All error: %v", err)
   135  			assert.Equal(t, 5, len(docs), "expected 5 documents, got %v", len(docs))
   136  
   137  			for index, doc := range docs {
   138  				expected := Document{Foo: int32(index)}
   139  				assert.Equal(t, expected, doc, "expected doc %v, got %v", expected, doc)
   140  			}
   141  		})
   142  
   143  		t.Run("multiple batches are included", func(t *testing.T) {
   144  			cursor, err := newCursor(newTestBatchCursor(2, 5), nil, nil)
   145  			assert.Nil(t, err, "newCursor error: %v", err)
   146  			var docs []bson.D
   147  			err = cursor.All(context.Background(), &docs)
   148  			assert.Nil(t, err, "All error: %v", err)
   149  			assert.Equal(t, 10, len(docs), "expected 10 docs, got %v", len(docs))
   150  
   151  			for index, doc := range docs {
   152  				expected := bson.D{{"foo", int32(index)}}
   153  				assert.Equal(t, expected, doc, "expected doc %v, got %v", expected, doc)
   154  			}
   155  		})
   156  
   157  		t.Run("cursor is closed after All is called", func(t *testing.T) {
   158  			var docs []bson.D
   159  
   160  			tbc := newTestBatchCursor(1, 5)
   161  			cursor, err := newCursor(tbc, nil, nil)
   162  			assert.Nil(t, err, "newCursor error: %v", err)
   163  
   164  			err = cursor.All(context.Background(), &docs)
   165  			assert.Nil(t, err, "All error: %v", err)
   166  			assert.True(t, tbc.closed, "expected batch cursor to be closed but was not")
   167  		})
   168  
   169  		t.Run("does not error given interface as parameter", func(t *testing.T) {
   170  			var docs interface{} = []bson.D{}
   171  
   172  			cursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)
   173  			assert.Nil(t, err, "newCursor error: %v", err)
   174  
   175  			err = cursor.All(context.Background(), &docs)
   176  			assert.Nil(t, err, "expected Nil, got error: %v", err)
   177  			assert.Equal(t, 5, len(docs.([]bson.D)), "expected 5 documents, got %v", len(docs.([]bson.D)))
   178  		})
   179  		t.Run("errors when not given pointer to slice", func(t *testing.T) {
   180  			var docs interface{} = "test"
   181  
   182  			cursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)
   183  			assert.Nil(t, err, "newCursor error: %v", err)
   184  
   185  			err = cursor.All(context.Background(), &docs)
   186  			assert.NotNil(t, err, "expected error, got: %v", err)
   187  		})
   188  		t.Run("with BSONOptions", func(t *testing.T) {
   189  			cursor, err := newCursor(
   190  				newTestBatchCursor(1, 5),
   191  				&options.BSONOptions{
   192  					UseJSONStructTags: true,
   193  				},
   194  				nil)
   195  			require.NoError(t, err, "newCursor error")
   196  
   197  			type myDocument struct {
   198  				A int32 `json:"foo"`
   199  			}
   200  			var got []myDocument
   201  
   202  			err = cursor.All(context.Background(), &got)
   203  			require.NoError(t, err, "All error")
   204  
   205  			want := []myDocument{{A: 0}, {A: 1}, {A: 2}, {A: 3}, {A: 4}}
   206  
   207  			assert.Equal(t, want, got, "expected and actual All results are different")
   208  		})
   209  	})
   210  }
   211  
   212  func TestNewCursorFromDocuments(t *testing.T) {
   213  	// Mock documents returned by Find in a Cursor.
   214  	t.Run("mock Find", func(t *testing.T) {
   215  		findResult := []interface{}{
   216  			bson.D{{"_id", 0}, {"foo", "bar"}},
   217  			bson.D{{"_id", 1}, {"baz", "qux"}},
   218  			bson.D{{"_id", 2}, {"quux", "quuz"}},
   219  		}
   220  		cur, err := NewCursorFromDocuments(findResult, nil, nil)
   221  		assert.Nil(t, err, "NewCursorFromDocuments error: %v", err)
   222  
   223  		// Assert that decoded documents are as expected.
   224  		var i int
   225  		for cur.Next(context.Background()) {
   226  			docBytes, err := bson.Marshal(findResult[i])
   227  			assert.Nil(t, err, "Marshal error: %v", err)
   228  			expectedDecoded := bson.Raw(docBytes)
   229  
   230  			var decoded bson.Raw
   231  			err = cur.Decode(&decoded)
   232  			assert.Nil(t, err, "Decode error: %v", err)
   233  			assert.Equal(t, expectedDecoded, decoded,
   234  				"expected decoded document %v of Cursor to be %v, got %v",
   235  				i, expectedDecoded, decoded)
   236  			i++
   237  		}
   238  		assert.Equal(t, 3, i, "expected 3 calls to cur.Next, got %v", i)
   239  
   240  		// Check for error on Cursor.
   241  		assert.Nil(t, cur.Err(), "Cursor error: %v", cur.Err())
   242  
   243  		// Assert that a call to cur.Close will not fail.
   244  		err = cur.Close(context.Background())
   245  		assert.Nil(t, err, "Close error: %v", err)
   246  	})
   247  
   248  	// Mock an error in a Cursor.
   249  	t.Run("mock Find with error", func(t *testing.T) {
   250  		mockErr := fmt.Errorf("mock error")
   251  		findResult := []interface{}{bson.D{{"_id", 0}, {"foo", "bar"}}}
   252  		cur, err := NewCursorFromDocuments(findResult, mockErr, nil)
   253  		assert.Nil(t, err, "NewCursorFromDocuments error: %v", err)
   254  
   255  		// Assert that a call to Next will return false because of existing error.
   256  		next := cur.Next(context.Background())
   257  		assert.False(t, next, "expected call to Next to return false, got true")
   258  
   259  		// Check for error on Cursor.
   260  		assert.NotNil(t, cur.Err(), "expected Cursor error, got nil")
   261  		assert.Equal(t, mockErr, cur.Err(), "expected Cursor error %v, got %v",
   262  			mockErr, cur.Err())
   263  	})
   264  }
   265  

View as plain text