KevinStephenson
Adding in weaviate code
b110593
raw
history blame
10.7 kB
// _ _
// __ _____ __ ___ ___ __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
// \ V V / __/ (_| |\ V /| | (_| | || __/
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
// CONTACT: [email protected]
//
package test
// Acceptance tests for objects.
import (
"encoding/json"
"errors"
"fmt"
"testing"
"github.com/go-openapi/strfmt"
"github.com/stretchr/testify/assert"
"github.com/weaviate/weaviate/client/objects"
"github.com/weaviate/weaviate/entities/models"
"github.com/weaviate/weaviate/test/helper"
testhelper "github.com/weaviate/weaviate/test/helper"
)
// run from setup_test.go
func creatingObjects(t *testing.T) {
const fakeObjectId strfmt.UUID = "11111111-1111-1111-1111-111111111111"
t.Run("create object with user specified id", func(t *testing.T) {
var (
id = strfmt.UUID("d47ea61b-0ed7-4e5f-9c05-6d2c0786660f")
className = "TestObject"
// Set all object values to compare
objectTestString = "Test string"
)
// clean up to make sure we can run this test multiple times in a row
defer func() {
params := objects.NewObjectsDeleteParams().WithID(id)
helper.Client(t).Objects.ObjectsDelete(params, nil)
{
params := objects.NewObjectsClassGetParams()
params.WithClassName(className).WithID(id)
_, err := helper.Client(t).Objects.ObjectsClassGet(params, nil)
if err == nil {
t.Errorf("Object %v cannot exist after deletion", id)
}
werr := new(objects.ObjectsClassGetNotFound)
if ok := errors.As(err, &werr); !ok {
t.Errorf("get deleted object err got: %v want: %v", err, werr)
}
}
}()
params := objects.NewObjectsCreateParams().WithBody(
&models.Object{
ID: id,
Class: className,
Properties: map[string]interface{}{
"testString": objectTestString,
},
})
resp, err := helper.Client(t).Objects.ObjectsCreate(params, nil)
// Ensure that the response is OK
helper.AssertRequestOk(t, resp, err, func() {
object := resp.Payload
assert.Regexp(t, strfmt.UUIDPattern, object.ID)
schema, ok := object.Properties.(map[string]interface{})
if !ok {
t.Fatal("The returned schema is not an JSON object")
}
// Check whether the returned information is the same as the data added
assert.Equal(t, objectTestString, schema["testString"])
})
// wait for the object to be created
testhelper.AssertEventuallyEqual(t, id, func() interface{} {
params := objects.NewObjectsClassGetParams()
params.WithClassName(className).WithID(id)
object, err := helper.Client(t).Objects.ObjectsClassGet(params, nil)
if err != nil {
return nil
}
return object.Payload.ID
})
// deprecated: is here because of backward compatibility reasons
testhelper.AssertEventuallyEqual(t, id, func() interface{} {
params := objects.NewObjectsGetParams().WithID(id)
object, err := helper.Client(t).Objects.ObjectsGet(params, nil)
if err != nil {
return nil
}
return object.Payload.ID
})
// Try to create the same object again and make sure it fails
params = objects.NewObjectsCreateParams().WithBody(
&models.Object{
ID: id,
Class: "TestObject",
Properties: map[string]interface{}{
"testString": objectTestString,
},
})
resp, err = helper.Client(t).Objects.ObjectsCreate(params, nil)
helper.AssertRequestFail(t, resp, err, func() {
errResponse, ok := err.(*objects.ObjectsCreateUnprocessableEntity)
if !ok {
t.Fatalf("Did not get not found response, but %#v", err)
}
assert.Equal(t, fmt.Sprintf("id '%s' already exists", id), errResponse.Payload.Error[0].Message)
})
})
// Check if we can create a Object, and that it's properties are stored correctly.
t.Run("creating a object", func(t *testing.T) {
t.Parallel()
// Set all object values to compare
objectTestString := "Test string"
objectTestInt := 1
objectTestBoolean := true
objectTestNumber := 1.337
objectTestDate := "2017-10-06T08:15:30+01:00"
objectTestPhoneNumber := map[string]interface{}{
"input": "0171 11122233",
"defaultCountry": "DE",
}
params := objects.NewObjectsCreateParams().WithBody(
&models.Object{
Class: "TestObject",
Properties: map[string]interface{}{
"testString": objectTestString,
"testWholeNumber": objectTestInt,
"testTrueFalse": objectTestBoolean,
"testNumber": objectTestNumber,
"testDateTime": objectTestDate,
"testPhoneNumber": objectTestPhoneNumber,
},
})
resp, err := helper.Client(t).Objects.ObjectsCreate(params, nil)
// Ensure that the response is OK
helper.AssertRequestOk(t, resp, err, func() {
object := resp.Payload
assert.Regexp(t, strfmt.UUIDPattern, object.ID)
schema, ok := object.Properties.(map[string]interface{})
if !ok {
t.Fatal("The returned schema is not an JSON object")
}
testWholeNumber, _ := schema["testWholeNumber"].(json.Number).Int64()
testNumber, _ := schema["testNumber"].(json.Number).Float64()
expectedParsedPhoneNumber := map[string]interface{}{
"input": "0171 11122233",
"defaultCountry": "DE",
"countryCode": json.Number("49"),
"internationalFormatted": "+49 171 11122233",
"national": json.Number("17111122233"),
"nationalFormatted": "0171 11122233",
"valid": true,
}
// Check whether the returned information is the same as the data added
assert.Equal(t, objectTestString, schema["testString"])
assert.Equal(t, objectTestInt, int(testWholeNumber))
assert.Equal(t, objectTestBoolean, schema["testTrueFalse"])
assert.Equal(t, objectTestNumber, testNumber)
assert.Equal(t, objectTestDate, schema["testDateTime"])
assert.Equal(t, expectedParsedPhoneNumber, schema["testPhoneNumber"])
})
})
// Examples of how a Object can be invalid.
invalidObjectTestCases := []struct {
// What is wrong in this example
mistake string
// the example object, with a mistake.
// this is a function, so that we can use utility functions like
// helper.GetWeaviateURL(), which might not be initialized yet
// during the static construction of the examples.
object func() *models.Object
// Enable the option to perform some extra assertions on the error response
errorCheck func(t *testing.T, err *models.ErrorResponse)
}{
{
mistake: "missing the class",
object: func() *models.Object {
return &models.Object{
Properties: map[string]interface{}{
"testString": "test",
},
}
},
errorCheck: func(t *testing.T, err *models.ErrorResponse) {
assert.Equal(t, "invalid object: the given class is empty", err.Error[0].Message)
},
},
// AUTO_SCHEMA creates classes automatically
// {
// mistake: "non existing class",
// object: func() *models.Object {
// return &models.Object{
// Class: "NonExistingClass",
// Properties: map[string]interface{}{
// "testString": "test",
// },
// }
// },
// errorCheck: func(t *testing.T, err *models.ErrorResponse) {
// assert.Equal(t, fmt.Sprintf("invalid object: class '%s' not present in schema", "NonExistingClass"), err.Error[0].Message)
// },
// },
// AUTO_SCHEMA creates missing properties automatically
// {
// mistake: "non existing property",
// object: func() *models.Object {
// return &models.Object{
// Class: "TestObject",
// Properties: map[string]interface{}{
// "nonExistingProperty": "test",
// },
// }
// },
// errorCheck: func(t *testing.T, err *models.ErrorResponse) {
// assert.Equal(t, fmt.Sprintf("invalid object: "+schema.ErrorNoSuchProperty, "nonExistingProperty", "TestObject"), err.Error[0].Message)
// },
// },
{
/* TODO gh-616: don't count nr of elements in validation. Just validate keys, and _also_ generate an error on superfluous keys.
E.g.
var cref *string
var type_ *string
var locationUrl *string
for key, val := range(propertyValue) {
switch key {
case "beacon": cref = val
case "type": type_ = val
case "locationUrl": locationUrl = val
default:
return fmt.Errof("Unexpected key %s", key)
}
}
if cref == nil { return fmt.Errorf("beacon missing") }
if type_ == nil { return fmt.Errorf("type missing") }
if locationUrl == nil { return fmt.Errorf("locationUrl missing") }
// now everything has a valid state.
*/
mistake: "invalid cref, property missing locationUrl",
object: func() *models.Object {
return &models.Object{
Class: "TestObject",
Properties: map[string]interface{}{
"testReference": map[string]interface{}{
"beacon": fakeObjectId,
"x": nil,
"type": "Object",
},
},
}
},
errorCheck: func(t *testing.T, err *models.ErrorResponse) {
assert.NotNil(t, err)
},
},
{
mistake: "invalid property; assign int to string",
object: func() *models.Object {
return &models.Object{
Class: "TestObject",
Properties: map[string]interface{}{
"testString": 2,
},
}
},
errorCheck: func(t *testing.T, err *models.ErrorResponse) {
assert.Contains(t,
"invalid object: invalid text property 'testString' on class 'TestObject': not a string, but json.Number",
err.Error[0].Message)
},
},
}
// Check that none of the examples of invalid objects can be created.
t.Run("cannot create invalid objects", func(t *testing.T) {
// invalidObjectTestCases defined below this test.
for _, example_ := range invalidObjectTestCases {
t.Run(example_.mistake, func(t *testing.T) {
example := example_ // Needed; example is updated to point to a new test case.
t.Parallel()
params := objects.NewObjectsCreateParams().WithBody(example.object())
resp, err := helper.Client(t).Objects.ObjectsCreate(params, nil)
helper.AssertRequestFail(t, resp, err, func() {
errResponse, ok := err.(*objects.ObjectsCreateUnprocessableEntity)
if !ok {
t.Fatalf("Did not get not found response, but %#v", err)
}
example.errorCheck(t, errResponse.Payload)
})
})
}
})
}