github.com/weaviate/weaviate@v1.24.6/usecases/modules/module_config_init_and_validate_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 modules 13 14 import ( 15 "context" 16 "testing" 17 18 "github.com/pkg/errors" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "github.com/weaviate/weaviate/entities/models" 22 "github.com/weaviate/weaviate/entities/modulecapabilities" 23 "github.com/weaviate/weaviate/entities/moduletools" 24 "github.com/weaviate/weaviate/entities/schema" 25 ) 26 27 func TestSetClassDefaults(t *testing.T) { 28 t.Run("no modules", func(t *testing.T) { 29 class := &models.Class{ 30 Class: "Foo", 31 Vectorizer: "none", 32 } 33 34 p := NewProvider() 35 p.SetClassDefaults(class) 36 37 assert.Equal(t, &models.Class{Class: "Foo", Vectorizer: "none"}, class, 38 "the class is not changed") 39 }) 40 41 t.Run("module is set, but does not have config capability", func(t *testing.T) { 42 class := &models.Class{ 43 Class: "Foo", 44 Vectorizer: "my-module", 45 } 46 47 p := NewProvider() 48 p.Register(&dummyText2VecModuleNoCapabilities{name: "my-module"}) 49 p.SetClassDefaults(class) 50 51 assert.Equal(t, &models.Class{Class: "Foo", Vectorizer: "my-module"}, class, 52 "the class is not changed") 53 }) 54 55 t.Run("without user-provided values", func(t *testing.T) { 56 class := &models.Class{ 57 Class: "Foo", 58 Properties: []*models.Property{{ 59 Name: "Foo", 60 DataType: schema.DataTypeText.PropString(), 61 Tokenization: models.PropertyTokenizationWhitespace, 62 }}, 63 Vectorizer: "my-module", 64 } 65 expected := &models.Class{ 66 Class: "Foo", 67 ModuleConfig: map[string]interface{}{ 68 "my-module": map[string]interface{}{ 69 "per-class-prop-1": "some default value", 70 "per-class-prop-2": "some default value", 71 }, 72 }, 73 Properties: []*models.Property{{ 74 Name: "Foo", 75 DataType: schema.DataTypeText.PropString(), 76 Tokenization: models.PropertyTokenizationWhitespace, 77 ModuleConfig: map[string]interface{}{ 78 "my-module": map[string]interface{}{ 79 "per-prop-1": "prop default value", 80 "per-prop-2": "prop default value", 81 }, 82 }, 83 }}, 84 Vectorizer: "my-module", 85 } 86 87 p := NewProvider() 88 p.Register(&dummyModuleClassConfigurator{ 89 dummyText2VecModuleNoCapabilities: dummyText2VecModuleNoCapabilities{ 90 name: "my-module", 91 }, 92 }) 93 p.SetClassDefaults(class) 94 95 assert.Equal(t, expected, class, 96 "the defaults were set from config") 97 }) 98 99 t.Run("with some user-provided values", func(t *testing.T) { 100 class := &models.Class{ 101 Class: "Foo", 102 ModuleConfig: map[string]interface{}{ 103 "my-module": map[string]interface{}{ 104 "per-class-prop-1": "overwritten by user", 105 }, 106 }, 107 Properties: []*models.Property{{ 108 Name: "Foo", 109 DataType: schema.DataTypeText.PropString(), 110 Tokenization: models.PropertyTokenizationWhitespace, 111 ModuleConfig: map[string]interface{}{ 112 "my-module": map[string]interface{}{ 113 "per-prop-1": "prop overwritten by user", 114 }, 115 }, 116 }}, 117 Vectorizer: "my-module", 118 } 119 expected := &models.Class{ 120 Class: "Foo", 121 ModuleConfig: map[string]interface{}{ 122 "my-module": map[string]interface{}{ 123 "per-class-prop-1": "overwritten by user", 124 "per-class-prop-2": "some default value", 125 }, 126 }, 127 Properties: []*models.Property{{ 128 Name: "Foo", 129 DataType: schema.DataTypeText.PropString(), 130 Tokenization: models.PropertyTokenizationWhitespace, 131 ModuleConfig: map[string]interface{}{ 132 "my-module": map[string]interface{}{ 133 "per-prop-1": "prop overwritten by user", 134 "per-prop-2": "prop default value", 135 }, 136 }, 137 }}, 138 Vectorizer: "my-module", 139 } 140 141 p := NewProvider() 142 p.Register(&dummyModuleClassConfigurator{ 143 dummyText2VecModuleNoCapabilities: dummyText2VecModuleNoCapabilities{ 144 name: "my-module", 145 }, 146 }) 147 p.SetClassDefaults(class) 148 149 assert.Equal(t, expected, class, 150 "the defaults were set from config") 151 }) 152 } 153 154 func TestValidateClass(t *testing.T) { 155 ctx := context.Background() 156 t.Run("when class has no vectorizer set, it does not check", func(t *testing.T) { 157 class := &models.Class{ 158 Class: "Foo", 159 Properties: []*models.Property{{ 160 Name: "Foo", 161 DataType: schema.DataTypeText.PropString(), 162 Tokenization: models.PropertyTokenizationWhitespace, 163 }}, 164 Vectorizer: "none", 165 } 166 167 p := NewProvider() 168 p.Register(&dummyModuleClassConfigurator{ 169 validateError: errors.Errorf("if I was used, you'd fail"), 170 dummyText2VecModuleNoCapabilities: dummyText2VecModuleNoCapabilities{ 171 name: "my-module", 172 }, 173 }) 174 p.SetClassDefaults(class) 175 176 assert.Nil(t, p.ValidateClass(ctx, class)) 177 }) 178 179 t.Run("when vectorizer does not have capability, it skips validation", 180 func(t *testing.T) { 181 class := &models.Class{ 182 Class: "Foo", 183 Properties: []*models.Property{{ 184 Name: "Foo", 185 DataType: schema.DataTypeText.PropString(), 186 Tokenization: models.PropertyTokenizationWhitespace, 187 }}, 188 Vectorizer: "my-module", 189 } 190 191 p := NewProvider() 192 p.Register(&dummyText2VecModuleNoCapabilities{ 193 name: "my-module", 194 }) 195 p.SetClassDefaults(class) 196 197 assert.Nil(t, p.ValidateClass(ctx, class)) 198 }) 199 200 t.Run("the module validates if capable and configured", func(t *testing.T) { 201 class := &models.Class{ 202 Class: "Foo", 203 Properties: []*models.Property{{ 204 Name: "Foo", 205 DataType: schema.DataTypeText.PropString(), 206 Tokenization: models.PropertyTokenizationWhitespace, 207 }}, 208 Vectorizer: "my-module", 209 } 210 211 p := NewProvider() 212 p.Register(&dummyModuleClassConfigurator{ 213 validateError: errors.Errorf("no can do!"), 214 dummyText2VecModuleNoCapabilities: dummyText2VecModuleNoCapabilities{ 215 name: "my-module", 216 }, 217 }) 218 p.SetClassDefaults(class) 219 220 err := p.ValidateClass(ctx, class) 221 require.NotNil(t, err) 222 assert.Equal(t, "module 'my-module': no can do!", err.Error()) 223 }) 224 } 225 226 func TestSetSinglePropertyDefaults(t *testing.T) { 227 class := &models.Class{ 228 Class: "Foo", 229 ModuleConfig: map[string]interface{}{ 230 "my-module": map[string]interface{}{ 231 "per-class-prop-1": "overwritten by user", 232 }, 233 }, 234 Properties: []*models.Property{{ 235 Name: "Foo", 236 DataType: schema.DataTypeText.PropString(), 237 Tokenization: models.PropertyTokenizationWhitespace, 238 ModuleConfig: map[string]interface{}{ 239 "my-module": map[string]interface{}{ 240 "per-prop-1": "prop overwritten by user", 241 }, 242 }, 243 }}, 244 Vectorizer: "my-module", 245 } 246 prop := &models.Property{ 247 DataType: []string{"boolean"}, 248 ModuleConfig: map[string]interface{}{ 249 "my-module": map[string]interface{}{ 250 "per-prop-1": "overwritten by user", 251 }, 252 }, 253 Name: "newProp", 254 } 255 expected := &models.Property{ 256 DataType: []string{"boolean"}, 257 ModuleConfig: map[string]interface{}{ 258 "my-module": map[string]interface{}{ 259 "per-prop-1": "overwritten by user", 260 "per-prop-2": "prop default value", 261 }, 262 }, 263 Name: "newProp", 264 } 265 266 p := NewProvider() 267 p.Register(&dummyModuleClassConfigurator{ 268 dummyText2VecModuleNoCapabilities: dummyText2VecModuleNoCapabilities{ 269 name: "my-module", 270 }, 271 }) 272 p.SetSinglePropertyDefaults(class, prop) 273 274 assert.Equal(t, expected, prop, 275 "user specified module config is used, for rest the default value is used") 276 } 277 278 type dummyModuleClassConfigurator struct { 279 dummyText2VecModuleNoCapabilities 280 validateError error 281 } 282 283 func (d *dummyModuleClassConfigurator) ClassConfigDefaults() map[string]interface{} { 284 return map[string]interface{}{ 285 "per-class-prop-1": "some default value", 286 "per-class-prop-2": "some default value", 287 } 288 } 289 290 func (d *dummyModuleClassConfigurator) PropertyConfigDefaults( 291 dt *schema.DataType, 292 ) map[string]interface{} { 293 return map[string]interface{}{ 294 "per-prop-1": "prop default value", 295 "per-prop-2": "prop default value", 296 } 297 } 298 299 func (d *dummyModuleClassConfigurator) ValidateClass(ctx context.Context, 300 class *models.Class, cfg moduletools.ClassConfig, 301 ) error { 302 return d.validateError 303 } 304 305 var _ = modulecapabilities.ClassConfigurator( 306 &dummyModuleClassConfigurator{})