github.com/weaviate/weaviate@v1.24.6/usecases/schema/read_consensus_test.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package schema
    13  
    14  import (
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  	"testing"
    19  
    20  	logrustest "github.com/sirupsen/logrus/hooks/test"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  	"github.com/weaviate/weaviate/entities/models"
    24  	"github.com/weaviate/weaviate/usecases/cluster"
    25  	"github.com/weaviate/weaviate/usecases/sharding"
    26  )
    27  
    28  func TestReadConsensus(t *testing.T) {
    29  	type test struct {
    30  		name           string
    31  		in             []*cluster.Transaction
    32  		expectedResult *cluster.Transaction
    33  		expectError    bool
    34  		parser         parserFn
    35  	}
    36  
    37  	tests := []test{
    38  		{
    39  			name: "different (unrelated) tx type (no consensus required)",
    40  			in: []*cluster.Transaction{
    41  				{
    42  					Type: AddClass,
    43  					ID:   "id1",
    44  				},
    45  			},
    46  			expectError: false,
    47  		},
    48  		{
    49  			name: "single schema with content",
    50  			in: []*cluster.Transaction{
    51  				wrapSchemaAsRawReadTx(&State{
    52  					ShardingState: map[string]*sharding.State{
    53  						"Foo": {
    54  							IndexID: "1234",
    55  						},
    56  					},
    57  					ObjectSchema: &models.Schema{
    58  						Classes: []*models.Class{
    59  							{
    60  								Class: "Foo",
    61  							},
    62  						},
    63  					},
    64  				}),
    65  			},
    66  			expectedResult: wrapSchemaAsReadTx(&State{
    67  				ShardingState: map[string]*sharding.State{
    68  					"Foo": {
    69  						IndexID: "1234",
    70  					},
    71  				},
    72  				ObjectSchema: &models.Schema{
    73  					Classes: []*models.Class{
    74  						{
    75  							Class: "Foo",
    76  						},
    77  					},
    78  				},
    79  			}),
    80  		},
    81  		{
    82  			name: "two identical empty schemas",
    83  			in: []*cluster.Transaction{
    84  				wrapSchemaAsRawReadTx(newSchema()),
    85  				wrapSchemaAsRawReadTx(newSchema()),
    86  			},
    87  			expectedResult: wrapSchemaAsReadTx(newSchema()),
    88  		},
    89  		{
    90  			name: "two identical filled schemas",
    91  			in: []*cluster.Transaction{
    92  				wrapSchemaAsRawReadTx(&State{
    93  					ShardingState: map[string]*sharding.State{
    94  						"Foo": {
    95  							IndexID: "1234",
    96  						},
    97  					},
    98  					ObjectSchema: &models.Schema{
    99  						Classes: []*models.Class{
   100  							{
   101  								Class: "Foo",
   102  							},
   103  						},
   104  					},
   105  				}),
   106  				wrapSchemaAsRawReadTx(&State{
   107  					ShardingState: map[string]*sharding.State{
   108  						"Foo": {
   109  							IndexID: "1234",
   110  						},
   111  					},
   112  					ObjectSchema: &models.Schema{
   113  						Classes: []*models.Class{
   114  							{
   115  								Class: "Foo",
   116  							},
   117  						},
   118  					},
   119  				}),
   120  			},
   121  			expectedResult: wrapSchemaAsReadTx(&State{
   122  				ShardingState: map[string]*sharding.State{
   123  					"Foo": {
   124  						IndexID: "1234",
   125  					},
   126  				},
   127  				ObjectSchema: &models.Schema{
   128  					Classes: []*models.Class{
   129  						{
   130  							Class: "Foo",
   131  						},
   132  					},
   133  				},
   134  			}),
   135  		},
   136  		{
   137  			name: "3 responses with a conflict",
   138  			in: []*cluster.Transaction{
   139  				wrapSchemaAsRawReadTx(&State{
   140  					ShardingState: map[string]*sharding.State{
   141  						"Foo": {
   142  							IndexID: "1234",
   143  						},
   144  					},
   145  					ObjectSchema: &models.Schema{
   146  						Classes: []*models.Class{
   147  							{
   148  								Class: "Foo",
   149  							},
   150  						},
   151  					},
   152  				}),
   153  				wrapSchemaAsRawReadTx(&State{
   154  					ShardingState: map[string]*sharding.State{
   155  						"Foo": {
   156  							IndexID: "1234",
   157  						},
   158  					},
   159  					ObjectSchema: &models.Schema{
   160  						Classes: []*models.Class{
   161  							{
   162  								Class: "Foo",
   163  							},
   164  						},
   165  					},
   166  				}),
   167  				wrapSchemaAsRawReadTx(&State{
   168  					ShardingState: map[string]*sharding.State{
   169  						"Foo": {
   170  							IndexID: "1234",
   171  						},
   172  					},
   173  					ObjectSchema: &models.Schema{
   174  						Classes: []*models.Class{
   175  							{
   176  								Class: "Foo",
   177  								//  the other classes don't have the vector index set:
   178  								VectorIndexType: "La-Forca-de-Bruta",
   179  							},
   180  						},
   181  					},
   182  				}),
   183  			},
   184  			expectError: true,
   185  		},
   186  		{
   187  			name: "tx id mismatch",
   188  			in: []*cluster.Transaction{
   189  				{
   190  					Type:    ReadSchema,
   191  					Payload: json.RawMessage("null"),
   192  					ID:      "id1",
   193  				},
   194  				{
   195  					Type:    ReadSchema,
   196  					Payload: json.RawMessage("null"),
   197  					ID:      "id2",
   198  				},
   199  			},
   200  			expectError: true,
   201  		},
   202  		{
   203  			name: "invalid payload json",
   204  			in: []*cluster.Transaction{
   205  				{
   206  					Type:    ReadSchema,
   207  					Payload: json.RawMessage("----<@#()$*--"),
   208  					ID:      "id1",
   209  				},
   210  			},
   211  			expectError: true,
   212  		},
   213  		{
   214  			name: "invalid payload json",
   215  			in: []*cluster.Transaction{
   216  				{
   217  					Type:    ReadSchema,
   218  					Payload: json.RawMessage("----<@#()$*--"),
   219  					ID:      "id1",
   220  				},
   221  			},
   222  			expectError: true,
   223  		},
   224  		{
   225  			name: "schema parse error",
   226  			parser: func(ctx context.Context, s *State) error {
   227  				return fmt.Errorf("not so fast there, Mister Schema!")
   228  			},
   229  			in: []*cluster.Transaction{
   230  				wrapSchemaAsRawReadTx(&State{
   231  					ShardingState: map[string]*sharding.State{
   232  						"Foo": {
   233  							IndexID: "1234",
   234  						},
   235  					},
   236  					ObjectSchema: &models.Schema{
   237  						Classes: []*models.Class{
   238  							{
   239  								Class: "Foo",
   240  							},
   241  						},
   242  					},
   243  				}),
   244  			},
   245  			expectError: true,
   246  		},
   247  	}
   248  
   249  	for _, test := range tests {
   250  		t.Run(test.name, func(t *testing.T) {
   251  			parser := dummyParser
   252  			if test.parser != nil {
   253  				parser = test.parser
   254  			}
   255  
   256  			logger, _ := logrustest.NewNullLogger()
   257  			out, err := newReadConsensus(parser, logger)(context.Background(), test.in)
   258  
   259  			if test.expectError {
   260  				require.NotNil(t, err, "must error")
   261  			} else {
   262  				require.Nil(t, err)
   263  			}
   264  
   265  			assert.Equal(t, test.expectedResult, out)
   266  		})
   267  	}
   268  }
   269  
   270  // raw = before unmarshalling the payload
   271  func wrapSchemaAsRawReadTx(s *State) *cluster.Transaction {
   272  	payloadJSON, _ := json.Marshal(ReadSchemaPayload{
   273  		Schema: s,
   274  	})
   275  	return &cluster.Transaction{
   276  		Type:    ReadSchema,
   277  		Payload: json.RawMessage(payloadJSON),
   278  		ID:      "best-tx",
   279  	}
   280  }
   281  
   282  func wrapSchemaAsReadTx(s *State) *cluster.Transaction {
   283  	return &cluster.Transaction{
   284  		Type:    ReadSchema,
   285  		Payload: ReadSchemaPayload{Schema: s},
   286  		ID:      "best-tx",
   287  	}
   288  }
   289  
   290  func dummyParser(ctx context.Context, schema *State) error {
   291  	return nil
   292  }