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  }