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 }