...

Source file src/go.mongodb.org/mongo-driver/x/bsonx/bsoncore/document_sequence_test.go

Documentation: go.mongodb.org/mongo-driver/x/bsonx/bsoncore

     1  // Copyright (C) MongoDB, Inc. 2022-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 bsoncore
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"io"
    13  	"strconv"
    14  	"testing"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  )
    18  
    19  func TestDocumentSequence(t *testing.T) {
    20  
    21  	genArrayStyle := func(num int) []byte {
    22  		idx, seq := AppendDocumentStart(nil)
    23  		for i := 0; i < num; i++ {
    24  			seq = AppendDocumentElement(
    25  				seq, strconv.Itoa(i),
    26  				BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
    27  			)
    28  		}
    29  		seq, _ = AppendDocumentEnd(seq, idx)
    30  		return seq
    31  	}
    32  	genSequenceStyle := func(num int) []byte {
    33  		var seq []byte
    34  		for i := 0; i < num; i++ {
    35  			seq = append(seq, BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159))...)
    36  		}
    37  		return seq
    38  	}
    39  
    40  	idx, arrayStyle := AppendDocumentStart(nil)
    41  	idx2, arrayStyle := AppendDocumentElementStart(arrayStyle, "0")
    42  	arrayStyle = AppendDoubleElement(arrayStyle, "pi", 3.14159)
    43  	arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
    44  	idx2, arrayStyle = AppendDocumentElementStart(arrayStyle, "1")
    45  	arrayStyle = AppendStringElement(arrayStyle, "hello", "world")
    46  	arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
    47  	arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx)
    48  
    49  	t.Run("Documents", func(t *testing.T) {
    50  		testCases := []struct {
    51  			name      string
    52  			style     DocumentSequenceStyle
    53  			data      []byte
    54  			documents []Document
    55  			err       error
    56  		}{
    57  			{
    58  				"SequenceStle/corrupted document",
    59  				SequenceStyle,
    60  				[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
    61  				nil,
    62  				ErrCorruptedDocument,
    63  			},
    64  			{
    65  				"SequenceStyle/success",
    66  				SequenceStyle,
    67  				BuildDocument(
    68  					BuildDocument(
    69  						nil,
    70  						AppendStringElement(AppendDoubleElement(nil, "pi", 3.14159), "hello", "world"),
    71  					),
    72  					AppendDoubleElement(AppendStringElement(nil, "hello", "world"), "pi", 3.14159),
    73  				),
    74  				[]Document{
    75  					BuildDocument(nil, AppendStringElement(AppendDoubleElement(nil, "pi", 3.14159), "hello", "world")),
    76  					BuildDocument(nil, AppendDoubleElement(AppendStringElement(nil, "hello", "world"), "pi", 3.14159)),
    77  				},
    78  				nil,
    79  			},
    80  			{
    81  				"ArrayStyle/insufficient bytes",
    82  				ArrayStyle,
    83  				[]byte{0x01, 0x02, 0x03, 0x04, 0x05},
    84  				nil,
    85  				ErrCorruptedDocument,
    86  			},
    87  			{
    88  				"ArrayStyle/non-document",
    89  				ArrayStyle,
    90  				BuildDocument(nil, AppendDoubleElement(nil, "0", 12345.67890)),
    91  				nil,
    92  				ErrNonDocument,
    93  			},
    94  			{
    95  				"ArrayStyle/success",
    96  				ArrayStyle,
    97  				arrayStyle,
    98  				[]Document{
    99  					BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
   100  					BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
   101  				},
   102  				nil,
   103  			},
   104  			{"Invalid DocumentSequenceStyle", 0, nil, nil, ErrInvalidDocumentSequenceStyle},
   105  		}
   106  
   107  		for _, tc := range testCases {
   108  			t.Run(tc.name, func(t *testing.T) {
   109  				ds := &DocumentSequence{
   110  					Style: tc.style,
   111  					Data:  tc.data,
   112  				}
   113  				documents, err := ds.Documents()
   114  				if !cmp.Equal(documents, tc.documents) {
   115  					t.Errorf("Documents do not match. got %v; want %v", documents, tc.documents)
   116  				}
   117  				if !errors.Is(err, tc.err) {
   118  					t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
   119  				}
   120  			})
   121  		}
   122  	})
   123  	t.Run("Next", func(t *testing.T) {
   124  		seqDoc := BuildDocument(
   125  			BuildDocument(
   126  				nil,
   127  				AppendDoubleElement(nil, "pi", 3.14159),
   128  			),
   129  			AppendStringElement(nil, "hello", "world"),
   130  		)
   131  
   132  		idx, arrayStyle := AppendDocumentStart(nil)
   133  		idx2, arrayStyle := AppendDocumentElementStart(arrayStyle, "0")
   134  		arrayStyle = AppendDoubleElement(arrayStyle, "pi", 3.14159)
   135  		arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
   136  		idx2, arrayStyle = AppendDocumentElementStart(arrayStyle, "1")
   137  		arrayStyle = AppendStringElement(arrayStyle, "hello", "world")
   138  		arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
   139  		arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx)
   140  
   141  		testCases := []struct {
   142  			name     string
   143  			style    DocumentSequenceStyle
   144  			data     []byte
   145  			pos      int
   146  			document Document
   147  			err      error
   148  		}{
   149  			{"io.EOF", 0, make([]byte, 10), 10, nil, io.EOF},
   150  			{
   151  				"SequenceStyle/corrupted document",
   152  				SequenceStyle,
   153  				[]byte{0x01, 0x02, 0x03, 0x04},
   154  				0,
   155  				nil,
   156  				ErrCorruptedDocument,
   157  			},
   158  			{
   159  				"SequenceStyle/success/first",
   160  				SequenceStyle,
   161  				seqDoc,
   162  				0,
   163  				BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
   164  				nil,
   165  			},
   166  			{
   167  				"SequenceStyle/success/second",
   168  				SequenceStyle,
   169  				seqDoc,
   170  				17,
   171  				BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
   172  				nil,
   173  			},
   174  			{
   175  				"ArrayStyle/corrupted document/too short",
   176  				ArrayStyle,
   177  				[]byte{0x01, 0x02, 0x03},
   178  				0,
   179  				nil,
   180  				ErrCorruptedDocument,
   181  			},
   182  			{
   183  				"ArrayStyle/corrupted document/invalid element",
   184  				ArrayStyle,
   185  				[]byte{0x00, 0x00, 0x00, 0x00, 0x01, '0', 0x00, 0x01, 0x02},
   186  				0,
   187  				nil,
   188  				ErrCorruptedDocument,
   189  			},
   190  			{
   191  				"ArrayStyle/non-document",
   192  				ArrayStyle,
   193  				BuildDocument(nil, AppendDoubleElement(nil, "0", 12345.67890)),
   194  				0,
   195  				nil,
   196  				ErrNonDocument,
   197  			},
   198  			{
   199  				"ArrayStyle/success/first",
   200  				ArrayStyle,
   201  				arrayStyle,
   202  				0,
   203  				BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
   204  				nil,
   205  			},
   206  			{
   207  				"ArrayStyle/success/second",
   208  				ArrayStyle,
   209  				arrayStyle,
   210  				24,
   211  				BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
   212  				nil,
   213  			},
   214  			{"Invalid DocumentSequenceStyle", 0, make([]byte, 4), 0, nil, ErrInvalidDocumentSequenceStyle},
   215  		}
   216  
   217  		for _, tc := range testCases {
   218  			t.Run(tc.name, func(t *testing.T) {
   219  				ds := &DocumentSequence{
   220  					Style: tc.style,
   221  					Data:  tc.data,
   222  					Pos:   tc.pos,
   223  				}
   224  				document, err := ds.Next()
   225  				if !bytes.Equal(document, tc.document) {
   226  					t.Errorf("Documents do not match. got %v; want %v", document, tc.document)
   227  				}
   228  				if !errors.Is(err, tc.err) {
   229  					t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
   230  				}
   231  			})
   232  		}
   233  	})
   234  
   235  	t.Run("Full Iteration", func(t *testing.T) {
   236  		testCases := []struct {
   237  			name  string
   238  			style DocumentSequenceStyle
   239  			data  []byte
   240  			count int
   241  		}{
   242  			{"SequenceStyle/success/nil", SequenceStyle, nil, 0},
   243  			{"SequenceStyle/success/0", SequenceStyle, []byte{}, 0},
   244  			{"SequenceStyle/success/1", SequenceStyle, genSequenceStyle(1), 1},
   245  			{"SequenceStyle/success/2", SequenceStyle, genSequenceStyle(2), 2},
   246  			{"SequenceStyle/success/10", SequenceStyle, genSequenceStyle(10), 10},
   247  			{"SequenceStyle/success/100", SequenceStyle, genSequenceStyle(100), 100},
   248  			{"ArrayStyle/success/nil", ArrayStyle, nil, 0},
   249  			{"ArrayStyle/success/0", ArrayStyle, []byte{0x05, 0x00, 0x00, 0x00, 0x00}, 0},
   250  			{"ArrayStyle/success/1", ArrayStyle, genArrayStyle(1), 1},
   251  			{"ArrayStyle/success/2", ArrayStyle, genArrayStyle(2), 2},
   252  			{"ArrayStyle/success/10", ArrayStyle, genArrayStyle(10), 10},
   253  			{"ArrayStyle/success/100", ArrayStyle, genArrayStyle(100), 100},
   254  		}
   255  
   256  		for _, tc := range testCases {
   257  			t.Run("Documents/"+tc.name, func(t *testing.T) {
   258  				ds := &DocumentSequence{
   259  					Style: tc.style,
   260  					Data:  tc.data,
   261  				}
   262  				docs, err := ds.Documents()
   263  				if err != nil {
   264  					t.Fatalf("Unexpected error: %v", err)
   265  				}
   266  				count := len(docs)
   267  				if count != tc.count {
   268  					t.Errorf("Coun't fully iterate documents, wrong count. got %v; want %v", count, tc.count)
   269  				}
   270  			})
   271  			t.Run("Next/"+tc.name, func(t *testing.T) {
   272  				ds := &DocumentSequence{
   273  					Style: tc.style,
   274  					Data:  tc.data,
   275  				}
   276  				var docs []Document
   277  				for {
   278  					doc, err := ds.Next()
   279  					if errors.Is(err, io.EOF) {
   280  						break
   281  					}
   282  					if err != nil {
   283  						t.Fatalf("Unexpected error: %v", err)
   284  					}
   285  					docs = append(docs, doc)
   286  				}
   287  				count := len(docs)
   288  				if count != tc.count {
   289  					t.Errorf("Coun't fully iterate documents, wrong count. got %v; want %v", count, tc.count)
   290  				}
   291  			})
   292  		}
   293  	})
   294  	t.Run("DocumentCount", func(t *testing.T) {
   295  		testCases := []struct {
   296  			name  string
   297  			style DocumentSequenceStyle
   298  			data  []byte
   299  			count int
   300  		}{
   301  			{
   302  				"SequenceStyle/corrupt document/first",
   303  				SequenceStyle,
   304  				[]byte{0x01, 0x02, 0x03},
   305  				0,
   306  			},
   307  			{
   308  				"SequenceStyle/corrupt document/second",
   309  				SequenceStyle,
   310  				[]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
   311  				0,
   312  			},
   313  			{"SequenceStyle/success/nil", SequenceStyle, nil, 0},
   314  			{"SequenceStyle/success/0", SequenceStyle, []byte{}, 0},
   315  			{"SequenceStyle/success/1", SequenceStyle, genSequenceStyle(1), 1},
   316  			{"SequenceStyle/success/2", SequenceStyle, genSequenceStyle(2), 2},
   317  			{"SequenceStyle/success/10", SequenceStyle, genSequenceStyle(10), 10},
   318  			{"SequenceStyle/success/100", SequenceStyle, genSequenceStyle(100), 100},
   319  			{
   320  				"ArrayStyle/corrupt document/length",
   321  				ArrayStyle,
   322  				[]byte{0x01, 0x02, 0x03},
   323  				0,
   324  			},
   325  			{
   326  				"ArrayStyle/corrupt element/first",
   327  				ArrayStyle,
   328  				BuildDocument(nil, []byte{0x01, 0x00, 0x03, 0x04, 0x05}),
   329  				0,
   330  			},
   331  			{
   332  				"ArrayStyle/corrupt element/second",
   333  				ArrayStyle,
   334  				BuildDocument(nil, []byte{0x0A, 0x00, 0x01, 0x00, 0x03, 0x04, 0x05}),
   335  				0,
   336  			},
   337  			{"ArrayStyle/success/nil", ArrayStyle, nil, 0},
   338  			{"ArrayStyle/success/0", ArrayStyle, []byte{0x05, 0x00, 0x00, 0x00, 0x00}, 0},
   339  			{"ArrayStyle/success/1", ArrayStyle, genArrayStyle(1), 1},
   340  			{"ArrayStyle/success/2", ArrayStyle, genArrayStyle(2), 2},
   341  			{"ArrayStyle/success/10", ArrayStyle, genArrayStyle(10), 10},
   342  			{"ArrayStyle/success/100", ArrayStyle, genArrayStyle(100), 100},
   343  			{"Invalid DocumentSequenceStyle", 0, nil, 0},
   344  		}
   345  
   346  		for _, tc := range testCases {
   347  			t.Run(tc.name, func(t *testing.T) {
   348  				ds := &DocumentSequence{
   349  					Style: tc.style,
   350  					Data:  tc.data,
   351  				}
   352  				count := ds.DocumentCount()
   353  				if count != tc.count {
   354  					t.Errorf("Document counts don't match. got %v; want %v", count, tc.count)
   355  				}
   356  			})
   357  		}
   358  	})
   359  	t.Run("Empty", func(t *testing.T) {
   360  		testCases := []struct {
   361  			name    string
   362  			ds      *DocumentSequence
   363  			isEmpty bool
   364  		}{
   365  			{"ArrayStyle/is empty/nil", nil, true},
   366  			{"ArrayStyle/is empty/0", &DocumentSequence{Style: ArrayStyle, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}}, true},
   367  			{"ArrayStyle/is not empty/non-0", &DocumentSequence{Style: ArrayStyle, Data: genArrayStyle(10)}, false},
   368  			{"SequenceStyle/is empty/nil", nil, true},
   369  			{"SequenceStyle/is empty/0", &DocumentSequence{Style: SequenceStyle, Data: []byte{}}, true},
   370  			{"SequenceStyle/is not empty/non-0", &DocumentSequence{Style: SequenceStyle, Data: genSequenceStyle(10)}, false},
   371  		}
   372  
   373  		for _, tc := range testCases {
   374  			t.Run(tc.name, func(t *testing.T) {
   375  				isEmpty := tc.ds.Empty()
   376  				if isEmpty != tc.isEmpty {
   377  					t.Errorf("Unexpected Empty result. got %v; want %v", isEmpty, tc.isEmpty)
   378  				}
   379  			})
   380  		}
   381  	})
   382  	t.Run("ResetIterator", func(t *testing.T) {
   383  		ds := &DocumentSequence{Pos: 1234567890}
   384  		want := 0
   385  		ds.ResetIterator()
   386  		if ds.Pos != want {
   387  			t.Errorf("Unexpected position after ResetIterator. got %d; want %d", ds.Pos, want)
   388  		}
   389  	})
   390  	t.Run("no panic on nil", func(t *testing.T) {
   391  		capturePanic := func() {
   392  			if err := recover(); err != nil {
   393  				t.Errorf("Unexpected panic. got %v; want <nil>", err)
   394  			}
   395  		}
   396  		t.Run("DocumentCount", func(t *testing.T) {
   397  			defer capturePanic()
   398  			var ds *DocumentSequence
   399  			_ = ds.DocumentCount()
   400  		})
   401  		t.Run("Empty", func(t *testing.T) {
   402  			defer capturePanic()
   403  			var ds *DocumentSequence
   404  			_ = ds.Empty()
   405  		})
   406  		t.Run("ResetIterator", func(t *testing.T) {
   407  			defer capturePanic()
   408  			var ds *DocumentSequence
   409  			ds.ResetIterator()
   410  		})
   411  		t.Run("Documents", func(t *testing.T) {
   412  			defer capturePanic()
   413  			var ds *DocumentSequence
   414  			_, _ = ds.Documents()
   415  		})
   416  		t.Run("Next", func(t *testing.T) {
   417  			defer capturePanic()
   418  			var ds *DocumentSequence
   419  			_, _ = ds.Next()
   420  		})
   421  	})
   422  }
   423  

View as plain text