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  }