github.com/weaviate/weaviate@v1.24.6/test/acceptance/multi_tenancy/create_and_delete_tenants_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/nodes"
    20  	"github.com/weaviate/weaviate/entities/models"
    21  	"github.com/weaviate/weaviate/entities/schema"
    22  	"github.com/weaviate/weaviate/entities/verbosity"
    23  	"github.com/weaviate/weaviate/test/helper"
    24  )
    25  
    26  var verbose = verbosity.OutputVerbose
    27  
    28  func TestCreateTenants(t *testing.T) {
    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  		},
    40  	}
    41  
    42  	t.Run("Create tenant", func(Z *testing.T) {
    43  		expectedTenants := []string{
    44  			"Tenant1", "Tenant2", "Tenant3",
    45  		}
    46  
    47  		defer func() {
    48  			helper.DeleteClass(t, testClass.Class)
    49  		}()
    50  		helper.CreateClass(t, &testClass)
    51  
    52  		tenants := make([]*models.Tenant, len(expectedTenants))
    53  		for i := range tenants {
    54  			tenants[i] = &models.Tenant{
    55  				Name:           expectedTenants[i],
    56  				ActivityStatus: models.TenantActivityStatusHOT,
    57  			}
    58  		}
    59  		helper.CreateTenants(t, testClass.Class, tenants)
    60  
    61  		respGet, errGet := helper.GetTenants(t, testClass.Class)
    62  		require.Nil(t, errGet)
    63  		require.NotNil(t, respGet)
    64  		require.ElementsMatch(t, respGet.Payload, tenants)
    65  
    66  		resp, err := helper.Client(t).Nodes.NodesGet(nodes.NewNodesGetParams().WithOutput(&verbose), nil)
    67  		require.Nil(t, err)
    68  		require.NotNil(t, resp.Payload)
    69  		require.NotNil(t, resp.Payload.Nodes)
    70  		require.Len(t, resp.Payload.Nodes, 1)
    71  		require.Len(t, resp.Payload.Nodes[0].Shards, 3)
    72  
    73  		var foundTenants []string
    74  		for _, found := range resp.Payload.Nodes[0].Shards {
    75  			assert.Equal(t, testClass.Class, found.Class)
    76  			// Creating a tenant alone should not result in a loaded shard.
    77  			// This check also ensures that the nods api did not cause a
    78  			// force load.
    79  			assert.False(t, found.Loaded)
    80  			foundTenants = append(foundTenants, found.Name)
    81  		}
    82  		assert.ElementsMatch(t, expectedTenants, foundTenants)
    83  	})
    84  
    85  	t.Run("Create duplicate tenant once", func(Z *testing.T) {
    86  		defer func() {
    87  			helper.DeleteClass(t, testClass.Class)
    88  		}()
    89  		helper.CreateClass(t, &testClass)
    90  		err := helper.CreateTenantsReturnError(t, testClass.Class, []*models.Tenant{{Name: "DoubleTenant"}, {Name: "DoubleTenant"}})
    91  		require.Nil(t, err)
    92  
    93  		// only added once
    94  		respGet, errGet := helper.GetTenants(t, testClass.Class)
    95  		require.Nil(t, errGet)
    96  		require.NotNil(t, respGet)
    97  		require.Len(t, respGet.Payload, 1)
    98  	})
    99  
   100  	t.Run("Create same tenant multiple times", func(Z *testing.T) {
   101  		defer func() {
   102  			helper.DeleteClass(t, testClass.Class)
   103  		}()
   104  		helper.CreateClass(t, &testClass)
   105  		helper.CreateTenants(t, testClass.Class, []*models.Tenant{{Name: "AddTenantAgain"}})
   106  
   107  		// idempotent operation
   108  		err := helper.CreateTenantsReturnError(t, testClass.Class, []*models.Tenant{{Name: "AddTenantAgain"}})
   109  		require.Nil(t, err)
   110  	})
   111  }
   112  
   113  func TestDeleteTenants(t *testing.T) {
   114  	testClass := models.Class{
   115  		Class:              "MultiTenantClassDelete",
   116  		MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   117  	}
   118  
   119  	defer func() {
   120  		helper.DeleteClass(t, testClass.Class)
   121  	}()
   122  	helper.CreateClass(t, &testClass)
   123  
   124  	tenants := []*models.Tenant{
   125  		{Name: "tenant1"},
   126  		{Name: "tenant2"},
   127  		{Name: "tenant3"},
   128  		{Name: "tenant4"},
   129  	}
   130  	helper.CreateTenants(t, testClass.Class, tenants)
   131  
   132  	t.Run("Delete same tenant multiple times", func(t *testing.T) {
   133  		err := helper.DeleteTenants(t, testClass.Class, []string{"tenant4"})
   134  		require.Nil(t, err)
   135  
   136  		// deleted once
   137  		resp, err := helper.Client(t).Nodes.NodesGet(nodes.NewNodesGetParams().WithOutput(&verbose), nil)
   138  		require.Nil(t, err)
   139  		require.NotNil(t, resp.Payload)
   140  		require.NotNil(t, resp.Payload.Nodes)
   141  		require.Len(t, resp.Payload.Nodes, 1)
   142  		for _, shard := range resp.Payload.Nodes[0].Shards {
   143  			// Creating a tenant alone should not result in a loaded shard.
   144  			// This check also ensures that the nods api did not cause a
   145  			// force load.
   146  			assert.False(t, shard.Loaded)
   147  			assert.NotEqual(t, "tenant4", shard.Name)
   148  		}
   149  
   150  		// idempotent operation
   151  		err = helper.DeleteTenants(t, testClass.Class, []string{"tenant4"})
   152  		require.Nil(t, err)
   153  	})
   154  
   155  	t.Run("Delete duplicate tenant once", func(Z *testing.T) {
   156  		err := helper.DeleteTenants(t, testClass.Class, []string{"tenant1", "tenant1"})
   157  		// idempotent operation
   158  		require.Nil(t, err)
   159  
   160  		// deleted once
   161  		resp, err := helper.Client(t).Nodes.NodesGet(nodes.NewNodesGetParams().WithOutput(&verbose), nil)
   162  		require.Nil(t, err)
   163  		require.NotNil(t, resp.Payload)
   164  		require.NotNil(t, resp.Payload.Nodes)
   165  		require.Len(t, resp.Payload.Nodes, 1)
   166  		require.Len(t, resp.Payload.Nodes[0].Shards, 2)
   167  	})
   168  
   169  	t.Run("Delete non-existent tenant alongside existing", func(Z *testing.T) {
   170  		err := helper.DeleteTenants(t, testClass.Class, []string{"tenant1", "tenant5"})
   171  		require.Nil(t, err)
   172  
   173  		// idempotent - deleting multiple times works - tenant1 is removed
   174  		resp, err := helper.Client(t).Nodes.NodesGet(nodes.NewNodesGetParams().WithOutput(&verbose), nil)
   175  		require.Nil(t, err)
   176  		require.NotNil(t, resp.Payload)
   177  		require.NotNil(t, resp.Payload.Nodes)
   178  		require.Len(t, resp.Payload.Nodes, 1)
   179  		require.Len(t, resp.Payload.Nodes[0].Shards, 2)
   180  	})
   181  
   182  	t.Run("Delete tenants", func(Z *testing.T) {
   183  		err := helper.DeleteTenants(t, testClass.Class, []string{"tenant1", "tenant3"})
   184  		require.Nil(t, err)
   185  
   186  		// successfully deleted
   187  		resp, err := helper.Client(t).Nodes.NodesGet(nodes.NewNodesGetParams().WithOutput(&verbose), nil)
   188  		require.Nil(t, err)
   189  		require.NotNil(t, resp.Payload)
   190  		require.NotNil(t, resp.Payload.Nodes)
   191  		require.Len(t, resp.Payload.Nodes, 1)
   192  		require.Len(t, resp.Payload.Nodes[0].Shards, 1)
   193  	})
   194  }
   195  
   196  func TestTenantsNonMultiTenant(t *testing.T) {
   197  	testClass := models.Class{
   198  		Class: "TenantsNoMultiClass",
   199  		MultiTenancyConfig: &models.MultiTenancyConfig{
   200  			Enabled: false,
   201  		},
   202  	}
   203  	defer func() {
   204  		helper.DeleteClass(t, testClass.Class)
   205  	}()
   206  	helper.CreateClass(t, &testClass)
   207  
   208  	err := helper.CreateTenantsReturnError(t, testClass.Class, []*models.Tenant{{Name: "doesNotMatter"}})
   209  	require.NotNil(t, err)
   210  
   211  	_, err = helper.GetTenants(t, testClass.Class)
   212  	require.NotNil(t, err)
   213  
   214  	err = helper.DeleteTenants(t, testClass.Class, []string{"doesNotMatter"})
   215  	require.NotNil(t, err)
   216  }
   217  
   218  func TestTenantsClassDoesNotExist(t *testing.T) {
   219  	err := helper.CreateTenantsReturnError(t, "DoesNotExist", []*models.Tenant{{Name: "doesNotMatter"}})
   220  	require.NotNil(t, err)
   221  
   222  	_, err = helper.GetTenants(t, "DoesNotExist")
   223  	require.NotNil(t, err)
   224  
   225  	err = helper.DeleteTenants(t, "DoesNotExist", []string{"doesNotMatter"})
   226  	require.NotNil(t, err)
   227  }