// _ _ // __ _____ __ ___ ___ __ _| |_ ___ // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ // \ V V / __/ (_| |\ V /| | (_| | || __/ // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| // // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. // // CONTACT: hello@weaviate.io // package test import ( "context" "math/big" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1" "github.com/weaviate/weaviate/test/helper" "github.com/weaviate/weaviate/test/helper/sample-schema/books" "google.golang.org/grpc/health/grpc_health_v1" ) func idByte(id string) []byte { hexInteger, _ := new(big.Int).SetString(strings.Replace(id, "-", "", -1), 16) return hexInteger.Bytes() } func TestGRPC(t *testing.T) { conn, err := helper.CreateGrpcConnectionClient(":50051") require.NoError(t, err) require.NotNil(t, conn) grpcClient := helper.CreateGrpcWeaviateClient(conn) require.NotNil(t, grpcClient) // delete if exists and then re-create Books class booksClass := books.ClassContextionaryVectorizer() helper.DeleteClass(t, booksClass.Class) helper.CreateClass(t, booksClass) defer helper.DeleteClass(t, booksClass.Class) t.Run("Health Check", func(t *testing.T) { client := grpc_health_v1.NewHealthClient(conn) check, err := client.Check(context.TODO(), &grpc_health_v1.HealthCheckRequest{}) require.NoError(t, err) require.NotNil(t, check) assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING.Enum().Number(), check.Status.Number()) }) t.Run("Batch import", func(t *testing.T) { resp, err := grpcClient.BatchObjects(context.TODO(), &pb.BatchObjectsRequest{ Objects: books.BatchObjects(), }) require.NoError(t, err) require.NotNil(t, resp) }) tests := []struct { name string req *pb.SearchRequest }{ { name: "Search with props", req: &pb.SearchRequest{ Collection: booksClass.Class, Properties: &pb.PropertiesRequest{ NonRefProperties: []string{"title"}, ObjectProperties: []*pb.ObjectPropertiesRequest{ { PropName: "meta", PrimitiveProperties: []string{"isbn"}, ObjectProperties: []*pb.ObjectPropertiesRequest{ { PropName: "obj", PrimitiveProperties: []string{"text"}, }, { PropName: "objs", PrimitiveProperties: []string{"text"}, }, }, }, {PropName: "reviews", PrimitiveProperties: []string{"tags"}}, }, }, Metadata: &pb.MetadataRequest{ Uuid: true, }, Uses_123Api: true, }, }, { name: "Search without props", req: &pb.SearchRequest{ Collection: booksClass.Class, Metadata: &pb.MetadataRequest{ Uuid: true, }, Uses_123Api: true, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { scifi := "sci-fi" resp, err := grpcClient.Search(context.TODO(), tt.req) require.NoError(t, err) require.NotNil(t, resp) require.NotNil(t, resp.Results) assert.Equal(t, len(books.BatchObjects()), len(resp.Results)) for i := range resp.Results { res := resp.Results[i] id := res.Metadata.Id assert.True(t, id == books.Dune.String() || id == books.ProjectHailMary.String() || id == books.TheLordOfTheIceGarden.String()) titleRaw := res.Properties.NonRefProps.Fields["title"] require.NotNil(t, titleRaw) title := titleRaw.GetStringValue() require.NotNil(t, title) metaRaw := res.Properties.NonRefProps.Fields["meta"] require.NotNil(t, metaRaw) meta := metaRaw.GetObjectValue() require.NotNil(t, meta) isbnRaw := meta.GetFields()["isbn"] require.NotNil(t, isbnRaw) isbn := isbnRaw.GetStringValue() require.NotNil(t, isbn) objRaw := meta.GetFields()["obj"] require.NotNil(t, objRaw) obj := objRaw.GetObjectValue() require.NotNil(t, obj) objsRaw := meta.GetFields()["objs"] require.NotNil(t, objsRaw) objs := objsRaw.GetListValue() require.NotNil(t, objs) objEntryRaw := objs.Values[0] require.NotNil(t, objEntryRaw) objEntry := objEntryRaw.GetObjectValue() require.NotNil(t, objEntry) reviewsRaw := res.Properties.NonRefProps.Fields["reviews"] require.NotNil(t, reviewsRaw) reviews := reviewsRaw.GetListValue() require.NotNil(t, reviews) require.Len(t, reviews.Values, 1) review := reviews.Values[0].GetObjectValue() require.NotNil(t, review) tags := review.Fields["tags"].GetListValue() require.NotNil(t, tags) strTags := make([]string, len(tags.Values)) for i, tag := range tags.Values { strTags[i] = tag.GetStringValue() } expectedTitle := "" expectedIsbn := "" expectedTags := []string{} if id == books.Dune.String() { expectedTitle = "Dune" expectedIsbn = "978-0593099322" expectedTags = []string{scifi, "epic"} } if id == books.ProjectHailMary.String() { expectedTitle = "Project Hail Mary" expectedIsbn = "978-0593135204" expectedTags = []string{scifi} } if id == books.TheLordOfTheIceGarden.String() { expectedTitle = "The Lord of the Ice Garden" expectedIsbn = "978-8374812962" expectedTags = []string{scifi, "fantasy"} } assert.Equal(t, expectedTitle, title) assert.Equal(t, expectedIsbn, isbn) assert.Equal(t, expectedTags, strTags) expectedObj := &pb.Properties{ Fields: map[string]*pb.Value{ "text": {Kind: &pb.Value_StringValue{StringValue: "some text"}}, }, } assert.Equal(t, expectedObj, obj) assert.Equal(t, expectedObj, objEntry) } }) } t.Run("Batch delete", func(t *testing.T) { resp, err := grpcClient.BatchDelete(context.TODO(), &pb.BatchDeleteRequest{ Collection: "Books", Filters: &pb.Filters{Operator: pb.Filters_OPERATOR_EQUAL, TestValue: &pb.Filters_ValueText{ValueText: "Dune"}, Target: &pb.FilterTarget{Target: &pb.FilterTarget_Property{Property: "title"}}}, DryRun: true, Verbose: true, }) require.NoError(t, err) require.NotNil(t, resp) require.Equal(t, resp.Matches, int64(1)) require.Equal(t, resp.Successful, int64(1)) require.Equal(t, resp.Failed, int64(0)) require.Equal(t, resp.Objects[0].Uuid, idByte(books.Dune.String())) }) t.Run("gRPC Search removed", func(t *testing.T) { _, err := grpcClient.Search(context.TODO(), &pb.SearchRequest{}) require.NotNil(t, err) }) }