github.com/weaviate/weaviate@v1.24.6/usecases/schema/tenant_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 schema
    13  
    14  import (
    15  	"context"
    16  	"testing"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/weaviate/weaviate/entities/models"
    20  	"github.com/weaviate/weaviate/entities/schema"
    21  )
    22  
    23  func TestAddTenants(t *testing.T) {
    24  	var (
    25  		ctx        = context.Background()
    26  		mt         = &models.MultiTenancyConfig{Enabled: true}
    27  		tenants    = []*models.Tenant{{Name: "USER1"}, {Name: "USER2"}}
    28  		cls        = "C1"
    29  		properties = []*models.Property{
    30  			{
    31  				Name:     "uUID",
    32  				DataType: schema.DataTypeText.PropString(),
    33  			},
    34  		}
    35  		repConfig = &models.ReplicationConfig{Factor: 1}
    36  	)
    37  
    38  	type test struct {
    39  		name    string
    40  		Class   string
    41  		tenants []*models.Tenant
    42  		initial *models.Class
    43  		errMsgs []string
    44  	}
    45  	tests := []test{
    46  		{
    47  			name:    "UnknownClass",
    48  			Class:   "UnknownClass",
    49  			tenants: tenants,
    50  			initial: &models.Class{
    51  				Class:              cls,
    52  				MultiTenancyConfig: mt,
    53  				Properties:         properties,
    54  				ReplicationConfig:  repConfig,
    55  			},
    56  			errMsgs: []string{ErrNotFound.Error()},
    57  		},
    58  		{
    59  			name:    "MTIsNil",
    60  			Class:   cls,
    61  			tenants: tenants,
    62  			initial: &models.Class{
    63  				Class:              cls,
    64  				MultiTenancyConfig: nil,
    65  				Properties:         properties,
    66  				ReplicationConfig:  repConfig,
    67  			},
    68  			errMsgs: []string{"not enabled"},
    69  		},
    70  		{
    71  			name:    "MTDisabled",
    72  			Class:   cls,
    73  			tenants: tenants,
    74  			initial: &models.Class{
    75  				Class:              cls,
    76  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: false},
    77  				Properties:         properties,
    78  				ReplicationConfig:  repConfig,
    79  			},
    80  			errMsgs: []string{"not enabled"},
    81  		},
    82  		{
    83  			name:    "EmptyTenantValue",
    84  			Class:   cls,
    85  			tenants: []*models.Tenant{{Name: "Aaaa"}, {Name: ""}, {Name: "Bbbb"}},
    86  			initial: &models.Class{
    87  				Class:              cls,
    88  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
    89  				Properties:         properties,
    90  				ReplicationConfig:  repConfig,
    91  			},
    92  			errMsgs: []string{"tenant"},
    93  		},
    94  		{
    95  			name:  "InvalidActivityStatus",
    96  			Class: cls,
    97  			tenants: []*models.Tenant{
    98  				{Name: "Aaaa", ActivityStatus: "DOES_NOT_EXIST_1"},
    99  				{Name: "Bbbb", ActivityStatus: "DOES_NOT_EXIST_2"},
   100  			},
   101  			initial: &models.Class{
   102  				Class:              cls,
   103  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   104  				Properties:         properties,
   105  				ReplicationConfig:  repConfig,
   106  			},
   107  			errMsgs: []string{
   108  				"invalid activity status",
   109  				"DOES_NOT_EXIST_1",
   110  				"DOES_NOT_EXIST_2",
   111  			},
   112  		},
   113  		{
   114  			name:  "UnsupportedActivityStatus",
   115  			Class: cls,
   116  			tenants: []*models.Tenant{
   117  				{Name: "Aaaa", ActivityStatus: models.TenantActivityStatusWARM},
   118  				{Name: "Bbbb", ActivityStatus: models.TenantActivityStatusFROZEN},
   119  			},
   120  			initial: &models.Class{
   121  				Class:              cls,
   122  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   123  				Properties:         properties,
   124  				ReplicationConfig:  repConfig,
   125  			},
   126  			errMsgs: []string{
   127  				"not yet supported activity status",
   128  				models.TenantActivityStatusWARM,
   129  				models.TenantActivityStatusFROZEN,
   130  			},
   131  		},
   132  		{
   133  			name:  "Success",
   134  			Class: cls,
   135  			tenants: []*models.Tenant{
   136  				{Name: "Aaaa"},
   137  				{Name: "Bbbb", ActivityStatus: models.TenantActivityStatusHOT},
   138  				{Name: "Cccc", ActivityStatus: models.TenantActivityStatusCOLD},
   139  			},
   140  			initial: &models.Class{
   141  				Class:              cls,
   142  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   143  				Properties:         properties,
   144  				ReplicationConfig:  repConfig,
   145  			},
   146  			errMsgs: []string{},
   147  		},
   148  		// TODO test with replication factor >= 2
   149  	}
   150  
   151  	// AddTenants
   152  	for _, test := range tests {
   153  		sm := newSchemaManager()
   154  		err := sm.AddClass(ctx, nil, test.initial)
   155  		if err == nil {
   156  			_, err = sm.AddTenants(ctx, nil, test.Class, test.tenants)
   157  		}
   158  		if len(test.errMsgs) == 0 {
   159  			assert.Nil(t, err)
   160  			ss := sm.schemaCache.ShardingState[test.Class]
   161  			assert.NotNil(t, ss, test.name)
   162  			assert.Equal(t, len(ss.Physical), len(test.tenants), test.name)
   163  		} else {
   164  			for _, msg := range test.errMsgs {
   165  				assert.ErrorContains(t, err, msg, test.name)
   166  			}
   167  		}
   168  	}
   169  }
   170  
   171  func TestUpdateTenants(t *testing.T) {
   172  	var (
   173  		ctx     = context.Background()
   174  		mt      = &models.MultiTenancyConfig{Enabled: true}
   175  		tenants = []*models.Tenant{
   176  			{Name: "USER1", ActivityStatus: models.TenantActivityStatusHOT},
   177  			{Name: "USER2", ActivityStatus: models.TenantActivityStatusHOT},
   178  		}
   179  		cls        = "C1"
   180  		properties = []*models.Property{
   181  			{
   182  				Name:     "uUID",
   183  				DataType: schema.DataTypeText.PropString(),
   184  			},
   185  		}
   186  		repConfig = &models.ReplicationConfig{Factor: 1}
   187  	)
   188  
   189  	type test struct {
   190  		name          string
   191  		Class         string
   192  		updateTenants []*models.Tenant
   193  		initial       *models.Class
   194  		errMsgs       []string
   195  		skipAdd       bool
   196  	}
   197  	tests := []test{
   198  		{
   199  			name:          "UnknownClass",
   200  			Class:         "UnknownClass",
   201  			updateTenants: tenants,
   202  			initial: &models.Class{
   203  				Class:              cls,
   204  				MultiTenancyConfig: mt,
   205  				Properties:         properties,
   206  				ReplicationConfig:  repConfig,
   207  			},
   208  			errMsgs: []string{ErrNotFound.Error()},
   209  		},
   210  		{
   211  			name:          "MTIsNil",
   212  			Class:         cls,
   213  			updateTenants: tenants,
   214  			initial: &models.Class{
   215  				Class:              cls,
   216  				MultiTenancyConfig: nil,
   217  				Properties:         properties,
   218  				ReplicationConfig:  repConfig,
   219  			},
   220  			errMsgs: []string{"not enabled"},
   221  			skipAdd: true,
   222  		},
   223  		{
   224  			name:          "MTDisabled",
   225  			Class:         cls,
   226  			updateTenants: tenants,
   227  			initial: &models.Class{
   228  				Class:              cls,
   229  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: false},
   230  				Properties:         properties,
   231  				ReplicationConfig:  repConfig,
   232  			},
   233  			errMsgs: []string{"not enabled"},
   234  			skipAdd: true,
   235  		},
   236  		{
   237  			name:          "EmptyTenantValue",
   238  			Class:         cls,
   239  			updateTenants: []*models.Tenant{{Name: ""}},
   240  			initial: &models.Class{
   241  				Class:              cls,
   242  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   243  				Properties:         properties,
   244  				ReplicationConfig:  repConfig,
   245  			},
   246  			errMsgs: []string{"tenant"},
   247  		},
   248  		{
   249  			name:  "InvalidActivityStatus",
   250  			Class: cls,
   251  			updateTenants: []*models.Tenant{
   252  				{Name: tenants[0].Name, ActivityStatus: "DOES_NOT_EXIST_1"},
   253  				{Name: tenants[1].Name, ActivityStatus: "DOES_NOT_EXIST_2"},
   254  			},
   255  			initial: &models.Class{
   256  				Class:              cls,
   257  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   258  				Properties:         properties,
   259  				ReplicationConfig:  repConfig,
   260  			},
   261  			errMsgs: []string{
   262  				"invalid activity status",
   263  				"DOES_NOT_EXIST_1",
   264  				"DOES_NOT_EXIST_2",
   265  			},
   266  		},
   267  		{
   268  			name:  "UnsupportedActivityStatus",
   269  			Class: cls,
   270  			updateTenants: []*models.Tenant{
   271  				{Name: tenants[0].Name, ActivityStatus: models.TenantActivityStatusWARM},
   272  				{Name: tenants[1].Name, ActivityStatus: models.TenantActivityStatusFROZEN},
   273  			},
   274  			initial: &models.Class{
   275  				Class:              cls,
   276  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   277  				Properties:         properties,
   278  				ReplicationConfig:  repConfig,
   279  			},
   280  			errMsgs: []string{
   281  				"not yet supported activity status",
   282  				models.TenantActivityStatusWARM,
   283  				models.TenantActivityStatusFROZEN,
   284  			},
   285  		},
   286  		{
   287  			name:  "EmptyActivityStatus",
   288  			Class: cls,
   289  			updateTenants: []*models.Tenant{
   290  				{Name: tenants[0].Name},
   291  				{Name: tenants[1].Name, ActivityStatus: ""},
   292  			},
   293  			initial: &models.Class{
   294  				Class:              cls,
   295  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   296  				Properties:         properties,
   297  				ReplicationConfig:  repConfig,
   298  			},
   299  			errMsgs: []string{"invalid activity status"},
   300  		},
   301  		{
   302  			name:  "Success",
   303  			Class: cls,
   304  			updateTenants: []*models.Tenant{
   305  				{Name: tenants[0].Name, ActivityStatus: models.TenantActivityStatusCOLD},
   306  				{Name: tenants[1].Name, ActivityStatus: models.TenantActivityStatusCOLD},
   307  			},
   308  			initial: &models.Class{
   309  				Class:              cls,
   310  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   311  				Properties:         properties,
   312  				ReplicationConfig:  repConfig,
   313  			},
   314  			errMsgs: []string{},
   315  		},
   316  	}
   317  
   318  	for _, test := range tests {
   319  		sm := newSchemaManager()
   320  		if err := sm.AddClass(ctx, nil, test.initial); err != nil {
   321  			t.Fatalf("%s: add class: %v", test.name, err)
   322  		}
   323  		if !test.skipAdd {
   324  			if _, err := sm.AddTenants(ctx, nil, cls, tenants); err != nil {
   325  				t.Fatalf("%s: add tenants: %v", test.name, err)
   326  			}
   327  		}
   328  
   329  		err := sm.UpdateTenants(ctx, nil, test.Class, test.updateTenants)
   330  		if len(test.errMsgs) == 0 {
   331  			if err != nil {
   332  				t.Fatalf("%s: update tenants: %v", test.name, err)
   333  			}
   334  			ss := sm.schemaCache.ShardingState[test.Class]
   335  			if ss == nil {
   336  				t.Fatalf("%s: sharding state equal nil", test.name)
   337  			}
   338  
   339  			assert.Len(t, ss.Physical, len(tenants))
   340  			for _, tenant := range test.updateTenants {
   341  				assert.Equal(t, tenant.ActivityStatus, ss.Physical[tenant.Name].Status, test.name)
   342  			}
   343  		} else {
   344  			for _, msg := range test.errMsgs {
   345  				assert.ErrorContains(t, err, msg, test.name)
   346  			}
   347  		}
   348  	}
   349  }
   350  
   351  func TestDeleteTenants(t *testing.T) {
   352  	var (
   353  		ctx     = context.Background()
   354  		tenants = []*models.Tenant{
   355  			{Name: "USER1"},
   356  			{Name: "USER2"},
   357  			{Name: "USER3"},
   358  			{Name: "USER4"},
   359  		}
   360  		cls        = "C1"
   361  		properties = []*models.Property{
   362  			{
   363  				Name:     "uUID",
   364  				DataType: schema.DataTypeText.PropString(),
   365  			},
   366  		}
   367  		repConfig = &models.ReplicationConfig{Factor: 1}
   368  	)
   369  
   370  	type test struct {
   371  		name       string
   372  		Class      string
   373  		tenants    []*models.Tenant
   374  		initial    *models.Class
   375  		errMsg     string
   376  		addTenants bool
   377  	}
   378  	tests := []test{
   379  		{
   380  			name:    "UnknownClass",
   381  			Class:   "UnknownClass",
   382  			tenants: tenants,
   383  			initial: &models.Class{
   384  				Class: cls, MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   385  				Properties:        properties,
   386  				ReplicationConfig: repConfig,
   387  			},
   388  			errMsg: ErrNotFound.Error(),
   389  		},
   390  		{
   391  			name:    "MTIsNil",
   392  			Class:   "C1",
   393  			tenants: tenants,
   394  			initial: &models.Class{
   395  				Class:              cls,
   396  				MultiTenancyConfig: nil,
   397  				Properties:         properties,
   398  				ReplicationConfig:  repConfig,
   399  			},
   400  			errMsg: "not enabled",
   401  		},
   402  		{
   403  			name:    "MTDisabled",
   404  			Class:   "C1",
   405  			tenants: tenants,
   406  			initial: &models.Class{
   407  				Class:              cls,
   408  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: false},
   409  				Properties:         properties,
   410  				ReplicationConfig:  repConfig,
   411  			},
   412  			errMsg: "not enabled",
   413  		},
   414  		{
   415  			name:    "EmptyTenantValue",
   416  			Class:   "C1",
   417  			tenants: []*models.Tenant{{Name: "Aaaa"}, {Name: ""}, {Name: "Bbbb"}},
   418  			initial: &models.Class{
   419  				Class:              cls,
   420  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   421  				Properties:         properties,
   422  				ReplicationConfig:  repConfig,
   423  			},
   424  			errMsg: "empty tenant name at index 1",
   425  		},
   426  		{
   427  			name:    "Success",
   428  			Class:   "C1",
   429  			tenants: tenants[:2],
   430  			initial: &models.Class{
   431  				Class:              cls,
   432  				MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   433  				Properties:         properties,
   434  				ReplicationConfig:  repConfig,
   435  			},
   436  			errMsg:     "",
   437  			addTenants: true,
   438  		},
   439  	}
   440  
   441  	for _, test := range tests {
   442  		sm := newSchemaManager()
   443  		err := sm.AddClass(ctx, nil, test.initial)
   444  		if err != nil {
   445  			t.Fatalf("%s: add class: %v", test.name, err)
   446  		}
   447  		if test.addTenants {
   448  			_, err = sm.AddTenants(ctx, nil, test.Class, tenants)
   449  			if err != nil {
   450  				t.Fatalf("%s: add tenants: %v", test.name, err)
   451  			}
   452  		}
   453  		var tenantNames []string
   454  		for i := range test.tenants {
   455  			tenantNames = append(tenantNames, test.tenants[i].Name)
   456  		}
   457  
   458  		err = sm.DeleteTenants(ctx, nil, test.Class, tenantNames)
   459  		if test.errMsg == "" {
   460  			if err != nil {
   461  				t.Fatalf("%s: remove tenants: %v", test.name, err)
   462  			}
   463  			ss := sm.schemaCache.ShardingState[test.Class]
   464  			if ss == nil {
   465  				t.Fatalf("%s: sharding state equal nil", test.name)
   466  			}
   467  			assert.Equal(t, len(test.tenants)+len(ss.Physical), len(tenants))
   468  		} else {
   469  			assert.ErrorContains(t, err, test.errMsg, test.name)
   470  		}
   471  
   472  	}
   473  }