github.com/weaviate/weaviate@v1.24.6/test/acceptance/multi_tenancy/patch_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 "fmt" 16 "net/http" 17 "testing" 18 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "github.com/weaviate/weaviate/client/objects" 22 "github.com/weaviate/weaviate/entities/models" 23 "github.com/weaviate/weaviate/entities/schema" 24 "github.com/weaviate/weaviate/test/helper" 25 ) 26 27 func TestPatchTenantObjects(t *testing.T) { 28 mutableProp := "mutableProp" 29 testClass := models.Class{ 30 Class: "MultiTenantClass", 31 MultiTenancyConfig: &models.MultiTenancyConfig{ 32 Enabled: true, 33 }, 34 Properties: []*models.Property{ 35 { 36 Name: "name", 37 DataType: schema.DataTypeText.PropString(), 38 }, { 39 Name: mutableProp, 40 DataType: schema.DataTypeText.PropString(), 41 }, 42 }, 43 } 44 tenantNames := []string{ 45 "Tenant1", "Tenant2", "Tenant3", 46 } 47 tenantObjects := []*models.Object{ 48 { 49 ID: "0927a1e0-398e-4e76-91fb-04a7a8f0405c", 50 Class: testClass.Class, 51 Properties: map[string]interface{}{ 52 "name": tenantNames[0], 53 mutableProp: "obj#0", 54 }, 55 Tenant: tenantNames[0], 56 }, 57 { 58 ID: "831ae1d0-f441-44b1-bb2a-46548048e26f", 59 Class: testClass.Class, 60 Properties: map[string]interface{}{ 61 "name": tenantNames[1], 62 mutableProp: "obj#1", 63 }, 64 Tenant: tenantNames[1], 65 }, 66 { 67 ID: "6f3363e0-c0a0-4618-bf1f-b6cad9cdff59", 68 Class: testClass.Class, 69 Properties: map[string]interface{}{ 70 "name": tenantNames[2], 71 mutableProp: "obj#2", 72 }, 73 Tenant: tenantNames[2], 74 }, 75 } 76 77 defer func() { 78 helper.DeleteClass(t, testClass.Class) 79 }() 80 81 t.Run("create class with multi-tenancy enabled", func(t *testing.T) { 82 helper.CreateClass(t, &testClass) 83 }) 84 85 t.Run("create tenants", func(t *testing.T) { 86 tenants := make([]*models.Tenant, len(tenantNames)) 87 for i := range tenants { 88 tenants[i] = &models.Tenant{Name: tenantNames[i]} 89 } 90 helper.CreateTenants(t, testClass.Class, tenants) 91 }) 92 93 t.Run("add tenant objects", func(t *testing.T) { 94 for _, obj := range tenantObjects { 95 helper.CreateObject(t, obj) 96 } 97 98 t.Run("verify tenant object creation", func(t *testing.T) { 99 for i, obj := range tenantObjects { 100 resp, err := helper.TenantObject(t, obj.Class, obj.ID, tenantNames[i]) 101 require.Nil(t, err) 102 require.Equal(t, obj.ID, resp.ID) 103 require.Equal(t, obj.Class, resp.Class) 104 require.Equal(t, obj.Properties, resp.Properties) 105 } 106 }) 107 }) 108 109 t.Run("patch tenant objects", func(t *testing.T) { 110 for i, obj := range tenantObjects { 111 mut := obj.Properties.(map[string]interface{})[mutableProp] 112 toUpdate := &models.Object{ 113 Class: testClass.Class, 114 ID: obj.ID, 115 Properties: map[string]interface{}{ 116 "name": tenantNames[i], 117 mutableProp: fmt.Sprintf("%s--patched", mut), 118 }, 119 Tenant: tenantNames[i], 120 } 121 helper.PatchObject(t, toUpdate) 122 } 123 124 t.Run("assert tenant object updates", func(t *testing.T) { 125 for i, obj := range tenantObjects { 126 resp, err := helper.TenantObject(t, obj.Class, obj.ID, tenantNames[i]) 127 require.Nil(t, err) 128 require.Equal(t, obj.ID, resp.ID) 129 require.Equal(t, obj.Class, resp.Class) 130 expectedProps := obj.Properties.(map[string]interface{}) 131 expectedProps[mutableProp] = fmt.Sprintf("%s--patched", expectedProps[mutableProp]) 132 require.Equal(t, expectedProps, resp.Properties) 133 } 134 }) 135 }) 136 } 137 138 func TestPatchTenantObjects_ChangeTenant(t *testing.T) { 139 className := "MultiTenantClassPatch" 140 tenantName := "Tenant1" 141 testClass := models.Class{ 142 Class: className, 143 MultiTenancyConfig: &models.MultiTenancyConfig{ 144 Enabled: true, 145 }, 146 Properties: []*models.Property{ 147 { 148 Name: "name", 149 DataType: schema.DataTypeText.PropString(), 150 }, 151 }, 152 } 153 tenantObject := models.Object{ 154 ID: "0927a1e0-398e-4e76-91fb-04a7a8f0405c", 155 Class: className, 156 Properties: map[string]interface{}{ 157 "name": tenantName, 158 }, 159 Tenant: tenantName, 160 } 161 162 defer func() { 163 helper.DeleteClass(t, className) 164 }() 165 166 t.Run("create class with multi-tenancy enabled", func(t *testing.T) { 167 helper.CreateClass(t, &testClass) 168 helper.CreateTenants(t, className, []*models.Tenant{{Name: tenantName}}) 169 }) 170 171 t.Run("add tenant object", func(t *testing.T) { 172 params := objects.NewObjectsCreateParams(). 173 WithBody(&tenantObject) 174 _, err := helper.Client(t).Objects.ObjectsCreate(params, nil) 175 require.Nil(t, err) 176 }) 177 178 t.Run("patch tenant object", func(t *testing.T) { 179 toUpdate := models.Object{ 180 Class: testClass.Class, 181 ID: tenantObject.ID, 182 Properties: map[string]interface{}{ 183 "name": "updatedTenantName", 184 }, 185 Tenant: "updatedTenantName", 186 } 187 params := objects.NewObjectsClassPatchParams().WithClassName(toUpdate.Class). 188 WithID(toUpdate.ID).WithBody(&toUpdate) 189 _, err := helper.Client(t).Objects.ObjectsClassPatch(params, nil) 190 require.NotNil(t, err) // tenant does not exist 191 parsedErr, ok := err.(*objects.ObjectsClassPatchUnprocessableEntity) 192 require.True(t, ok) 193 require.NotNil(t, parsedErr.Payload.Error) 194 require.Len(t, parsedErr.Payload.Error, 1) 195 assert.Contains(t, err.Error(), fmt.Sprint(http.StatusUnprocessableEntity)) 196 expected := "\"updatedTenantName\"" 197 assert.Contains(t, parsedErr.Payload.Error[0].Message, expected) 198 }) 199 }