Spaces:
Running
Running
| // _ _ | |
| // __ _____ __ ___ ___ __ _| |_ ___ | |
| // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
| // \ V V / __/ (_| |\ V /| | (_| | || __/ | |
| // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
| // | |
| // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
| // | |
| // CONTACT: [email protected] | |
| // | |
| package schema | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "fmt" | |
| "testing" | |
| "github.com/stretchr/testify/assert" | |
| "github.com/stretchr/testify/require" | |
| "github.com/weaviate/weaviate/entities/models" | |
| "github.com/weaviate/weaviate/entities/schema" | |
| "github.com/weaviate/weaviate/usecases/sharding" | |
| ) | |
| // This test makes sure that even when a commit fails, the coordinator still | |
| // honors the commit. This was introduced as part of | |
| // https://github.com/weaviate/weaviate/issues/2616, where a schema | |
| // inconsistency was guaranteed as soon as any node died in it's commit phase | |
| // because the coordinator would behave differently than other (alive) which it | |
| // told to commit. | |
| func TestFailedCommits(t *testing.T) { | |
| type test struct { | |
| name string | |
| // prepare runs before any commit errors occur to build an initial state | |
| prepare func(*testing.T, *Manager) | |
| // action runs with commit errors | |
| action func(*testing.T, *Manager) | |
| expSchema []*models.Class | |
| } | |
| ctx := context.Background() | |
| vTrue := true | |
| vFalse := false | |
| tests := []test{ | |
| { | |
| name: "Add a class", | |
| action: func(t *testing.T, sm *Manager) { | |
| sm.AddClass(ctx, nil, &models.Class{ | |
| Class: "MyClass", | |
| VectorIndexType: "hnsw", | |
| }) | |
| }, | |
| expSchema: []*models.Class{ | |
| classWithDefaultsWithProps(t, "MyClass", nil), | |
| }, | |
| }, | |
| { | |
| name: "Delete a class", | |
| prepare: func(t *testing.T, sm *Manager) { | |
| sm.AddClass(ctx, nil, &models.Class{ | |
| Class: "MyClass", | |
| VectorIndexType: "hnsw", | |
| }) | |
| sm.AddClass(ctx, nil, &models.Class{ | |
| Class: "OtherClass", | |
| VectorIndexType: "hnsw", | |
| }) | |
| }, | |
| action: func(t *testing.T, sm *Manager) { | |
| assert.Nil(t, sm.DeleteClass(ctx, nil, "MyClass")) | |
| }, | |
| expSchema: []*models.Class{ | |
| classWithDefaultsWithProps(t, "OtherClass", nil), | |
| }, | |
| }, | |
| { | |
| name: "Extend a class with a property", | |
| prepare: func(t *testing.T, sm *Manager) { | |
| sm.AddClass(ctx, nil, &models.Class{ | |
| Class: "MyClass", | |
| VectorIndexType: "hnsw", | |
| }) | |
| }, | |
| action: func(t *testing.T, sm *Manager) { | |
| err := sm.AddClassProperty(ctx, nil, "MyClass", &models.Property{ | |
| Name: "prop_1", | |
| DataType: schema.DataTypeInt.PropString(), | |
| }) | |
| assert.Nil(t, err) | |
| }, | |
| expSchema: []*models.Class{ | |
| classWithDefaultsWithProps(t, "MyClass", []*models.Property{ | |
| { | |
| Name: "prop_1", | |
| DataType: schema.DataTypeInt.PropString(), | |
| IndexFilterable: &vTrue, | |
| IndexSearchable: &vFalse, | |
| }, | |
| }), | |
| }, | |
| }, | |
| } | |
| for _, test := range tests { | |
| t.Run(test.name, func(t *testing.T) { | |
| clusterState := &fakeClusterState{ | |
| hosts: []string{"node1", "node2"}, | |
| } | |
| // required for the startup sync | |
| txJSON, _ := json.Marshal(ReadSchemaPayload{ | |
| Schema: &State{ | |
| ObjectSchema: &models.Schema{ | |
| Classes: []*models.Class{}, | |
| }, | |
| }, | |
| }) | |
| txClient := &fakeTxClient{ | |
| openInjectPayload: json.RawMessage(txJSON), // required for the startup sync | |
| } | |
| initialSchema := &State{ | |
| ObjectSchema: &models.Schema{}, | |
| ShardingState: map[string]*sharding.State{}, | |
| } | |
| sm, err := newManagerWithClusterAndTx(t, clusterState, txClient, initialSchema) | |
| require.Nil(t, err) | |
| sm.StartServing(context.Background()) | |
| if test.prepare != nil { | |
| test.prepare(t, sm) | |
| } | |
| txClient.commitErr = fmt.Errorf("Oh I, I just died in your arms tonight") | |
| test.action(t, sm) | |
| assert.ElementsMatch(t, test.expSchema, sm.GetSchemaSkipAuth().Objects.Classes) | |
| }) | |
| } | |
| } | |
| func classWithDefaultsWithProps(t *testing.T, name string, | |
| props []*models.Property, | |
| ) *models.Class { | |
| class := &models.Class{Class: name, VectorIndexType: "hnsw"} | |
| class.Vectorizer = "none" | |
| sc, err := sharding.ParseConfig(map[string]interface{}{}, 1) | |
| require.Nil(t, err) | |
| class.ShardingConfig = sc | |
| class.VectorIndexConfig = fakeVectorConfig{} | |
| class.ReplicationConfig = &models.ReplicationConfig{Factor: 1} | |
| class.MultiTenancyConfig = &models.MultiTenancyConfig{Enabled: false} | |
| (&fakeModuleConfig{}).SetClassDefaults(class) | |
| setInvertedConfigDefaults(class) | |
| class.Properties = props | |
| return class | |
| } | |