github.com/weaviate/weaviate@v1.24.6/test/acceptance/multi_tenancy/get_tenant_objects_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 test 13 14 import ( 15 "testing" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 "github.com/weaviate/weaviate/client/objects" 20 "github.com/weaviate/weaviate/entities/models" 21 "github.com/weaviate/weaviate/entities/schema" 22 "github.com/weaviate/weaviate/test/helper" 23 ) 24 25 func TestGetTenantObjects(t *testing.T) { 26 testClass := models.Class{ 27 Class: "MultiTenantClass", 28 MultiTenancyConfig: &models.MultiTenancyConfig{ 29 Enabled: true, 30 }, 31 Properties: []*models.Property{ 32 { 33 Name: "name", 34 DataType: schema.DataTypeText.PropString(), 35 }, 36 }, 37 } 38 tenantNames := []string{ 39 "Tenant1", "Tenant2", "Tenant3", 40 } 41 tenantObjects := []*models.Object{ 42 { 43 ID: "0927a1e0-398e-4e76-91fb-04a7a8f0405c", 44 Class: testClass.Class, 45 Properties: map[string]interface{}{ 46 "name": tenantNames[0], 47 }, 48 Tenant: tenantNames[0], 49 }, 50 { 51 ID: "831ae1d0-f441-44b1-bb2a-46548048e26f", 52 Class: testClass.Class, 53 Properties: map[string]interface{}{ 54 "name": tenantNames[1], 55 }, 56 Tenant: tenantNames[1], 57 }, 58 { 59 ID: "6f3363e0-c0a0-4618-bf1f-b6cad9cdff59", 60 Class: testClass.Class, 61 Properties: map[string]interface{}{ 62 "name": tenantNames[2], 63 }, 64 Tenant: tenantNames[2], 65 }, 66 } 67 68 defer func() { 69 helper.DeleteClass(t, testClass.Class) 70 }() 71 72 t.Run("create class with multi-tenancy enabled", func(t *testing.T) { 73 helper.CreateClass(t, &testClass) 74 }) 75 76 t.Run("create tenants", func(t *testing.T) { 77 tenants := make([]*models.Tenant, len(tenantNames)) 78 for i := range tenants { 79 tenants[i] = &models.Tenant{Name: tenantNames[i]} 80 } 81 helper.CreateTenants(t, testClass.Class, tenants) 82 }) 83 84 t.Run("add tenant objects", func(t *testing.T) { 85 for _, obj := range tenantObjects { 86 helper.CreateObject(t, obj) 87 } 88 }) 89 90 t.Run("get tenant objects", func(t *testing.T) { 91 for i, obj := range tenantObjects { 92 resp, err := helper.TenantObject(t, obj.Class, obj.ID, tenantNames[i]) 93 require.Nil(t, err) 94 assert.Equal(t, obj.ID, resp.ID) 95 assert.Equal(t, obj.Class, resp.Class) 96 assert.Equal(t, obj.Properties, resp.Properties) 97 } 98 }) 99 100 t.Run("get tenant objects with include", func(t *testing.T) { 101 for i, obj := range tenantObjects { 102 resp, err := helper.TenantObjectWithInclude(t, obj.Class, obj.ID, tenantNames[i], "vector") 103 require.Nil(t, err) 104 assert.Equal(t, obj.ID, resp.ID) 105 assert.Equal(t, obj.Class, resp.Class) 106 assert.Equal(t, obj.Properties, resp.Properties) 107 } 108 }) 109 } 110 111 func TestListTenantObjects(t *testing.T) { 112 tenantNames := []string{ 113 "Tenant1", "Tenant2", 114 } 115 116 classMT_1 := models.Class{ 117 Class: "MultiTenantClass1", 118 MultiTenancyConfig: &models.MultiTenancyConfig{ 119 Enabled: true, 120 }, 121 Properties: []*models.Property{ 122 { 123 Name: "name", 124 DataType: schema.DataTypeText.PropString(), 125 }, 126 }, 127 } 128 classMT_2 := models.Class{ 129 Class: "MultiTenantClass2", 130 MultiTenancyConfig: &models.MultiTenancyConfig{ 131 Enabled: true, 132 }, 133 Properties: []*models.Property{ 134 { 135 Name: "name", 136 DataType: schema.DataTypeText.PropString(), 137 }, 138 }, 139 } 140 classMT_3 := models.Class{ 141 Class: "SingleTenantClass3", 142 MultiTenancyConfig: &models.MultiTenancyConfig{ 143 Enabled: true, 144 }, 145 Properties: []*models.Property{ 146 { 147 Name: "name", 148 DataType: schema.DataTypeText.PropString(), 149 }, 150 }, 151 } 152 classMT_4 := models.Class{ 153 Class: "SingleTenantClass4", 154 MultiTenancyConfig: &models.MultiTenancyConfig{ 155 Enabled: true, 156 }, 157 Properties: []*models.Property{ 158 { 159 Name: "name", 160 DataType: schema.DataTypeText.PropString(), 161 }, 162 }, 163 } 164 classNonMT_1 := models.Class{ 165 Class: "NonTenantClass1", 166 Properties: []*models.Property{ 167 { 168 Name: "name", 169 DataType: schema.DataTypeText.PropString(), 170 }, 171 }, 172 } 173 classNonMT_2 := models.Class{ 174 Class: "NonTenantClass2", 175 Properties: []*models.Property{ 176 { 177 Name: "name", 178 DataType: schema.DataTypeText.PropString(), 179 }, 180 }, 181 } 182 183 objectsMT_T1 := []*models.Object{ 184 { 185 ID: "b1d19f8a-2158-4c41-b648-ba77a0ea7074", 186 Class: classMT_1.Class, 187 Properties: map[string]interface{}{ 188 "name": "Obj1_Class1_Tenant1", 189 }, 190 Tenant: tenantNames[0], 191 }, 192 { 193 ID: "a95c027c-07fb-4175-b726-4d5cfd55a7cf", 194 Class: classMT_1.Class, 195 Properties: map[string]interface{}{ 196 "name": "Obj2_Class1_Tenant1", 197 }, 198 Tenant: tenantNames[0], 199 }, 200 { 201 ID: "026890f5-8623-4d31-b295-b2820a81b85a", 202 Class: classMT_2.Class, 203 Properties: map[string]interface{}{ 204 "name": "Obj3_Class2_Tenant1", 205 }, 206 Tenant: tenantNames[0], 207 }, 208 { 209 ID: "d697d6b6-d7e6-47e6-a268-42e917b614e1", 210 Class: classMT_3.Class, 211 Properties: map[string]interface{}{ 212 "name": "Obj4_Class3_Tenant1", 213 }, 214 Tenant: tenantNames[0], 215 }, 216 } 217 objectsMT_T2 := []*models.Object{ 218 { 219 ID: "7baead88-a42b-4876-a185-e0ccc61c58ca", 220 Class: classMT_1.Class, 221 Properties: map[string]interface{}{ 222 "name": "Obj1_Class1_Tenant2", 223 }, 224 Tenant: tenantNames[1], 225 }, 226 { 227 ID: "7fa1fd17-a883-465a-ae22-44f103250b27", 228 Class: classMT_2.Class, 229 Properties: map[string]interface{}{ 230 "name": "Obj2_Class2_Tenant2", 231 }, 232 Tenant: tenantNames[1], 233 }, 234 { 235 ID: "fd4ce87a-8034-4e27-8d47-539fa9dde1f3", 236 Class: classMT_2.Class, 237 Properties: map[string]interface{}{ 238 "name": "Obj3_Class2_Tenant2", 239 }, 240 Tenant: tenantNames[1], 241 }, 242 { 243 ID: "b33d8f4c-30f9-426d-94a5-fa256f3fb5e7", 244 Class: classMT_4.Class, 245 Properties: map[string]interface{}{ 246 "name": "Obj4_Class4_Tenant2", 247 }, 248 Tenant: tenantNames[1], 249 }, 250 } 251 objectsNonMT := []*models.Object{ 252 { 253 ID: "6f019424-bacf-4539-b1be-fc1d3eccb50a", 254 Class: classNonMT_1.Class, 255 Properties: map[string]interface{}{ 256 "name": "Obj1_NonTenant1", 257 }, 258 }, 259 { 260 ID: "8d02b16c-478c-4cae-9384-3b686bae0f4e", 261 Class: classNonMT_1.Class, 262 Properties: map[string]interface{}{ 263 "name": "Obj2_NonTenant1", 264 }, 265 }, 266 { 267 ID: "865a820a-c325-4d10-8d8c-4b991bc43778", 268 Class: classNonMT_2.Class, 269 Properties: map[string]interface{}{ 270 "name": "Obj3_NonTenant2", 271 }, 272 }, 273 } 274 275 defer func() { 276 helper.DeleteClass(t, classMT_1.Class) 277 helper.DeleteClass(t, classMT_2.Class) 278 helper.DeleteClass(t, classMT_3.Class) 279 helper.DeleteClass(t, classMT_4.Class) 280 helper.DeleteClass(t, classNonMT_1.Class) 281 helper.DeleteClass(t, classNonMT_2.Class) 282 }() 283 284 extractIds := func(objs []*models.Object) []string { 285 ids := make([]string, len(objs)) 286 for i, obj := range objs { 287 ids[i] = obj.ID.String() 288 } 289 return ids 290 } 291 292 t.Run("create MT and non-MT classes", func(t *testing.T) { 293 helper.CreateClass(t, &classMT_1) 294 helper.CreateClass(t, &classMT_2) 295 helper.CreateClass(t, &classMT_3) 296 helper.CreateClass(t, &classMT_4) 297 helper.CreateClass(t, &classNonMT_1) 298 helper.CreateClass(t, &classNonMT_2) 299 }) 300 301 t.Run("create tenants for MT classes", func(t *testing.T) { 302 tenants := make([]*models.Tenant, len(tenantNames)) 303 for i := range tenants { 304 tenants[i] = &models.Tenant{Name: tenantNames[i]} 305 } 306 helper.CreateTenants(t, classMT_1.Class, tenants) 307 helper.CreateTenants(t, classMT_2.Class, tenants) 308 helper.CreateTenants(t, classMT_3.Class, tenants[:1]) 309 helper.CreateTenants(t, classMT_4.Class, tenants[1:]) 310 }) 311 312 t.Run("add objects", func(t *testing.T) { 313 objects := append(objectsMT_T1, objectsMT_T2...) 314 objects = append(objects, objectsNonMT...) 315 316 helper.CreateObjectsBatch(t, objects) 317 }) 318 319 t.Run("list objects for tenant 1", func(t *testing.T) { 320 t.Run("no class", func(t *testing.T) { 321 res, err := helper.TenantListObjects(t, "", tenantNames[0]) 322 require.Nil(t, err) 323 require.NotNil(t, res) 324 require.Equal(t, int64(4), res.TotalResults) 325 assert.ElementsMatch(t, []string{ 326 "b1d19f8a-2158-4c41-b648-ba77a0ea7074", 327 "a95c027c-07fb-4175-b726-4d5cfd55a7cf", 328 "026890f5-8623-4d31-b295-b2820a81b85a", 329 "d697d6b6-d7e6-47e6-a268-42e917b614e1", 330 }, extractIds(res.Objects)) 331 }) 332 t.Run("classMT_T1T2_1", func(t *testing.T) { 333 res, err := helper.TenantListObjects(t, classMT_1.Class, tenantNames[0]) 334 335 require.Nil(t, err) 336 require.NotNil(t, res) 337 require.Equal(t, int64(2), res.TotalResults) 338 assert.ElementsMatch(t, []string{ 339 "b1d19f8a-2158-4c41-b648-ba77a0ea7074", 340 "a95c027c-07fb-4175-b726-4d5cfd55a7cf", 341 }, extractIds(res.Objects)) 342 }) 343 t.Run("classMT_T1T2_2", func(t *testing.T) { 344 res, err := helper.TenantListObjects(t, classMT_2.Class, tenantNames[0]) 345 346 require.Nil(t, err) 347 require.NotNil(t, res) 348 require.Equal(t, int64(1), res.TotalResults) 349 assert.ElementsMatch(t, []string{ 350 "026890f5-8623-4d31-b295-b2820a81b85a", 351 }, extractIds(res.Objects)) 352 }) 353 354 t.Run("classMT_T1", func(t *testing.T) { 355 res, err := helper.TenantListObjects(t, classMT_3.Class, tenantNames[0]) 356 357 require.Nil(t, err) 358 require.NotNil(t, res) 359 require.Equal(t, int64(1), res.TotalResults) 360 assert.ElementsMatch(t, []string{ 361 "d697d6b6-d7e6-47e6-a268-42e917b614e1", 362 }, extractIds(res.Objects)) 363 }) 364 365 t.Run("classMT_T2", func(t *testing.T) { 366 res, err := helper.TenantListObjects(t, classMT_4.Class, tenantNames[0]) 367 368 require.NotNil(t, err) 369 expErr := &objects.ObjectsListUnprocessableEntity{} 370 require.ErrorAs(t, err, &expErr) 371 assert.Contains(t, err.(*objects.ObjectsListUnprocessableEntity).Payload.Error[0].Message, tenantNames[0]) 372 require.Nil(t, res) 373 }) 374 }) 375 376 t.Run("list objects for tenant 2", func(t *testing.T) { 377 t.Run("no class", func(t *testing.T) { 378 res, err := helper.TenantListObjects(t, "", tenantNames[1]) 379 380 require.Nil(t, err) 381 require.NotNil(t, res) 382 require.Equal(t, int64(4), res.TotalResults) 383 assert.ElementsMatch(t, []string{ 384 "7baead88-a42b-4876-a185-e0ccc61c58ca", 385 "7fa1fd17-a883-465a-ae22-44f103250b27", 386 "fd4ce87a-8034-4e27-8d47-539fa9dde1f3", 387 "b33d8f4c-30f9-426d-94a5-fa256f3fb5e7", 388 }, extractIds(res.Objects)) 389 }) 390 391 t.Run("classMT_T1T2_1", func(t *testing.T) { 392 res, err := helper.TenantListObjects(t, classMT_1.Class, tenantNames[1]) 393 394 require.Nil(t, err) 395 require.NotNil(t, res) 396 require.Equal(t, int64(1), res.TotalResults) 397 assert.ElementsMatch(t, []string{ 398 "7baead88-a42b-4876-a185-e0ccc61c58ca", 399 }, extractIds(res.Objects)) 400 }) 401 402 t.Run("classMT_T1T2_2", func(t *testing.T) { 403 res, err := helper.TenantListObjects(t, classMT_2.Class, tenantNames[1]) 404 405 require.Nil(t, err) 406 require.NotNil(t, res) 407 require.Equal(t, int64(2), res.TotalResults) 408 assert.ElementsMatch(t, []string{ 409 "7fa1fd17-a883-465a-ae22-44f103250b27", 410 "fd4ce87a-8034-4e27-8d47-539fa9dde1f3", 411 }, extractIds(res.Objects)) 412 }) 413 414 t.Run("classMT_T1", func(t *testing.T) { 415 res, err := helper.TenantListObjects(t, classMT_3.Class, tenantNames[1]) 416 417 require.NotNil(t, err) 418 expErr := &objects.ObjectsListUnprocessableEntity{} 419 require.ErrorAs(t, err, &expErr) 420 assert.Contains(t, err.(*objects.ObjectsListUnprocessableEntity).Payload.Error[0].Message, tenantNames[1]) 421 require.Nil(t, res) 422 }) 423 424 t.Run("classMT_T2", func(t *testing.T) { 425 res, err := helper.TenantListObjects(t, classMT_4.Class, tenantNames[1]) 426 427 require.Nil(t, err) 428 require.NotNil(t, res) 429 require.Equal(t, int64(1), res.TotalResults) 430 assert.ElementsMatch(t, []string{ 431 "b33d8f4c-30f9-426d-94a5-fa256f3fb5e7", 432 }, extractIds(res.Objects)) 433 }) 434 }) 435 436 t.Run("list objects no tenant", func(t *testing.T) { 437 t.Run("no class", func(t *testing.T) { 438 res, err := helper.ListObjects(t, "") 439 440 require.Nil(t, err) 441 require.NotNil(t, res) 442 require.Equal(t, int64(3), res.TotalResults) 443 assert.ElementsMatch(t, []string{ 444 "6f019424-bacf-4539-b1be-fc1d3eccb50a", 445 "8d02b16c-478c-4cae-9384-3b686bae0f4e", 446 "865a820a-c325-4d10-8d8c-4b991bc43778", 447 }, extractIds(res.Objects)) 448 }) 449 450 t.Run("classNonMT_1", func(t *testing.T) { 451 res, err := helper.ListObjects(t, classNonMT_1.Class) 452 453 require.Nil(t, err) 454 require.NotNil(t, res) 455 require.Equal(t, int64(2), res.TotalResults) 456 assert.ElementsMatch(t, []string{ 457 "6f019424-bacf-4539-b1be-fc1d3eccb50a", 458 "8d02b16c-478c-4cae-9384-3b686bae0f4e", 459 }, extractIds(res.Objects)) 460 }) 461 462 t.Run("classNonMT_2", func(t *testing.T) { 463 res, err := helper.ListObjects(t, classNonMT_2.Class) 464 465 require.Nil(t, err) 466 require.NotNil(t, res) 467 require.Equal(t, int64(1), res.TotalResults) 468 assert.ElementsMatch(t, []string{ 469 "865a820a-c325-4d10-8d8c-4b991bc43778", 470 }, extractIds(res.Objects)) 471 }) 472 }) 473 }