KevinStephenson
Adding in weaviate code
b110593
raw
history blame
7.65 kB
// _ _
// __ _____ __ ___ ___ __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
// \ V V / __/ (_| |\ V /| | (_| | || __/
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
// CONTACT: [email protected]
//
package local
import (
"fmt"
"runtime/debug"
"testing"
logrus "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tailor-inc/graphql"
"github.com/weaviate/weaviate/entities/models"
"github.com/weaviate/weaviate/entities/schema"
"github.com/weaviate/weaviate/usecases/config"
"github.com/weaviate/weaviate/usecases/modules"
)
// These tests are component tests for the local package including all its
// subpackages, such as get, getmeta, etc.. However, they only assert that the
// graphql tree can be built under certain circumstances. This helps us to
// catch errors on edge cases like empty schemas, classes with empty
// properties, empty peer lists, peers with empty schemas, etc. However, we
// don't get any guarantee of whether the individual queries resolve
// correctly. For those cases we have unit tests in die individual subpackages
// (i.e. get, getmeta, aggregate, etc.). Additionally we have (a few) e2e
// tests.
func TestBuild_GraphQLNetwork(t *testing.T) {
tests := testCases{
// This tests asserts that an action-only schema doesn't lead to errors.
testCase{
name: "with only objects locally",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "BestLocalAction",
Properties: []*models.Property{
{
DataType: schema.DataTypeText.PropString(),
Name: "myStringProp",
Tokenization: models.PropertyTokenizationWhitespace,
},
},
},
},
},
},
},
// This tests asserts that a things-only schema doesn't lead to errors.
testCase{
name: "with only objects locally",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "BestLocalThing",
Properties: []*models.Property{
{
DataType: schema.DataTypeText.PropString(),
Name: "myStringProp",
Tokenization: models.PropertyTokenizationWhitespace,
},
},
},
},
},
},
},
// This tests asserts that a class without any properties doesn't lead to
// errors.
testCase{
name: "with things without properties locally",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "BestLocalThing",
Properties: []*models.Property{},
},
},
},
},
},
testCase{
name: "without any peers",
localSchema: validSchema(),
},
}
tests.AssertNoError(t)
}
func TestBuild_RefProps(t *testing.T) {
t.Run("expected error logs", func(t *testing.T) {
tests := testCases{
{
name: "build class with nonexistent ref prop",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "ThisClassExists",
Properties: []*models.Property{
{
DataType: []string{"ThisClassDoesNotExist"},
Name: "ofNonexistentClass",
},
},
},
},
},
},
},
}
expectedLogMsg := "ignoring ref prop \"ofNonexistentClass\" on class \"ThisClassExists\", " +
"because it contains reference to nonexistent class [\"ThisClassDoesNotExist\"]"
tests.AssertErrorLogs(t, expectedLogMsg)
})
t.Run("expected success", func(t *testing.T) {
tests := testCases{
{
name: "build class with existing non-circular ref prop",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "ThisClassExists",
Properties: []*models.Property{
{
DataType: []string{"ThisClassAlsoExists"},
Name: "ofExistingClass",
},
},
},
{
Class: "ThisClassAlsoExists",
Properties: []*models.Property{
{
DataType: schema.DataTypeText.PropString(),
Name: "stringProp",
Tokenization: models.PropertyTokenizationWhitespace,
},
},
},
},
},
},
},
{
name: "build class with existing circular ref prop",
localSchema: schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "ThisClassExists",
Properties: []*models.Property{
{
DataType: []string{"ThisClassAlsoExists"},
Name: "ofExistingClass",
},
},
},
{
Class: "ThisClassAlsoExists",
Properties: []*models.Property{
{
DataType: []string{"ThisClassExists"},
Name: "ofExistingClass",
},
},
},
},
},
},
},
}
tests.AssertNoError(t)
})
}
type testCase struct {
name string
localSchema schema.Schema
}
type testCases []testCase
func (tests testCases) AssertNoError(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
modules := modules.NewProvider()
localSchema, err := Build(&test.localSchema, nil, config.Config{}, modules)
require.Nil(t, err, test.name)
schemaObject := graphql.ObjectConfig{
Name: "WeaviateObj",
Description: "Location of the root query",
Fields: localSchema,
}
func() {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v at %s", r, debug.Stack())
}
}()
_, err = graphql.NewSchema(graphql.SchemaConfig{
Query: graphql.NewObject(schemaObject),
})
}()
assert.Nil(t, err, test.name)
})
}
}
// AssertErrorLogs still expects the test to pass without errors,
// but does expect the Build logger to contain errors messages
// from the GQL schema rebuilding thunk
func (tests testCases) AssertErrorLogs(t *testing.T, expectedMsg string) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
modules := modules.NewProvider()
logger, logsHook := logrus.NewNullLogger()
localSchema, err := Build(&test.localSchema, logger, config.Config{}, modules)
require.Nil(t, err, test.name)
schemaObject := graphql.ObjectConfig{
Name: "WeaviateObj",
Description: "Location of the root query",
Fields: localSchema,
}
func() {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v at %s", r, debug.Stack())
}
}()
_, err = graphql.NewSchema(graphql.SchemaConfig{
Query: graphql.NewObject(schemaObject),
})
}()
last := logsHook.LastEntry()
assert.Contains(t, last.Message, expectedMsg)
assert.Nil(t, err)
})
}
}
func validSchema() schema.Schema {
return schema.Schema{
Objects: &models.Schema{
Classes: []*models.Class{
{
Class: "BestLocalThing",
Properties: []*models.Property{
{
DataType: schema.DataTypeText.PropString(),
Name: "myStringProp",
Tokenization: models.PropertyTokenizationWhitespace,
},
},
},
},
},
}
}