github.com/weaviate/weaviate@v1.24.6/test/acceptance/objects/crefs_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  	"testing"
    17  
    18  	"github.com/google/uuid"
    19  
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/weaviate/weaviate/client/batch"
    24  
    25  	"github.com/go-openapi/strfmt"
    26  	"github.com/weaviate/weaviate/client/objects"
    27  	clschema "github.com/weaviate/weaviate/client/schema"
    28  	"github.com/weaviate/weaviate/entities/models"
    29  	"github.com/weaviate/weaviate/entities/schema"
    30  	"github.com/weaviate/weaviate/test/helper"
    31  	testhelper "github.com/weaviate/weaviate/test/helper"
    32  )
    33  
    34  const (
    35  	beaconStart = "weaviate://localhost/"
    36  	pathStart   = "/v1/objects/"
    37  )
    38  
    39  func TestRefsWithTenantWithoutToClass(t *testing.T) {
    40  	refToClassName := "ReferenceTo"
    41  	refFromClassName := "ReferenceFrom"
    42  
    43  	toParam := clschema.NewSchemaObjectsCreateParams().WithObjectClass(
    44  		&models.Class{Class: refToClassName, MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true}},
    45  	)
    46  	respTo, err := helper.Client(t).Schema.SchemaObjectsCreate(toParam, nil)
    47  	helper.AssertRequestOk(t, respTo, err, nil)
    48  
    49  	fromParam := clschema.NewSchemaObjectsCreateParams().WithObjectClass(
    50  		&models.Class{
    51  			Class:              refFromClassName,
    52  			MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
    53  			Properties: []*models.Property{
    54  				{
    55  					DataType: []string{refToClassName},
    56  					Name:     "ref",
    57  				},
    58  			},
    59  		},
    60  	)
    61  	respFrom, err := helper.Client(t).Schema.SchemaObjectsCreate(fromParam, nil)
    62  	helper.AssertRequestOk(t, respFrom, err, nil)
    63  
    64  	defer deleteObjectClass(t, refToClassName)
    65  	defer deleteObjectClass(t, refFromClassName)
    66  
    67  	tenant := "tenant"
    68  	tenants := make([]*models.Tenant, 1)
    69  	for i := range tenants {
    70  		tenants[i] = &models.Tenant{Name: tenant}
    71  	}
    72  	helper.CreateTenants(t, refToClassName, tenants)
    73  	helper.CreateTenants(t, refFromClassName, tenants)
    74  
    75  	refToId := strfmt.UUID(uuid.New().String())
    76  	assertCreateObjectWithID(t, refToClassName, tenant, refToId, map[string]interface{}{})
    77  
    78  	refFromId1 := strfmt.UUID(uuid.New().String())
    79  	assertCreateObjectWithID(t, refFromClassName, tenant, refFromId1, map[string]interface{}{})
    80  
    81  	// add reference between objects without to class name
    82  	postRefParams := objects.NewObjectsClassReferencesCreateParams().
    83  		WithID(refFromId1).
    84  		WithPropertyName("ref").WithClassName(refFromClassName).
    85  		WithBody(&models.SingleRef{
    86  			Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s", refToId.String())),
    87  		}).WithTenant(&tenant)
    88  	postRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesCreate(postRefParams, nil)
    89  	helper.AssertRequestOk(t, postRefResponse, err, nil)
    90  
    91  	// add reference from batch
    92  	refFromId2 := strfmt.UUID(uuid.New().String())
    93  	assertCreateObjectWithID(t, refFromClassName, tenant, refFromId2, map[string]interface{}{})
    94  
    95  	// add refs without toClass
    96  	batchRefs := []*models.BatchReference{
    97  		{From: strfmt.URI(beaconStart + "ReferenceFrom/" + refFromId2 + "/ref"), To: strfmt.URI(beaconStart + refToId), Tenant: tenant},
    98  	}
    99  	postRefBatchParams := batch.NewBatchReferencesCreateParams().WithBody(batchRefs)
   100  	postRefBatchResponse, err := helper.Client(t).Batch.BatchReferencesCreate(postRefBatchParams, nil)
   101  	helper.AssertRequestOk(t, postRefBatchResponse, err, nil)
   102  	require.Nil(t, postRefBatchResponse.Payload[0].Result.Errors)
   103  }
   104  
   105  func TestRefsWithoutToClass(t *testing.T) {
   106  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: "ReferenceTo"})
   107  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   108  	helper.AssertRequestOk(t, resp, err, nil)
   109  	refToClassName := "ReferenceTo"
   110  	refFromClassName := "ReferenceFrom"
   111  	otherClassMT := "Other"
   112  
   113  	paramsMT := clschema.NewSchemaObjectsCreateParams().WithObjectClass(
   114  		&models.Class{
   115  			Class:              otherClassMT,
   116  			MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true},
   117  			Properties: []*models.Property{
   118  				{
   119  					DataType: []string{refToClassName},
   120  					Name:     "ref",
   121  				},
   122  			},
   123  		},
   124  	)
   125  	respMT, err := helper.Client(t).Schema.SchemaObjectsCreate(paramsMT, nil)
   126  	helper.AssertRequestOk(t, respMT, err, nil)
   127  
   128  	tenant := "tenant"
   129  	tenants := make([]*models.Tenant, 1)
   130  	for i := range tenants {
   131  		tenants[i] = &models.Tenant{Name: tenant}
   132  	}
   133  	helper.CreateTenants(t, otherClassMT, tenants)
   134  
   135  	refFromClass := &models.Class{
   136  		Class: refFromClassName,
   137  		Properties: []*models.Property{
   138  			{
   139  				DataType: []string{refToClassName},
   140  				Name:     "ref",
   141  			},
   142  		},
   143  	}
   144  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   145  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   146  	helper.AssertRequestOk(t, resp2, err, nil)
   147  
   148  	defer deleteObjectClass(t, refToClassName)
   149  	defer deleteObjectClass(t, refFromClassName)
   150  	defer deleteObjectClass(t, otherClassMT)
   151  
   152  	refToId := assertCreateObject(t, refToClassName, map[string]interface{}{})
   153  	assertGetObjectWithClass(t, refToId, refToClassName)
   154  	assertCreateObjectWithID(t, otherClassMT, tenant, refToId, map[string]interface{}{})
   155  	refFromId := assertCreateObject(t, refFromClassName, map[string]interface{}{})
   156  	assertGetObjectWithClass(t, refFromId, refFromClassName)
   157  
   158  	postRefParams := objects.NewObjectsClassReferencesCreateParams().
   159  		WithID(refFromId).
   160  		WithPropertyName("ref").WithClassName(refFromClass.Class).
   161  		WithBody(&models.SingleRef{
   162  			Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s", refToId.String())),
   163  		})
   164  	postRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesCreate(postRefParams, nil)
   165  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   166  
   167  	// validate that ref was create for the correct class
   168  	objWithRef := func() interface{} {
   169  		obj := assertGetObjectWithClass(t, refFromId, refFromClassName)
   170  		return obj.Properties
   171  	}
   172  	testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   173  		"ref": []interface{}{
   174  			map[string]interface{}{
   175  				"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, refToId.String()),
   176  				"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, refToId.String()),
   177  			},
   178  		},
   179  	}, objWithRef)
   180  
   181  	// update prop with multiple references
   182  	updateRefParams := objects.NewObjectsClassReferencesPutParams().
   183  		WithID(refFromId).
   184  		WithPropertyName("ref").WithClassName(refFromClass.Class).
   185  		WithBody(models.MultipleRef{
   186  			{Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s", refToId.String()))},
   187  			{Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s/%s", refToClassName, refToId.String()))},
   188  		})
   189  	updateRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesPut(updateRefParams, nil)
   190  	helper.AssertRequestOk(t, updateRefResponse, err, nil)
   191  
   192  	objWithTwoRef := func() interface{} {
   193  		obj := assertGetObjectWithClass(t, refFromId, refFromClassName)
   194  		return obj.Properties
   195  	}
   196  	testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   197  		"ref": []interface{}{
   198  			map[string]interface{}{
   199  				"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, refToId.String()),
   200  				"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, refToId.String()),
   201  			},
   202  			map[string]interface{}{
   203  				"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, refToId.String()),
   204  				"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, refToId.String()),
   205  			},
   206  		},
   207  	}, objWithTwoRef)
   208  
   209  	// delete reference without class
   210  	deleteRefParams := objects.NewObjectsClassReferencesDeleteParams().
   211  		WithID(refFromId).
   212  		WithPropertyName("ref").WithClassName(refFromClass.Class).
   213  		WithBody(&models.SingleRef{
   214  			Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s", refToId.String())),
   215  		})
   216  	deleteRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesDelete(deleteRefParams, nil)
   217  	helper.AssertRequestOk(t, deleteRefResponse, err, nil)
   218  	objWithoutRef := func() interface{} {
   219  		obj := assertGetObjectWithClass(t, refFromId, refFromClassName)
   220  		return obj.Properties
   221  	}
   222  	testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   223  		"ref": []interface{}{},
   224  	}, objWithoutRef)
   225  }
   226  
   227  func TestRefsMultiTarget(t *testing.T) {
   228  	refToClassName := "ReferenceTo"
   229  	refFromClassName := "ReferenceFrom"
   230  	defer deleteObjectClass(t, refToClassName)
   231  	defer deleteObjectClass(t, refFromClassName)
   232  
   233  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   234  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   235  	helper.AssertRequestOk(t, resp, err, nil)
   236  
   237  	refFromClass := &models.Class{
   238  		Class: refFromClassName,
   239  		Properties: []*models.Property{
   240  			{
   241  				DataType: []string{refToClassName, refFromClassName},
   242  				Name:     "ref",
   243  			},
   244  		},
   245  	}
   246  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   247  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   248  	helper.AssertRequestOk(t, resp2, err, nil)
   249  
   250  	refToId := assertCreateObject(t, refToClassName, map[string]interface{}{})
   251  	assertGetObjectEventually(t, refToId)
   252  	refFromId := assertCreateObject(t, refFromClassName, map[string]interface{}{})
   253  	assertGetObjectEventually(t, refFromId)
   254  
   255  	cases := []struct {
   256  		classRef string
   257  		id       string
   258  	}{
   259  		{classRef: "", id: refToId.String()},
   260  		{classRef: refToClassName + "/", id: refToId.String()},
   261  		{classRef: refFromClassName + "/", id: refFromId.String()},
   262  	}
   263  	for _, tt := range cases {
   264  		postRefParams := objects.NewObjectsClassReferencesCreateParams().
   265  			WithID(refFromId).
   266  			WithPropertyName("ref").WithClassName(refFromClass.Class).
   267  			WithBody(&models.SingleRef{
   268  				Beacon: strfmt.URI(fmt.Sprintf(beaconStart+"%s%s", tt.classRef, tt.id)),
   269  			})
   270  		postRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesCreate(postRefParams, nil)
   271  		helper.AssertRequestOk(t, postRefResponse, err, nil)
   272  
   273  		// validate that ref was create for the correct class
   274  		objWithRef := func() interface{} {
   275  			obj := assertGetObjectWithClass(t, refFromId, refFromClassName)
   276  			return obj.Properties
   277  		}
   278  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   279  			"ref": []interface{}{
   280  				map[string]interface{}{
   281  					"beacon": fmt.Sprintf(beaconStart+"%s%s", tt.classRef, tt.id),
   282  					"href":   fmt.Sprintf(pathStart+"%s%s", tt.classRef, tt.id),
   283  				},
   284  			},
   285  		}, objWithRef)
   286  
   287  		// delete refs
   288  		updateRefParams := objects.NewObjectsClassReferencesPutParams().
   289  			WithID(refFromId).
   290  			WithPropertyName("ref").WithClassName(refFromClass.Class).
   291  			WithBody(models.MultipleRef{})
   292  		updateRefResponse, err := helper.Client(t).Objects.ObjectsClassReferencesPut(updateRefParams, nil)
   293  		helper.AssertRequestOk(t, updateRefResponse, err, nil)
   294  	}
   295  }
   296  
   297  func TestBatchRefsMultiTarget(t *testing.T) {
   298  	refToClassName := "ReferenceTo"
   299  	refFromClassName := "ReferenceFrom"
   300  	defer deleteObjectClass(t, refToClassName)
   301  	defer deleteObjectClass(t, refFromClassName)
   302  
   303  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   304  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   305  	helper.AssertRequestOk(t, resp, err, nil)
   306  
   307  	refFromClass := &models.Class{
   308  		Class: refFromClassName,
   309  		Properties: []*models.Property{
   310  			{
   311  				DataType: []string{refToClassName, refFromClassName},
   312  				Name:     "ref",
   313  			},
   314  		},
   315  	}
   316  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   317  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   318  	helper.AssertRequestOk(t, resp2, err, nil)
   319  
   320  	uuidsTo := make([]strfmt.UUID, 10)
   321  	uuidsFrom := make([]strfmt.UUID, 10)
   322  	for i := 0; i < 10; i++ {
   323  		uuidsTo[i] = assertCreateObject(t, refToClassName, map[string]interface{}{})
   324  		assertGetObjectEventually(t, uuidsTo[i])
   325  		uuidsFrom[i] = assertCreateObject(t, refFromClassName, map[string]interface{}{})
   326  		assertGetObjectEventually(t, uuidsFrom[i])
   327  	}
   328  
   329  	// add refs without toClass
   330  	var batchRefs []*models.BatchReference
   331  	for i := range uuidsFrom[:2] {
   332  		from := beaconStart + "ReferenceFrom/" + uuidsFrom[i] + "/ref"
   333  		to := beaconStart + uuidsTo[i]
   334  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   335  	}
   336  
   337  	// add refs with toClass target 1
   338  	for i := range uuidsFrom[2:5] {
   339  		j := i + 2
   340  		from := beaconStart + "ReferenceFrom/" + uuidsFrom[j] + "/ref"
   341  		to := beaconStart + "ReferenceTo/" + uuidsTo[j]
   342  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   343  	}
   344  
   345  	// add refs with toClass target 2
   346  	for i := range uuidsFrom[5:] {
   347  		j := i + 5
   348  		from := beaconStart + "ReferenceFrom/" + uuidsFrom[j] + "/ref"
   349  		to := beaconStart + "ReferenceFrom/" + uuidsTo[j]
   350  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   351  	}
   352  
   353  	postRefParams := batch.NewBatchReferencesCreateParams().WithBody(batchRefs)
   354  	postRefResponse, err := helper.Client(t).Batch.BatchReferencesCreate(postRefParams, nil)
   355  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   356  
   357  	// no autodetect for multi-target
   358  	for i := range uuidsFrom[:2] {
   359  		objWithRef := func() interface{} {
   360  			obj := assertGetObjectWithClass(t, uuidsFrom[i], refFromClassName)
   361  			return obj.Properties
   362  		}
   363  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   364  			"ref": []interface{}{
   365  				map[string]interface{}{
   366  					"beacon": fmt.Sprintf(beaconStart+"%s", uuidsTo[i].String()),
   367  					"href":   fmt.Sprintf(pathStart+"%s", uuidsTo[i].String()),
   368  				},
   369  			},
   370  		}, objWithRef)
   371  	}
   372  
   373  	// refs for target 1
   374  	for i := range uuidsFrom[2:5] {
   375  		j := i + 2
   376  		objWithRef := func() interface{} {
   377  			obj := assertGetObjectWithClass(t, uuidsFrom[j], refFromClassName)
   378  			return obj.Properties
   379  		}
   380  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   381  			"ref": []interface{}{
   382  				map[string]interface{}{
   383  					"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, uuidsTo[j].String()),
   384  					"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, uuidsTo[j].String()),
   385  				},
   386  			},
   387  		}, objWithRef)
   388  	}
   389  
   390  	// refs for target 2
   391  	for i := range uuidsFrom[5:] {
   392  		j := i + 5
   393  		objWithRef := func() interface{} {
   394  			obj := assertGetObjectWithClass(t, uuidsFrom[j], refFromClassName)
   395  			return obj.Properties
   396  		}
   397  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   398  			"ref": []interface{}{
   399  				map[string]interface{}{
   400  					"beacon": fmt.Sprintf(beaconStart+"%s/%s", refFromClassName, uuidsTo[j].String()),
   401  					"href":   fmt.Sprintf(pathStart+"%s/%s", refFromClassName, uuidsTo[j].String()),
   402  				},
   403  			},
   404  		}, objWithRef)
   405  	}
   406  }
   407  
   408  func TestBatchRefsWithoutFromAndToClass(t *testing.T) {
   409  	refToClassName := "ReferenceTo"
   410  	refFromClassName := "ReferenceFrom"
   411  
   412  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   413  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   414  	helper.AssertRequestOk(t, resp, err, nil)
   415  
   416  	refFromClass := &models.Class{
   417  		Class: refFromClassName,
   418  		Properties: []*models.Property{
   419  			{
   420  				DataType: []string{refToClassName},
   421  				Name:     "ref",
   422  			},
   423  		},
   424  	}
   425  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   426  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   427  	helper.AssertRequestOk(t, resp2, err, nil)
   428  
   429  	defer deleteObjectClass(t, refToClassName)
   430  	defer deleteObjectClass(t, refFromClassName)
   431  
   432  	uuidsTo := make([]strfmt.UUID, 10)
   433  	uuidsFrom := make([]strfmt.UUID, 10)
   434  	for i := 0; i < 10; i++ {
   435  		uuidsTo[i] = assertCreateObject(t, refToClassName, map[string]interface{}{})
   436  		assertGetObjectWithClass(t, uuidsTo[i], refToClassName)
   437  
   438  		uuidsFrom[i] = assertCreateObject(t, refFromClassName, map[string]interface{}{})
   439  		assertGetObjectWithClass(t, uuidsFrom[i], refFromClassName)
   440  	}
   441  
   442  	// cannot do from urls without class
   443  	var batchRefs []*models.BatchReference
   444  	for i := range uuidsFrom {
   445  		from := beaconStart + uuidsFrom[i] + "/ref"
   446  		to := beaconStart + uuidsTo[i]
   447  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   448  	}
   449  
   450  	postRefParams := batch.NewBatchReferencesCreateParams().WithBody(batchRefs)
   451  	resp3, err := helper.Client(t).Batch.BatchReferencesCreate(postRefParams, nil)
   452  	require.Nil(t, err)
   453  	require.NotNil(t, resp3)
   454  	for i := range resp3.Payload {
   455  		require.NotNil(t, resp3.Payload[i].Result.Errors)
   456  	}
   457  }
   458  
   459  func TestBatchRefWithErrors(t *testing.T) {
   460  	refToClassName := "ReferenceTo"
   461  	refFromClassName := "ReferenceFrom"
   462  
   463  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   464  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   465  	helper.AssertRequestOk(t, resp, err, nil)
   466  
   467  	refFromClass := &models.Class{
   468  		Class: refFromClassName,
   469  		Properties: []*models.Property{
   470  			{
   471  				DataType: []string{refToClassName},
   472  				Name:     "ref",
   473  			},
   474  		},
   475  	}
   476  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   477  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   478  	helper.AssertRequestOk(t, resp2, err, nil)
   479  
   480  	defer deleteObjectClass(t, refToClassName)
   481  	defer deleteObjectClass(t, refFromClassName)
   482  
   483  	uuidsTo := make([]strfmt.UUID, 2)
   484  	uuidsFrom := make([]strfmt.UUID, 2)
   485  	for i := 0; i < 2; i++ {
   486  		uuidsTo[i] = assertCreateObject(t, refToClassName, map[string]interface{}{})
   487  		assertGetObjectWithClass(t, uuidsTo[i], refToClassName)
   488  
   489  		uuidsFrom[i] = assertCreateObject(t, refFromClassName, map[string]interface{}{})
   490  		assertGetObjectWithClass(t, uuidsFrom[i], refFromClassName)
   491  	}
   492  
   493  	var batchRefs []*models.BatchReference
   494  	for i := range uuidsFrom {
   495  		from := beaconStart + "ReferenceFrom/" + uuidsFrom[i] + "/ref"
   496  		to := beaconStart + uuidsTo[i]
   497  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   498  	}
   499  
   500  	// append one entry with a non-existent class
   501  	batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(beaconStart + "DoesNotExist/" + uuidsFrom[0] + "/ref"), To: strfmt.URI(beaconStart + uuidsTo[0])})
   502  
   503  	// append one entry with a non-existent property for existing class
   504  	batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(beaconStart + "ReferenceFrom/" + uuidsFrom[0] + "/doesNotExist"), To: strfmt.URI(beaconStart + uuidsTo[0])})
   505  
   506  	postRefParams := batch.NewBatchReferencesCreateParams().WithBody(batchRefs)
   507  	postRefResponse, err := helper.Client(t).Batch.BatchReferencesCreate(postRefParams, nil)
   508  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   509  
   510  	require.NotNil(t, postRefResponse.Payload[2].Result.Errors)
   511  	require.Contains(t, postRefResponse.Payload[2].Result.Errors.Error[0].Message, "class DoesNotExist does not exist")
   512  
   513  	require.NotNil(t, postRefResponse.Payload[3].Result.Errors)
   514  	require.Contains(t, postRefResponse.Payload[3].Result.Errors.Error[0].Message, "property doesNotExist does not exist for class ReferenceFrom")
   515  }
   516  
   517  func TestBatchRefsWithoutToClass(t *testing.T) {
   518  	refToClassName := "ReferenceTo"
   519  	refFromClassName := "ReferenceFrom"
   520  	otherClassMT := "Other"
   521  
   522  	// other class has multi-tenancy enabled to make sure that problems trigger an error
   523  	paramsMT := clschema.NewSchemaObjectsCreateParams().WithObjectClass(
   524  		&models.Class{Class: otherClassMT, MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true}},
   525  	)
   526  	respMT, err := helper.Client(t).Schema.SchemaObjectsCreate(paramsMT, nil)
   527  	helper.AssertRequestOk(t, respMT, err, nil)
   528  
   529  	tenant := "tenant"
   530  	tenants := make([]*models.Tenant, 1)
   531  	for i := range tenants {
   532  		tenants[i] = &models.Tenant{Name: tenant}
   533  	}
   534  	helper.CreateTenants(t, otherClassMT, tenants)
   535  
   536  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   537  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   538  	helper.AssertRequestOk(t, resp, err, nil)
   539  
   540  	refFromClass := &models.Class{
   541  		Class: refFromClassName,
   542  		Properties: []*models.Property{
   543  			{
   544  				DataType: []string{refToClassName},
   545  				Name:     "ref",
   546  			},
   547  		},
   548  	}
   549  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   550  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   551  	helper.AssertRequestOk(t, resp2, err, nil)
   552  
   553  	defer deleteObjectClass(t, refToClassName)
   554  	defer deleteObjectClass(t, refFromClassName)
   555  	defer deleteObjectClass(t, otherClassMT)
   556  
   557  	uuidsTo := make([]strfmt.UUID, 10)
   558  	uuidsFrom := make([]strfmt.UUID, 10)
   559  	for i := 0; i < 10; i++ {
   560  		uuidsTo[i] = assertCreateObject(t, refToClassName, map[string]interface{}{})
   561  		assertGetObjectWithClass(t, uuidsTo[i], refToClassName)
   562  
   563  		// create object with same id in MT class
   564  		assertCreateObjectWithID(t, otherClassMT, tenant, uuidsTo[i], map[string]interface{}{})
   565  
   566  		uuidsFrom[i] = assertCreateObject(t, refFromClassName, map[string]interface{}{})
   567  		assertGetObjectWithClass(t, uuidsFrom[i], refFromClassName)
   568  	}
   569  
   570  	var batchRefs []*models.BatchReference
   571  	for i := range uuidsFrom {
   572  		from := beaconStart + "ReferenceFrom/" + uuidsFrom[i] + "/ref"
   573  		to := beaconStart + uuidsTo[i]
   574  		batchRefs = append(batchRefs, &models.BatchReference{From: strfmt.URI(from), To: strfmt.URI(to)})
   575  	}
   576  
   577  	postRefParams := batch.NewBatchReferencesCreateParams().WithBody(batchRefs)
   578  	postRefResponse, err := helper.Client(t).Batch.BatchReferencesCreate(postRefParams, nil)
   579  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   580  
   581  	for i := range uuidsFrom {
   582  		// validate that ref was create for the correct class
   583  		objWithRef := func() interface{} {
   584  			obj := assertGetObjectWithClass(t, uuidsFrom[i], refFromClassName)
   585  			return obj.Properties
   586  		}
   587  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   588  			"ref": []interface{}{
   589  				map[string]interface{}{
   590  					"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, uuidsTo[i].String()),
   591  					"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, uuidsTo[i].String()),
   592  				},
   593  			},
   594  		}, objWithRef)
   595  	}
   596  }
   597  
   598  func TestObjectBatchToClassDetection(t *testing.T) {
   599  	// uses same code path as normal object add
   600  	refToClassName := "ReferenceTo"
   601  	refFromClassName := "ReferenceFrom"
   602  	defer deleteObjectClass(t, refToClassName)
   603  	defer deleteObjectClass(t, refFromClassName)
   604  
   605  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   606  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   607  	helper.AssertRequestOk(t, resp, err, nil)
   608  
   609  	refFromClass := &models.Class{
   610  		Class: refFromClassName,
   611  		Properties: []*models.Property{
   612  			{
   613  				DataType: []string{refToClassName},
   614  				Name:     "ref",
   615  			},
   616  		},
   617  	}
   618  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   619  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   620  	helper.AssertRequestOk(t, resp2, err, nil)
   621  
   622  	refs := make([]interface{}, 10)
   623  	uuidsTo := make([]strfmt.UUID, 10)
   624  
   625  	for i := 0; i < 10; i++ {
   626  		uuidTo := assertCreateObject(t, refToClassName, map[string]interface{}{})
   627  		uuidsTo[i] = uuidTo
   628  		assertGetObjectEventually(t, uuidTo)
   629  		refs[i] = map[string]interface{}{
   630  			"beacon": beaconStart + uuidTo,
   631  		}
   632  	}
   633  
   634  	fromBatch := make([]*models.Object, 10)
   635  	for i := 0; i < 10; i++ {
   636  		fromBatch[i] = &models.Object{
   637  			Class: refFromClassName,
   638  			ID:    strfmt.UUID(uuid.New().String()),
   639  			Properties: map[string]interface{}{
   640  				"ref": refs[i : i+1],
   641  			},
   642  		}
   643  	}
   644  	paramsBatch := batch.NewBatchObjectsCreateParams().WithBody(
   645  		batch.BatchObjectsCreateBody{
   646  			Objects: fromBatch,
   647  		},
   648  	)
   649  	res, err := helper.Client(t).Batch.BatchObjectsCreate(paramsBatch, nil)
   650  	require.Nil(t, err)
   651  	for _, elem := range res.Payload {
   652  		assert.Nil(t, elem.Result.Errors)
   653  	}
   654  
   655  	for i := range fromBatch {
   656  		// validate that ref was create for the correct class
   657  		objWithRef := func() interface{} {
   658  			obj := assertGetObjectWithClass(t, fromBatch[i].ID, refFromClassName)
   659  			return obj.Properties
   660  		}
   661  		testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   662  			"ref": []interface{}{
   663  				map[string]interface{}{
   664  					"beacon": fmt.Sprintf(beaconStart+"%s/%s", refToClassName, uuidsTo[i].String()),
   665  					"href":   fmt.Sprintf(pathStart+"%s/%s", refToClassName, uuidsTo[i].String()),
   666  				},
   667  			},
   668  		}, objWithRef)
   669  	}
   670  }
   671  
   672  func TestObjectCrefWithoutToClass(t *testing.T) {
   673  	refToClassName := "ReferenceTo"
   674  	refFromClassName := "ReferenceFrom"
   675  	otherClassMT := "Other"
   676  
   677  	// other class has multi-tenancy enabled to make sure that problems trigger an error
   678  	paramsMT := clschema.NewSchemaObjectsCreateParams().WithObjectClass(
   679  		&models.Class{Class: otherClassMT, MultiTenancyConfig: &models.MultiTenancyConfig{Enabled: true}},
   680  	)
   681  	respMT, err := helper.Client(t).Schema.SchemaObjectsCreate(paramsMT, nil)
   682  	helper.AssertRequestOk(t, respMT, err, nil)
   683  
   684  	tenant := "tenant"
   685  	tenants := make([]*models.Tenant, 1)
   686  	for i := range tenants {
   687  		tenants[i] = &models.Tenant{Name: tenant}
   688  	}
   689  	helper.CreateTenants(t, otherClassMT, tenants)
   690  
   691  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(&models.Class{Class: refToClassName})
   692  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   693  	helper.AssertRequestOk(t, resp, err, nil)
   694  
   695  	refFromClass := &models.Class{
   696  		Class: refFromClassName,
   697  		Properties: []*models.Property{
   698  			{
   699  				DataType: []string{refToClassName},
   700  				Name:     "ref",
   701  			},
   702  		},
   703  	}
   704  	params2 := clschema.NewSchemaObjectsCreateParams().WithObjectClass(refFromClass)
   705  	resp2, err := helper.Client(t).Schema.SchemaObjectsCreate(params2, nil)
   706  	helper.AssertRequestOk(t, resp2, err, nil)
   707  
   708  	defer deleteObjectClass(t, refToClassName)
   709  	defer deleteObjectClass(t, refFromClassName)
   710  	defer deleteObjectClass(t, otherClassMT)
   711  
   712  	refs := make([]interface{}, 10)
   713  	uuids := make([]strfmt.UUID, 10)
   714  	for i := 0; i < 10; i++ {
   715  		uuidTo := assertCreateObject(t, refToClassName, map[string]interface{}{})
   716  		assertGetObjectWithClass(t, uuidTo, refToClassName)
   717  
   718  		// create object with same id in MT class
   719  		assertCreateObjectWithID(t, otherClassMT, tenant, uuidTo, map[string]interface{}{})
   720  
   721  		refs[i] = map[string]interface{}{
   722  			"beacon": beaconStart + uuidTo,
   723  		}
   724  		uuids[i] = uuidTo
   725  	}
   726  
   727  	uuidFrom := assertCreateObject(t, refFromClassName, map[string]interface{}{"ref": refs})
   728  	assertGetObjectWithClass(t, uuidFrom, refFromClassName)
   729  
   730  	objWithRef := assertGetObjectWithClass(t, uuidFrom, refFromClassName)
   731  	assert.NotNil(t, objWithRef.Properties)
   732  	refsReturned := objWithRef.Properties.(map[string]interface{})["ref"].([]interface{})
   733  	for i := range refsReturned {
   734  		require.Equal(t, refsReturned[i].(map[string]interface{})["beacon"], string(beaconStart+"ReferenceTo/"+uuids[i]))
   735  	}
   736  }
   737  
   738  // This test suite is meant to prevent a regression on
   739  // https://github.com/weaviate/weaviate/issues/868, hence it tries to
   740  // reproduce the steps outlined in there as closely as possible
   741  func Test_CREFWithCardinalityMany_UsingPatch(t *testing.T) {
   742  	defer func() {
   743  		// clean up so we can run this test multiple times in a row
   744  		delCityParams := clschema.NewSchemaObjectsDeleteParams().WithClassName("ReferenceTestCity")
   745  		dresp, err := helper.Client(t).Schema.SchemaObjectsDelete(delCityParams, nil)
   746  		t.Logf("clean up - delete city \n%v\n %v", dresp, err)
   747  
   748  		delPlaceParams := clschema.NewSchemaObjectsDeleteParams().WithClassName("ReferenceTestPlace")
   749  		dresp, err = helper.Client(t).Schema.SchemaObjectsDelete(delPlaceParams, nil)
   750  		t.Logf("clean up - delete place \n%v\n %v", dresp, err)
   751  	}()
   752  
   753  	t.Log("1. create ReferenceTestPlace class")
   754  	placeClass := &models.Class{
   755  		Class: "ReferenceTestPlace",
   756  		Properties: []*models.Property{
   757  			{
   758  				DataType:     schema.DataTypeText.PropString(),
   759  				Tokenization: models.PropertyTokenizationWhitespace,
   760  				Name:         "name",
   761  			},
   762  		},
   763  	}
   764  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(placeClass)
   765  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   766  	helper.AssertRequestOk(t, resp, err, nil)
   767  
   768  	t.Log("2. create ReferenceTestCity class with HasPlaces (many) cross-ref")
   769  	cityClass := &models.Class{
   770  		Class: "ReferenceTestCity",
   771  		Properties: []*models.Property{
   772  			{
   773  				DataType:     schema.DataTypeText.PropString(),
   774  				Tokenization: models.PropertyTokenizationWhitespace,
   775  				Name:         "name",
   776  			},
   777  			{
   778  				DataType: []string{"ReferenceTestPlace"},
   779  				Name:     "HasPlaces",
   780  			},
   781  		},
   782  	}
   783  	params = clschema.NewSchemaObjectsCreateParams().WithObjectClass(cityClass)
   784  	resp, err = helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   785  	helper.AssertRequestOk(t, resp, err, nil)
   786  
   787  	t.Log("3. add two places and save their IDs")
   788  	place1ID := assertCreateObject(t, "ReferenceTestPlace", map[string]interface{}{
   789  		"name": "Place 1",
   790  	})
   791  	place2ID := assertCreateObject(t, "ReferenceTestPlace", map[string]interface{}{
   792  		"name": "Place 2",
   793  	})
   794  	assertGetObjectEventually(t, place1ID)
   795  	assertGetObjectEventually(t, place2ID)
   796  
   797  	t.Log("4. add one city")
   798  	cityID := assertCreateObject(t, "ReferenceTestCity", map[string]interface{}{
   799  		"name": "My City",
   800  	})
   801  	assertGetObjectEventually(t, cityID)
   802  
   803  	t.Log("5. patch city to point to the first place")
   804  	patchParams := objects.NewObjectsPatchParams().
   805  		WithID(cityID).
   806  		WithBody(&models.Object{
   807  			Class: "ReferenceTestCity",
   808  			Properties: map[string]interface{}{
   809  				"hasPlaces": []interface{}{
   810  					map[string]interface{}{
   811  						"beacon": fmt.Sprintf("weaviate://localhost/%s", place1ID.String()),
   812  					},
   813  				},
   814  			},
   815  		})
   816  	patchResp, err := helper.Client(t).Objects.ObjectsPatch(patchParams, nil)
   817  	helper.AssertRequestOk(t, patchResp, err, nil)
   818  
   819  	t.Log("6. verify first cross ref was added")
   820  
   821  	actualThunk := func() interface{} {
   822  		cityAfterFirstPatch := assertGetObject(t, cityID)
   823  		return cityAfterFirstPatch.Properties
   824  	}
   825  
   826  	testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   827  		"name": "My City",
   828  		"hasPlaces": []interface{}{
   829  			map[string]interface{}{
   830  				"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place1ID.String()),
   831  				"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place1ID.String()),
   832  			},
   833  		},
   834  	}, actualThunk)
   835  
   836  	t.Log("7. patch city to point to the second place")
   837  	patchParams = objects.NewObjectsPatchParams().
   838  		WithID(cityID).
   839  		WithBody(&models.Object{
   840  			Class: "ReferenceTestCity",
   841  			Properties: map[string]interface{}{
   842  				"hasPlaces": []interface{}{
   843  					map[string]interface{}{
   844  						"beacon": fmt.Sprintf("weaviate://localhost/%s", place2ID.String()),
   845  					},
   846  				},
   847  			},
   848  		})
   849  	patchResp, err = helper.Client(t).Objects.ObjectsPatch(patchParams, nil)
   850  	helper.AssertRequestOk(t, patchResp, err, nil)
   851  
   852  	actualThunk = func() interface{} {
   853  		city := assertGetObject(t, cityID)
   854  		return city.Properties.(map[string]interface{})["hasPlaces"].([]interface{})
   855  	}
   856  
   857  	t.Log("9. verify both cross refs are present")
   858  	expectedRefs := []interface{}{
   859  		map[string]interface{}{
   860  			"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place1ID.String()),
   861  			"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place1ID.String()),
   862  		},
   863  		map[string]interface{}{
   864  			"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place2ID.String()),
   865  			"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place2ID.String()),
   866  		},
   867  	}
   868  
   869  	testhelper.AssertEventuallyEqual(t, expectedRefs, actualThunk)
   870  }
   871  
   872  // This test suite is meant to prevent a regression on
   873  // https://github.com/weaviate/weaviate/issues/868, hence it tries to
   874  // reproduce the steps outlined in there as closely as possible
   875  func Test_CREFWithCardinalityMany_UsingPostReference(t *testing.T) {
   876  	defer func() {
   877  		// clean up so we can run this test multiple times in a row
   878  		delCityParams := clschema.NewSchemaObjectsDeleteParams().WithClassName("ReferenceTestCity")
   879  		dresp, err := helper.Client(t).Schema.SchemaObjectsDelete(delCityParams, nil)
   880  		t.Logf("clean up - delete city \n%v\n %v", dresp, err)
   881  
   882  		delPlaceParams := clschema.NewSchemaObjectsDeleteParams().WithClassName("ReferenceTestPlace")
   883  		dresp, err = helper.Client(t).Schema.SchemaObjectsDelete(delPlaceParams, nil)
   884  		t.Logf("clean up - delete place \n%v\n %v", dresp, err)
   885  	}()
   886  
   887  	t.Log("1. create ReferenceTestPlace class")
   888  	placeClass := &models.Class{
   889  		Class: "ReferenceTestPlace",
   890  		Properties: []*models.Property{
   891  			{
   892  				DataType:     schema.DataTypeText.PropString(),
   893  				Tokenization: models.PropertyTokenizationWhitespace,
   894  				Name:         "name",
   895  			},
   896  		},
   897  	}
   898  	params := clschema.NewSchemaObjectsCreateParams().WithObjectClass(placeClass)
   899  	resp, err := helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   900  	helper.AssertRequestOk(t, resp, err, nil)
   901  
   902  	t.Log("2. create ReferenceTestCity class with HasPlaces (many) cross-ref")
   903  	cityClass := &models.Class{
   904  		Class: "ReferenceTestCity",
   905  		Properties: []*models.Property{
   906  			{
   907  				DataType:     schema.DataTypeText.PropString(),
   908  				Tokenization: models.PropertyTokenizationWhitespace,
   909  				Name:         "name",
   910  			},
   911  			{
   912  				DataType: []string{"ReferenceTestPlace"},
   913  				Name:     "HasPlaces",
   914  			},
   915  		},
   916  	}
   917  	params = clschema.NewSchemaObjectsCreateParams().WithObjectClass(cityClass)
   918  	resp, err = helper.Client(t).Schema.SchemaObjectsCreate(params, nil)
   919  	helper.AssertRequestOk(t, resp, err, nil)
   920  
   921  	t.Log("3. add two places and save their IDs")
   922  	place1ID := assertCreateObject(t, "ReferenceTestPlace", map[string]interface{}{
   923  		"name": "Place 1",
   924  	})
   925  	place2ID := assertCreateObject(t, "ReferenceTestPlace", map[string]interface{}{
   926  		"name": "Place 2",
   927  	})
   928  	assertGetObjectEventually(t, place1ID)
   929  	assertGetObjectEventually(t, place2ID)
   930  	t.Logf("Place 1 ID: %s", place1ID)
   931  	t.Logf("Place 2 ID: %s", place2ID)
   932  
   933  	t.Log("4. add one city")
   934  	cityID := assertCreateObject(t, "ReferenceTestCity", map[string]interface{}{
   935  		"name": "My City",
   936  	})
   937  	assertGetObjectEventually(t, cityID)
   938  
   939  	t.Log("5. POST /references/ for place 1")
   940  	postRefParams := objects.NewObjectsReferencesCreateParams().
   941  		WithID(cityID).
   942  		WithPropertyName("hasPlaces").
   943  		WithBody(&models.SingleRef{
   944  			Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", place1ID.String())),
   945  		})
   946  	postRefResponse, err := helper.Client(t).Objects.ObjectsReferencesCreate(postRefParams, nil)
   947  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   948  
   949  	actualThunk := func() interface{} {
   950  		city := assertGetObject(t, cityID)
   951  		return city.Properties
   952  	}
   953  	t.Log("7. verify first cross ref was added")
   954  	testhelper.AssertEventuallyEqual(t, map[string]interface{}{
   955  		"name": "My City",
   956  		"hasPlaces": []interface{}{
   957  			map[string]interface{}{
   958  				"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place1ID.String()),
   959  				"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place1ID.String()),
   960  			},
   961  		},
   962  	}, actualThunk)
   963  
   964  	t.Log("8. POST /references/ for place 2")
   965  	postRefParams = objects.NewObjectsReferencesCreateParams().
   966  		WithID(cityID).
   967  		WithPropertyName("hasPlaces").
   968  		WithBody(&models.SingleRef{
   969  			Beacon: strfmt.URI(fmt.Sprintf("weaviate://localhost/%s", place2ID.String())),
   970  		})
   971  	postRefResponse, err = helper.Client(t).Objects.ObjectsReferencesCreate(postRefParams, nil)
   972  	helper.AssertRequestOk(t, postRefResponse, err, nil)
   973  
   974  	t.Log("9. verify both cross refs are present")
   975  	actualThunk = func() interface{} {
   976  		city := assertGetObject(t, cityID)
   977  		return city.Properties.(map[string]interface{})["hasPlaces"].([]interface{})
   978  	}
   979  
   980  	expectedRefs := []interface{}{
   981  		map[string]interface{}{
   982  			"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place1ID.String()),
   983  			"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place1ID.String()),
   984  		},
   985  		map[string]interface{}{
   986  			"beacon": fmt.Sprintf("weaviate://localhost/%s/%s", placeClass.Class, place2ID.String()),
   987  			"href":   fmt.Sprintf("/v1/objects/%s/%s", placeClass.Class, place2ID.String()),
   988  		},
   989  	}
   990  
   991  	testhelper.AssertEventuallyEqual(t, expectedRefs, actualThunk)
   992  }