github.com/weaviate/weaviate@v1.24.6/usecases/objects/references_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 objects
    13  
    14  import (
    15  	"context"
    16  	"errors"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/go-openapi/strfmt"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/mock"
    23  	"github.com/stretchr/testify/require"
    24  	"github.com/weaviate/weaviate/entities/additional"
    25  	"github.com/weaviate/weaviate/entities/models"
    26  	"github.com/weaviate/weaviate/entities/schema"
    27  	"github.com/weaviate/weaviate/entities/schema/crossref"
    28  	"github.com/weaviate/weaviate/entities/search"
    29  	"github.com/weaviate/weaviate/entities/vectorindex/hnsw"
    30  )
    31  
    32  func Test_ReferencesAddDeprecated(t *testing.T) {
    33  	cls := "Zoo"
    34  	id := strfmt.UUID("my-id")
    35  	t.Run("without prior refs", func(t *testing.T) {
    36  		req := AddReferenceInput{
    37  			ID:       id,
    38  			Property: "hasAnimals",
    39  			Ref: models.SingleRef{
    40  				Beacon: strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7"),
    41  			},
    42  		}
    43  		m := newFakeGetManager(zooAnimalSchemaForTest())
    44  		m.repo.On("Exists", "Animal", mock.Anything).Return(true, nil)
    45  		m.repo.On("ObjectByID", mock.Anything, mock.Anything, mock.Anything).Return(&search.Result{
    46  			ClassName: cls,
    47  			Schema: map[string]interface{}{
    48  				"name": "MyZoo",
    49  			},
    50  		}, nil)
    51  		expectedRefProperty := "hasAnimals"
    52  		source := crossref.NewSource(schema.ClassName(cls), schema.PropertyName(expectedRefProperty), id)
    53  		target := crossref.New("localhost", "Animal", "d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
    54  		m.repo.On("AddReference", source, target).Return(nil)
    55  		m.modulesProvider.On("UsingRef2Vec", mock.Anything).Return(false)
    56  
    57  		err := m.AddObjectReference(context.Background(), nil, &req, nil, "")
    58  		require.Nil(t, err)
    59  		m.repo.AssertExpectations(t)
    60  	})
    61  	t.Run("source object missing", func(t *testing.T) {
    62  		req := AddReferenceInput{
    63  			ID:       strfmt.UUID("my-id"),
    64  			Property: "hasAnimals",
    65  			Ref: models.SingleRef{
    66  				Beacon: strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7"),
    67  			},
    68  		}
    69  		m := newFakeGetManager(zooAnimalSchemaForTest())
    70  		m.repo.On("ObjectByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
    71  		err := m.AddObjectReference(context.Background(), nil, &req, nil, "")
    72  		require.NotNil(t, err)
    73  		if !err.BadRequest() {
    74  			t.Errorf("error expected: not found error got: %v", err)
    75  		}
    76  	})
    77  	t.Run("source object missing", func(t *testing.T) {
    78  		req := AddReferenceInput{
    79  			ID:       strfmt.UUID("my-id"),
    80  			Property: "hasAnimals",
    81  			Ref: models.SingleRef{
    82  				Beacon: strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7"),
    83  			},
    84  		}
    85  		m := newFakeGetManager(zooAnimalSchemaForTest())
    86  		m.repo.On("ObjectByID", mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("any"))
    87  		err := m.AddObjectReference(context.Background(), nil, &req, nil, "")
    88  		require.NotNil(t, err)
    89  		if err.Code != StatusInternalServerError {
    90  			t.Errorf("error expected: internal error, got: %v", err)
    91  		}
    92  	})
    93  }
    94  
    95  func Test_ReferenceAdd(t *testing.T) {
    96  	t.Parallel()
    97  	var (
    98  		cls    = "Zoo"
    99  		prop   = "hasAnimals"
   100  		id     = strfmt.UUID("d18c8e5e-000-0000-0000-56b0cfe33ce7")
   101  		refID  = strfmt.UUID("d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
   102  		uri    = strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
   103  		anyErr = errors.New("any")
   104  		ref    = models.SingleRef{Beacon: uri}
   105  		req    = AddReferenceInput{
   106  			Class:    cls,
   107  			ID:       id,
   108  			Property: prop,
   109  			Ref:      ref,
   110  		}
   111  		source = crossref.NewSource(schema.ClassName(cls), schema.PropertyName(prop), id)
   112  		target = crossref.New("localhost", "Animal", refID)
   113  	)
   114  
   115  	tests := []struct {
   116  		Name string
   117  		// inputs
   118  		Req AddReferenceInput
   119  
   120  		// outputs
   121  		ExpectedRef models.SingleRef
   122  		WantCode    int
   123  		WantErr     error
   124  		SrcNotFound bool
   125  		// control errors
   126  		ErrAddRef       error
   127  		ErrTargetExists error
   128  		ErrSrcExists    error
   129  		ErrAuth         error
   130  		ErrLock         error
   131  		ErrSchema       error
   132  		// Stage: 1 -> validation(), 2 -> target exists(), 3 -> source exists(), 4 -> AddReference()
   133  		Stage int
   134  	}{
   135  		{
   136  			Name: "locking", Req: req, Stage: 0,
   137  			WantCode: StatusInternalServerError, WantErr: anyErr, ErrLock: anyErr,
   138  		},
   139  		{
   140  			Name: "authorization", Req: req, Stage: 0,
   141  			WantCode: StatusForbidden, WantErr: anyErr, ErrAuth: anyErr,
   142  		},
   143  		{
   144  			Name: "get schema",
   145  			Req:  req, Stage: 1,
   146  			ErrSchema: anyErr,
   147  			WantCode:  StatusBadRequest,
   148  		},
   149  		{
   150  			Name: "empty data type",
   151  			Req:  AddReferenceInput{Class: cls, ID: id, Property: "emptyType", Ref: ref}, Stage: 1,
   152  			WantCode: StatusBadRequest,
   153  		},
   154  		{
   155  			Name: "primitive data type",
   156  			Req:  AddReferenceInput{Class: cls, ID: id, Property: "name", Ref: ref}, Stage: 1,
   157  			WantCode: StatusBadRequest,
   158  		},
   159  		{
   160  			Name: "unknown property",
   161  			Req:  AddReferenceInput{Class: cls, ID: id, Property: "unknown", Ref: ref}, Stage: 1,
   162  			WantCode: StatusBadRequest,
   163  		},
   164  		{
   165  			Name: "valid class name",
   166  			Req:  AddReferenceInput{Class: "-", ID: id, Property: prop}, Stage: 1,
   167  			WantCode: StatusBadRequest,
   168  		},
   169  		{
   170  			Name: "reserved property name",
   171  			Req:  AddReferenceInput{Class: cls, ID: id, Property: "_id"}, Stage: 1,
   172  			WantCode: StatusBadRequest,
   173  		},
   174  		{
   175  			Name: "valid property name",
   176  			Req:  AddReferenceInput{Class: cls, ID: id, Property: "-"}, Stage: 1,
   177  			WantCode: StatusBadRequest,
   178  		},
   179  
   180  		{Name: "add valid reference", Req: req, Stage: 4},
   181  		{
   182  			Name: "referenced class not found", Req: req, Stage: 2,
   183  			WantCode:        StatusBadRequest,
   184  			ErrTargetExists: anyErr,
   185  			WantErr:         anyErr,
   186  		},
   187  		{
   188  			Name: "source object internal error", Req: req, Stage: 3,
   189  			WantCode:     StatusInternalServerError,
   190  			ErrSrcExists: anyErr,
   191  			WantErr:      anyErr,
   192  		},
   193  		{
   194  			Name: "source object missing", Req: req, Stage: 3,
   195  			WantCode:    StatusNotFound,
   196  			SrcNotFound: true,
   197  		},
   198  		{
   199  			Name: "internal error", Req: req, Stage: 4,
   200  			WantCode:  StatusInternalServerError,
   201  			ErrAddRef: anyErr,
   202  			WantErr:   anyErr,
   203  		},
   204  	}
   205  
   206  	for _, tc := range tests {
   207  		t.Run(tc.Name, func(t *testing.T) {
   208  			m := newFakeGetManager(zooAnimalSchemaForTest())
   209  			m.authorizer.Err = tc.ErrAuth
   210  			m.locks.Err = tc.ErrLock
   211  			m.schemaManager.(*fakeSchemaManager).GetschemaErr = tc.ErrSchema
   212  			m.modulesProvider.On("UsingRef2Vec", mock.Anything).Return(false)
   213  			if tc.Stage >= 2 {
   214  				m.repo.On("Exists", "Animal", refID).Return(true, tc.ErrTargetExists).Once()
   215  			}
   216  			if tc.Stage >= 3 {
   217  				m.repo.On("Exists", tc.Req.Class, tc.Req.ID).Return(!tc.SrcNotFound, tc.ErrSrcExists).Once()
   218  			}
   219  			if tc.Stage >= 4 {
   220  				m.repo.On("AddReference", source, target).Return(tc.ErrAddRef).Once()
   221  			}
   222  
   223  			err := m.AddObjectReference(context.Background(), nil, &tc.Req, nil, "")
   224  			if tc.WantCode != 0 {
   225  				code := 0
   226  				if err != nil {
   227  					code = err.Code
   228  				}
   229  				if code != tc.WantCode {
   230  					t.Fatalf("code expected: %v, got %v", tc.WantCode, code)
   231  				}
   232  
   233  				if tc.WantErr != nil && !errors.Is(err, tc.WantErr) {
   234  					t.Errorf("wrapped error expected: %v, got %v", tc.WantErr, err.Err)
   235  				}
   236  
   237  			}
   238  			m.repo.AssertExpectations(t)
   239  		})
   240  	}
   241  }
   242  
   243  func Test_ReferenceUpdate(t *testing.T) {
   244  	t.Parallel()
   245  	var (
   246  		cls    = "Zoo"
   247  		prop   = "hasAnimals"
   248  		id     = strfmt.UUID("d18c8e5e-000-0000-0000-56b0cfe33ce7")
   249  		refID  = strfmt.UUID("d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
   250  		uri    = strfmt.URI("weaviate://localhost/Animals/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
   251  		anyErr = errors.New("any")
   252  		refs   = models.MultipleRef{&models.SingleRef{Beacon: uri, Class: "Animals"}}
   253  		req    = PutReferenceInput{
   254  			Class:    cls,
   255  			ID:       id,
   256  			Property: prop,
   257  			Refs:     refs,
   258  		}
   259  	)
   260  
   261  	tests := []struct {
   262  		Name string
   263  		// inputs
   264  		Req PutReferenceInput
   265  
   266  		// outputs
   267  		ExpectedRef models.SingleRef
   268  		WantCode    int
   269  		WantErr     error
   270  		SrcNotFound bool
   271  		// control errors
   272  		ErrPutRefs      error
   273  		ErrTargetExists error
   274  		ErrSrcExists    error
   275  		ErrAuth         error
   276  		ErrLock         error
   277  		ErrSchema       error
   278  		// Stage: 1 -> validation(), 2 -> target exists(), 3 -> PutObject()
   279  		Stage int
   280  	}{
   281  		{
   282  			Name: "source object internal error", Req: req,
   283  			WantCode:     StatusInternalServerError,
   284  			ErrSrcExists: anyErr,
   285  			WantErr:      NewErrInternal("repo: object by id: %v", anyErr),
   286  			Stage:        1,
   287  		},
   288  		{
   289  			Name: "source object missing", Req: req,
   290  			WantCode:    StatusNotFound,
   291  			SrcNotFound: true,
   292  			Stage:       1,
   293  		},
   294  		{
   295  			Name: "locking", Req: req,
   296  			WantCode: StatusInternalServerError, WantErr: anyErr, ErrLock: anyErr,
   297  			Stage: 1,
   298  		},
   299  		{
   300  			Name: "authorization", Req: req,
   301  			WantCode: StatusForbidden, WantErr: anyErr, ErrAuth: anyErr,
   302  			Stage: 1,
   303  		},
   304  		{
   305  			Name: "get schema",
   306  			Req:  req, Stage: 1,
   307  			ErrSchema: anyErr,
   308  			WantCode:  StatusBadRequest,
   309  		},
   310  		{
   311  			Name: "empty data type",
   312  			Req:  PutReferenceInput{Class: cls, ID: id, Property: "emptyType", Refs: refs}, Stage: 1,
   313  			WantCode: StatusBadRequest,
   314  		},
   315  		{
   316  			Name: "primitive data type",
   317  			Req:  PutReferenceInput{Class: cls, ID: id, Property: "name", Refs: refs}, Stage: 1,
   318  			WantCode: StatusBadRequest,
   319  		},
   320  		{
   321  			Name: "unknown property",
   322  			Req:  PutReferenceInput{Class: cls, ID: id, Property: "unknown", Refs: refs}, Stage: 1,
   323  			WantCode: StatusBadRequest,
   324  		},
   325  		{
   326  			Name: "reserved property name",
   327  			Req:  PutReferenceInput{Class: cls, ID: id, Property: "_id", Refs: refs}, Stage: 1,
   328  			WantCode: StatusBadRequest,
   329  		},
   330  		{
   331  			Name: "valid property name",
   332  			Req:  PutReferenceInput{Class: cls, ID: id, Property: "-", Refs: refs}, Stage: 1,
   333  			WantCode: StatusBadRequest,
   334  		},
   335  
   336  		{Name: "update valid reference", Req: req, Stage: 3},
   337  		{
   338  			Name: "referenced class not found", Req: req, Stage: 2,
   339  			WantCode:        StatusBadRequest,
   340  			ErrTargetExists: anyErr,
   341  			WantErr:         anyErr,
   342  		},
   343  		{
   344  			Name: "internal error", Req: req, Stage: 3,
   345  			WantCode:   StatusInternalServerError,
   346  			ErrPutRefs: anyErr,
   347  			WantErr:    anyErr,
   348  		},
   349  	}
   350  
   351  	for _, tc := range tests {
   352  		t.Run(tc.Name, func(t *testing.T) {
   353  			m := newFakeGetManager(zooAnimalSchemaForTest())
   354  			m.authorizer.Err = tc.ErrAuth
   355  			m.locks.Err = tc.ErrLock
   356  			m.schemaManager.(*fakeSchemaManager).GetschemaErr = tc.ErrSchema
   357  			srcObj := &search.Result{
   358  				ClassName: cls,
   359  				Schema: map[string]interface{}{
   360  					"name": "MyZoo",
   361  				},
   362  			}
   363  			if tc.SrcNotFound {
   364  				srcObj = nil
   365  			}
   366  			if tc.Stage >= 1 {
   367  				m.repo.On("Object", cls, id, mock.Anything, mock.Anything, "").Return(srcObj, tc.ErrSrcExists)
   368  			}
   369  
   370  			if tc.Stage >= 2 {
   371  				m.repo.On("Exists", "Animals", refID).Return(true, tc.ErrTargetExists).Once()
   372  			}
   373  
   374  			if tc.Stage >= 3 {
   375  				m.repo.On("PutObject", mock.Anything, mock.Anything).Return(tc.ErrPutRefs).Once()
   376  			}
   377  
   378  			err := m.UpdateObjectReferences(context.Background(), nil, &tc.Req, nil, "")
   379  			if tc.WantCode != 0 {
   380  				code := 0
   381  				if err != nil {
   382  					code = err.Code
   383  				}
   384  				if code != tc.WantCode {
   385  					t.Fatalf("code expected: %v, got %v", tc.WantCode, code)
   386  				}
   387  
   388  				if tc.WantErr != nil && !errors.Is(err, tc.WantErr) {
   389  					t.Errorf("wrapped error expected: %v, got %v", tc.WantErr, err.Err)
   390  				}
   391  
   392  			}
   393  			m.repo.AssertExpectations(t)
   394  		})
   395  	}
   396  }
   397  
   398  func Test_ReferenceDelete(t *testing.T) {
   399  	t.Parallel()
   400  	var (
   401  		cls    = "Zoo"
   402  		prop   = "hasAnimals"
   403  		id     = strfmt.UUID("d18c8e5e-000-0000-0000-56b0cfe33ce7")
   404  		uri    = strfmt.URI("weaviate://localhost/Animal/d18c8e5e-a339-4c15-8af6-56b0cfe33ce7")
   405  		anyErr = errors.New("any")
   406  		ref    = models.SingleRef{Beacon: uri}
   407  		ref2   = &models.SingleRef{Beacon: strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce5")}
   408  		ref3   = &models.SingleRef{Beacon: strfmt.URI("weaviate://localhost/d18c8e5e-a339-4c15-8af6-56b0cfe33ce6")}
   409  		req    = DeleteReferenceInput{
   410  			Class:     cls,
   411  			ID:        id,
   412  			Property:  prop,
   413  			Reference: ref,
   414  		}
   415  	)
   416  
   417  	fakeProperties := func(refs ...*models.SingleRef) map[string]interface{} {
   418  		mrefs := make(models.MultipleRef, len(refs))
   419  		copy(mrefs, refs)
   420  		return map[string]interface{}{
   421  			"name": "MyZoo",
   422  			prop:   mrefs,
   423  		}
   424  	}
   425  
   426  	tests := []struct {
   427  		Name string
   428  		// inputs
   429  		Req           DeleteReferenceInput
   430  		properties    interface{}
   431  		NewSrcRefsLen int
   432  		// outputs
   433  		ExpectedRef models.SingleRef
   434  		WantCode    int
   435  		WantErr     error
   436  		SrcNotFound bool
   437  		// control errors
   438  		ErrPutRefs      error
   439  		ErrTargetExists error
   440  		ErrSrcExists    error
   441  		ErrAuth         error
   442  		ErrLock         error
   443  		ErrSchema       error
   444  		// Stage: 1 -> validation(), 2 -> target exists(), 3 -> PutObject()
   445  		Stage int
   446  	}{
   447  		{
   448  			Name: "source object internal error", Req: req,
   449  			WantCode:     StatusInternalServerError,
   450  			ErrSrcExists: anyErr,
   451  			WantErr:      NewErrInternal("repo: object by id: %v", anyErr), Stage: 2,
   452  		},
   453  		{
   454  			Name: "source object missing", Req: req,
   455  			WantCode:    StatusNotFound,
   456  			SrcNotFound: true, Stage: 2,
   457  		},
   458  		{
   459  			Name: "locking", Req: req,
   460  			WantCode: StatusInternalServerError, WantErr: anyErr, ErrLock: anyErr, Stage: 2,
   461  		},
   462  		{
   463  			Name: "authorization", Req: req,
   464  			WantCode: StatusForbidden, WantErr: anyErr, ErrAuth: anyErr, Stage: 2,
   465  		},
   466  		{
   467  			Name: "get schema",
   468  			Req:  req, Stage: 2,
   469  			ErrSchema: anyErr,
   470  			WantCode:  StatusBadRequest,
   471  		},
   472  		{
   473  			Name: "empty data type",
   474  			Req:  DeleteReferenceInput{Class: cls, ID: id, Property: "emptyType", Reference: ref}, Stage: 2,
   475  			WantCode: StatusBadRequest,
   476  		},
   477  		{
   478  			Name: "primitive data type",
   479  			Req:  DeleteReferenceInput{Class: cls, ID: id, Property: "name", Reference: ref}, Stage: 2,
   480  			WantCode: StatusBadRequest,
   481  		},
   482  		{
   483  			Name: "unknown property",
   484  			Req:  DeleteReferenceInput{Class: cls, ID: id, Property: "unknown", Reference: ref}, Stage: 2,
   485  			WantCode: StatusBadRequest,
   486  		},
   487  		{
   488  			Name: "reserved property name",
   489  			Req:  DeleteReferenceInput{Class: cls, ID: id, Property: "_id"}, Stage: 1,
   490  			WantCode: StatusBadRequest,
   491  		},
   492  		{
   493  			Name: "valid property name",
   494  			Req:  DeleteReferenceInput{Class: cls, ID: id, Property: "-"}, Stage: 1,
   495  			WantCode: StatusBadRequest,
   496  		},
   497  		{
   498  			Name:       "delete one reference",
   499  			Req:        req,
   500  			properties: fakeProperties(ref2, &ref, ref3), NewSrcRefsLen: 2,
   501  			Stage: 3,
   502  		},
   503  		{
   504  			Name:       "delete two references",
   505  			Req:        req,
   506  			properties: fakeProperties(&ref, ref2, &ref), NewSrcRefsLen: 1,
   507  			Stage: 3,
   508  		},
   509  		{
   510  			Name:       "delete all references",
   511  			Req:        req,
   512  			properties: fakeProperties(&ref, &ref), NewSrcRefsLen: 0,
   513  			Stage: 3,
   514  		},
   515  		{
   516  			Name:       "reference not found",
   517  			Req:        req,
   518  			properties: fakeProperties(ref2, ref3), NewSrcRefsLen: 2,
   519  			Stage: 2,
   520  		},
   521  		{
   522  			Name:       "wrong reference type",
   523  			Req:        req,
   524  			properties: map[string]interface{}{prop: "wrong reference type"}, NewSrcRefsLen: 0,
   525  			Stage: 2,
   526  		},
   527  		{
   528  			Name:       "empty properties list",
   529  			Req:        req,
   530  			properties: nil, NewSrcRefsLen: 0,
   531  			Stage: 2,
   532  		},
   533  		{
   534  			Name:       "internal error",
   535  			Req:        req,
   536  			properties: fakeProperties(ref2, &ref, ref3), NewSrcRefsLen: 3,
   537  			Stage:      3,
   538  			WantCode:   StatusInternalServerError,
   539  			ErrPutRefs: anyErr,
   540  			WantErr:    anyErr,
   541  		},
   542  	}
   543  
   544  	for _, tc := range tests {
   545  		t.Run(tc.Name, func(t *testing.T) {
   546  			m := newFakeGetManager(zooAnimalSchemaForTest())
   547  			m.authorizer.Err = tc.ErrAuth
   548  			m.locks.Err = tc.ErrLock
   549  			m.schemaManager.(*fakeSchemaManager).GetschemaErr = tc.ErrSchema
   550  			srcObj := &search.Result{
   551  				ClassName: cls,
   552  				Schema:    tc.properties,
   553  			}
   554  			if tc.SrcNotFound {
   555  				srcObj = nil
   556  			}
   557  			if tc.Stage >= 2 {
   558  				m.repo.On("Object", cls, id, mock.Anything, mock.Anything, "").Return(srcObj, tc.ErrSrcExists)
   559  				m.modulesProvider.On("UsingRef2Vec", mock.Anything).Return(false)
   560  			}
   561  
   562  			if tc.Stage >= 3 {
   563  				m.repo.On("PutObject", mock.Anything, mock.Anything).Return(tc.ErrPutRefs).Once()
   564  			}
   565  
   566  			err := m.DeleteObjectReference(context.Background(), nil, &tc.Req, nil, "")
   567  			if tc.WantCode != 0 {
   568  				code := 0
   569  				if err != nil {
   570  					code = err.Code
   571  				}
   572  				if code != tc.WantCode {
   573  					t.Fatalf("code expected: %v, got %v", tc.WantCode, code)
   574  				}
   575  
   576  				if tc.WantErr != nil && !errors.Is(err, tc.WantErr) {
   577  					t.Errorf("wrapped error expected: %v, got %v", tc.WantErr, err.Err)
   578  				}
   579  
   580  			} else if tc.properties != nil {
   581  				refs, ok := srcObj.Schema.(map[string]interface{})[prop].(models.MultipleRef)
   582  				if g, w := len(refs), tc.NewSrcRefsLen; ok && g != w {
   583  					t.Errorf("length of source reference after deletion got:%v, want:%v", g, w)
   584  				}
   585  
   586  			}
   587  
   588  			m.repo.AssertExpectations(t)
   589  		})
   590  	}
   591  }
   592  
   593  func Test_ReferenceAdd_Ref2Vec(t *testing.T) {
   594  	t.Parallel()
   595  
   596  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
   597  	defer cancel()
   598  
   599  	m := newFakeGetManager(articleSchemaForTest())
   600  
   601  	req := AddReferenceInput{
   602  		Class:    "Article",
   603  		ID:       strfmt.UUID("e1a60252-c38c-496d-8e54-306e1cedc5c4"),
   604  		Property: "hasParagraphs",
   605  		Ref: models.SingleRef{
   606  			Beacon: strfmt.URI("weaviate://localhost/Paragraph/494a2fe5-3e4c-4e9a-a47e-afcd9814f5ea"),
   607  		},
   608  	}
   609  
   610  	source := crossref.NewSource(schema.ClassName(req.Class), schema.PropertyName(req.Property), req.ID)
   611  	target := crossref.New("localhost", "Paragraph", "494a2fe5-3e4c-4e9a-a47e-afcd9814f5ea")
   612  	tenant := "randomTenant"
   613  
   614  	parent := &search.Result{
   615  		ID:        strfmt.UUID("e1a60252-c38c-496d-8e54-306e1cedc5c4"),
   616  		ClassName: "Article",
   617  		Schema:    map[string]interface{}{},
   618  	}
   619  
   620  	ref1 := &search.Result{
   621  		ID:        strfmt.UUID("494a2fe5-3e4c-4e9a-a47e-afcd9814f5ea"),
   622  		ClassName: "Paragraph",
   623  		Vector:    []float32{2, 4, 6},
   624  	}
   625  
   626  	m.repo.On("Exists", "Article", parent.ID).Return(true, nil)
   627  	m.repo.On("Exists", "Paragraph", ref1.ID).Return(true, nil)
   628  	m.repo.On("Object", "Article", parent.ID, search.SelectProperties{}, additional.Properties{}, tenant).Return(parent, nil)
   629  	m.repo.On("Object", "Paragraph", ref1.ID, search.SelectProperties{}, additional.Properties{}, tenant).Return(ref1, nil)
   630  	m.repo.On("AddReference", source, target).Return(nil)
   631  	m.modulesProvider.On("UsingRef2Vec", mock.Anything).Return(true)
   632  	m.modulesProvider.On("UpdateVector", mock.Anything, mock.AnythingOfType(FindObjectFn)).
   633  		Return(ref1.Vector, nil)
   634  	m.repo.On("PutObject", mock.Anything, ref1.Vector).Return(nil)
   635  	err := m.Manager.AddObjectReference(ctx, nil, &req, nil, tenant)
   636  	assert.Nil(t, err)
   637  }
   638  
   639  func Test_ReferenceDelete_Ref2Vec(t *testing.T) {
   640  	t.Parallel()
   641  
   642  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
   643  	defer cancel()
   644  
   645  	m := newFakeGetManager(articleSchemaForTest())
   646  
   647  	req := DeleteReferenceInput{
   648  		Class:    "Article",
   649  		ID:       strfmt.UUID("e1a60252-c38c-496d-8e54-306e1cedc5c4"),
   650  		Property: "hasParagraphs",
   651  		Reference: models.SingleRef{
   652  			Beacon: strfmt.URI("weaviate://localhost/Paragraph/494a2fe5-3e4c-4e9a-a47e-afcd9814f5ea"),
   653  		},
   654  	}
   655  
   656  	tenant := "randomTenant"
   657  
   658  	parent := &search.Result{
   659  		ID:        strfmt.UUID("e1a60252-c38c-496d-8e54-306e1cedc5c4"),
   660  		ClassName: "Article",
   661  		Schema:    map[string]interface{}{},
   662  	}
   663  
   664  	ref1 := &search.Result{
   665  		ID:        strfmt.UUID("494a2fe5-3e4c-4e9a-a47e-afcd9814f5ea"),
   666  		ClassName: "Paragraph",
   667  		Vector:    []float32{2, 4, 6},
   668  	}
   669  
   670  	m.repo.On("Exists", "Article", parent.ID).Return(true, nil)
   671  	m.repo.On("Exists", "Paragraph", ref1.ID).Return(true, nil)
   672  	m.repo.On("Object", req.Class, req.ID, search.SelectProperties{}, additional.Properties{}, tenant).Return(parent, nil)
   673  	m.repo.On("PutObject", parent.Object(), []float32(nil)).Return(nil)
   674  	m.modulesProvider.On("UsingRef2Vec", mock.Anything).Return(true)
   675  
   676  	err := m.Manager.DeleteObjectReference(ctx, nil, &req, nil, tenant)
   677  	assert.Nil(t, err)
   678  }
   679  
   680  func articleSchemaForTest() schema.Schema {
   681  	return schema.Schema{
   682  		Objects: &models.Schema{
   683  			Classes: []*models.Class{
   684  				{
   685  					Class:             "Paragraph",
   686  					VectorIndexConfig: hnsw.UserConfig{},
   687  					Properties: []*models.Property{
   688  						{
   689  							Name:     "contents",
   690  							DataType: []string{"text"},
   691  						},
   692  					},
   693  				},
   694  				{
   695  					Class:             "Article",
   696  					VectorIndexConfig: hnsw.UserConfig{},
   697  					Properties: []*models.Property{
   698  						{
   699  							Name:         "title",
   700  							DataType:     schema.DataTypeText.PropString(),
   701  							Tokenization: models.PropertyTokenizationWhitespace,
   702  						},
   703  						{
   704  							Name:     "hasParagraphs",
   705  							DataType: []string{"Paragraph"},
   706  						},
   707  					},
   708  					Vectorizer: "ref2vec-centroid",
   709  					ModuleConfig: map[string]interface{}{
   710  						"ref2vec-centroid": map[string]interface{}{
   711  							"referenceProperties": []string{"hasParagraphs"},
   712  							"method":              "mean",
   713  						},
   714  					},
   715  				},
   716  			},
   717  		},
   718  	}
   719  }
   720  
   721  func zooAnimalSchemaForTest() schema.Schema {
   722  	return schema.Schema{
   723  		Objects: &models.Schema{
   724  			Classes: []*models.Class{
   725  				{
   726  					Class:             "ZooAction",
   727  					VectorIndexConfig: hnsw.UserConfig{},
   728  					Properties: []*models.Property{
   729  						{
   730  							Name:         "name",
   731  							DataType:     schema.DataTypeText.PropString(),
   732  							Tokenization: models.PropertyTokenizationWhitespace,
   733  						},
   734  						{
   735  							Name:     "area",
   736  							DataType: []string{"number"},
   737  						},
   738  						{
   739  							Name:     "employees",
   740  							DataType: []string{"int"},
   741  						},
   742  						{
   743  							Name:     "located",
   744  							DataType: []string{"geoCoordinates"},
   745  						},
   746  						{
   747  							Name:     "foundedIn",
   748  							DataType: []string{"date"},
   749  						},
   750  						{
   751  							Name:     "hasAnimals",
   752  							DataType: []string{"AnimalAction"},
   753  						},
   754  					},
   755  				},
   756  				{
   757  					Class:             "AnimalAction",
   758  					VectorIndexConfig: hnsw.UserConfig{},
   759  					Properties: []*models.Property{
   760  						{
   761  							Name:         "name",
   762  							DataType:     schema.DataTypeText.PropString(),
   763  							Tokenization: models.PropertyTokenizationWhitespace,
   764  						},
   765  					},
   766  				},
   767  				{
   768  					Class:             "Zoo",
   769  					VectorIndexConfig: hnsw.UserConfig{},
   770  					Properties: []*models.Property{
   771  						{
   772  							Name:         "name",
   773  							DataType:     schema.DataTypeText.PropString(),
   774  							Tokenization: models.PropertyTokenizationWhitespace,
   775  						},
   776  						{
   777  							Name:     "area",
   778  							DataType: []string{"number"},
   779  						},
   780  						{
   781  							Name:     "employees",
   782  							DataType: []string{"int"},
   783  						},
   784  						{
   785  							Name:     "located",
   786  							DataType: []string{"geoCoordinates"},
   787  						},
   788  						{
   789  							Name:     "foundedIn",
   790  							DataType: []string{"date"},
   791  						},
   792  						{
   793  							Name:     "hasAnimals",
   794  							DataType: []string{"Animal"},
   795  						},
   796  						{
   797  							Name:     "emptyType",
   798  							DataType: []string{""},
   799  						},
   800  					},
   801  				},
   802  				{
   803  					Class:             "Animal",
   804  					VectorIndexConfig: hnsw.UserConfig{},
   805  					Properties: []*models.Property{
   806  						{
   807  							Name:         "name",
   808  							DataType:     schema.DataTypeText.PropString(),
   809  							Tokenization: models.PropertyTokenizationWhitespace,
   810  						},
   811  					},
   812  				},
   813  				{
   814  					Class:             "NotVectorized",
   815  					VectorIndexConfig: hnsw.UserConfig{},
   816  					Properties: []*models.Property{
   817  						{
   818  							Name:     "description",
   819  							DataType: []string{"text"},
   820  						},
   821  					},
   822  					Vectorizer: "none",
   823  				},
   824  			},
   825  		},
   826  	}
   827  }