github.com/weaviate/weaviate@v1.24.6/test/acceptance/replication/multi_tenancy_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 replication
    13  
    14  import (
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/go-openapi/strfmt"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	"github.com/weaviate/weaviate/client/objects"
    25  	"github.com/weaviate/weaviate/entities/models"
    26  	"github.com/weaviate/weaviate/entities/schema/crossref"
    27  	"github.com/weaviate/weaviate/test/docker"
    28  	"github.com/weaviate/weaviate/test/helper"
    29  	"github.com/weaviate/weaviate/test/helper/sample-schema/articles"
    30  	"github.com/weaviate/weaviate/usecases/replica"
    31  )
    32  
    33  const (
    34  	tenantID = strfmt.UUID("45e9e17e-8102-4011-95f0-3079ca188bbf")
    35  )
    36  
    37  func multiTenancyEnabled(t *testing.T) {
    38  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    39  	defer cancel()
    40  
    41  	compose, err := docker.New().
    42  		WithWeaviateCluster().
    43  		WithText2VecContextionary().
    44  		Start(ctx)
    45  	require.Nil(t, err)
    46  	defer func() {
    47  		if err := compose.Terminate(ctx); err != nil {
    48  			t.Fatalf("failed to terminate test containers: %s", err.Error())
    49  		}
    50  	}()
    51  
    52  	helper.SetupClient(compose.GetWeaviate().URI())
    53  	paragraphClass := articles.ParagraphsClass()
    54  	articleClass := articles.ArticlesClass()
    55  
    56  	t.Run("create schema", func(t *testing.T) {
    57  		paragraphClass.ReplicationConfig = &models.ReplicationConfig{
    58  			Factor: 2,
    59  		}
    60  		paragraphClass.MultiTenancyConfig = &models.MultiTenancyConfig{
    61  			Enabled: true,
    62  		}
    63  		helper.CreateClass(t, paragraphClass)
    64  		articleClass.ReplicationConfig = &models.ReplicationConfig{
    65  			Factor: 2,
    66  		}
    67  		articleClass.MultiTenancyConfig = &models.MultiTenancyConfig{
    68  			Enabled: true,
    69  		}
    70  		helper.CreateClass(t, articleClass)
    71  	})
    72  
    73  	t.Run("add tenants", func(t *testing.T) {
    74  		tenants := []*models.Tenant{{Name: tenantID.String()}}
    75  		helper.CreateTenants(t, paragraphClass.Class, tenants)
    76  		helper.CreateTenants(t, articleClass.Class, tenants)
    77  	})
    78  
    79  	t.Run("insert paragraphs batch", func(t *testing.T) {
    80  		t.Run("create objects on node 1", func(t *testing.T) {
    81  			batch := make([]*models.Object, len(paragraphIDs))
    82  			for i, id := range paragraphIDs {
    83  				batch[i] = articles.NewParagraph().
    84  					WithID(id).
    85  					WithContents(fmt.Sprintf("paragraph#%d", i)).
    86  					WithTenant(tenantID.String()).
    87  					Object()
    88  			}
    89  			createTenantObjects(t, compose.GetWeaviate().URI(), batch)
    90  		})
    91  
    92  		t.Run("stop node 1", func(t *testing.T) {
    93  			stopNode(ctx, t, compose, compose.GetWeaviate().Name())
    94  		})
    95  
    96  		t.Run("assert objects exist on node 2", func(t *testing.T) {
    97  			count := countTenantObjects(t, compose.GetWeaviateNode2().URI(),
    98  				"Paragraph", tenantID.String())
    99  			assert.Equal(t, int64(len(paragraphIDs)), count)
   100  		})
   101  
   102  		t.Run("restart node 1", func(t *testing.T) {
   103  			restartNode1(ctx, t, compose)
   104  		})
   105  	})
   106  
   107  	t.Run("insert articles individually", func(t *testing.T) {
   108  		t.Run("create objects on node 2", func(t *testing.T) {
   109  			for i, id := range articleIDs {
   110  				obj := articles.NewArticle().
   111  					WithID(id).
   112  					WithTitle(fmt.Sprintf("Article#%d", i)).
   113  					WithTenant(tenantID.String()).
   114  					Object()
   115  				createTenantObject(t, compose.GetWeaviateNode2().URI(), obj)
   116  			}
   117  		})
   118  
   119  		t.Run("stop node 2", func(t *testing.T) {
   120  			stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name())
   121  		})
   122  
   123  		t.Run("assert objects exist on node 1", func(t *testing.T) {
   124  			count := countTenantObjects(t, compose.GetWeaviate().URI(),
   125  				"Article", tenantID.String())
   126  			assert.Equal(t, int64(len(articleIDs)), count)
   127  		})
   128  
   129  		t.Run("restart node 2", func(t *testing.T) {
   130  			err = compose.Start(ctx, compose.GetWeaviateNode2().Name())
   131  			require.Nil(t, err)
   132  		})
   133  	})
   134  
   135  	t.Run("add references", func(t *testing.T) {
   136  		refs := make([]*models.BatchReference, len(articleIDs))
   137  		for i := range articleIDs {
   138  			refs[i] = &models.BatchReference{
   139  				From:   strfmt.URI(crossref.NewSource("Article", "hasParagraphs", articleIDs[i]).String()),
   140  				To:     strfmt.URI(crossref.NewLocalhost("Paragraph", paragraphIDs[i]).String()),
   141  				Tenant: tenantID.String(),
   142  			}
   143  		}
   144  
   145  		t.Run("add references to node 1", func(t *testing.T) {
   146  			addTenantReferences(t, compose.GetWeaviate().URI(), refs)
   147  		})
   148  
   149  		t.Run("stop node 1", func(t *testing.T) {
   150  			stopNode(ctx, t, compose, compose.GetWeaviate().Name())
   151  		})
   152  
   153  		t.Run("assert references were added successfully to node 2", func(t *testing.T) {
   154  			type additional struct {
   155  				ID strfmt.UUID `json:"id"`
   156  			}
   157  
   158  			type article struct {
   159  				Additional    additional `json:"_additional"`
   160  				HasParagraphs []struct {
   161  					Additional additional `json:"_additional"`
   162  				} `json:"hasParagraphs"`
   163  			}
   164  
   165  			// maps article id to referenced paragraph id
   166  			refPairs := make(map[strfmt.UUID]strfmt.UUID)
   167  			resp := gqlTenantGet(t, compose.GetWeaviateNode2().URI(), "Article", replica.One,
   168  				tenantID.String(), "_additional{id}", "hasParagraphs {... on Paragraph {_additional{id}}}")
   169  			assert.Len(t, resp, len(articleIDs))
   170  
   171  			for _, r := range resp {
   172  				b, err := json.Marshal(r)
   173  				require.Nil(t, err)
   174  				var art article
   175  				err = json.Unmarshal(b, &art)
   176  				require.Nil(t, err)
   177  				require.Len(t, art.HasParagraphs, 1)
   178  				refPairs[art.Additional.ID] = art.HasParagraphs[0].Additional.ID
   179  			}
   180  
   181  			for i := range articleIDs {
   182  				paragraphID, ok := refPairs[articleIDs[i]]
   183  				require.True(t, ok, "expected %q to be in refPairs: %+v", articleIDs[i], refPairs)
   184  				assert.Equal(t, paragraphIDs[i], paragraphID)
   185  			}
   186  		})
   187  
   188  		t.Run("restart node 1", func(t *testing.T) {
   189  			restartNode1(ctx, t, compose)
   190  		})
   191  	})
   192  
   193  	t.Run("patch an object", func(t *testing.T) {
   194  		before, err := getTenantObject(t, compose.GetWeaviate().URI(), "Article", articleIDs[0], tenantID.String())
   195  		require.Nil(t, err)
   196  		newTitle := "Article#9000"
   197  
   198  		t.Run("execute object patch on node 2", func(t *testing.T) {
   199  			patch := &models.Object{
   200  				ID:         before.ID,
   201  				Class:      "Article",
   202  				Properties: map[string]interface{}{"title": newTitle},
   203  				Tenant:     tenantID.String(),
   204  			}
   205  			patchTenantObject(t, compose.GetWeaviateNode2().URI(), patch)
   206  		})
   207  
   208  		t.Run("stop node 2", func(t *testing.T) {
   209  			stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name())
   210  		})
   211  
   212  		t.Run("assert object is patched on node 1", func(t *testing.T) {
   213  			after, err := getTenantObjectFromNode(t, compose.GetWeaviate().URI(),
   214  				"Article", articleIDs[0], "node1", tenantID.String())
   215  			require.Nil(t, err)
   216  
   217  			newVal, ok := after.Properties.(map[string]interface{})["title"]
   218  			require.True(t, ok)
   219  			assert.Equal(t, newTitle, newVal)
   220  		})
   221  
   222  		t.Run("restart node 2", func(t *testing.T) {
   223  			err = compose.Start(ctx, compose.GetWeaviateNode2().Name())
   224  			require.Nil(t, err)
   225  		})
   226  	})
   227  
   228  	t.Run("delete an object", func(t *testing.T) {
   229  		t.Run("execute delete object on node 1", func(t *testing.T) {
   230  			deleteTenantObject(t, compose.GetWeaviate().URI(), "Article", articleIDs[0], tenantID.String())
   231  		})
   232  
   233  		t.Run("stop node 1", func(t *testing.T) {
   234  			stopNode(ctx, t, compose, compose.GetWeaviate().Name())
   235  		})
   236  
   237  		t.Run("assert object removed from node 2", func(t *testing.T) {
   238  			_, err := getTenantObjectFromNode(t, compose.GetWeaviateNode2().URI(),
   239  				"Article", articleIDs[0], "node2", tenantID.String())
   240  			assert.Equal(t, &objects.ObjectsClassGetNotFound{}, err)
   241  		})
   242  
   243  		t.Run("restart node 1", func(t *testing.T) {
   244  			restartNode1(ctx, t, compose)
   245  		})
   246  	})
   247  
   248  	t.Run("batch delete all objects", func(t *testing.T) {
   249  		t.Run("execute batch delete on node 2", func(t *testing.T) {
   250  			deleteTenantObjects(t, compose.GetWeaviateNode2().URI(),
   251  				"Article", []string{"title"}, "Article#*", tenantID.String())
   252  		})
   253  
   254  		t.Run("stop node 2", func(t *testing.T) {
   255  			stopNode(ctx, t, compose, compose.GetWeaviateNode2().Name())
   256  		})
   257  
   258  		t.Run("assert objects are removed from node 1", func(t *testing.T) {
   259  			count := countTenantObjects(t, compose.GetWeaviate().URI(), "Article", tenantID.String())
   260  			assert.Zero(t, count)
   261  		})
   262  
   263  		t.Run("restart node 2", func(t *testing.T) {
   264  			err = compose.Start(ctx, compose.GetWeaviateNode2().Name())
   265  			require.Nil(t, err)
   266  		})
   267  	})
   268  }