From 0b01cb430bc416b8d83001b8cc339f741f9cc0a6 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Wed, 8 Jun 2022 10:32:05 +0900 Subject: [PATCH 01/12] add stream insert test case Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index bda502d8ff..43f4fe995b 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -1435,6 +1435,91 @@ func Test_server_StreamInsert(t *testing.T) { } return nil } + /* + - Equivalence Class Testing + - uint8, float32 + - case 1.1: Success to StreamInsert 1 vector (vector type is uint8) + - case 1.2: Success to StreamInsert 1 vector (vector type is float32) + - case 1.3: Success to StreamInsert 100 vector (vector type is uint8) + - case 1.4: Success to StreamInsert 100 vector (vector type is float32) + - case 1.5: Success to StreamInsert 0 vector (vector type is uint8) + - case 1.6: Success to StreamInsert 0 vector (vector type is float32) + - case 2.1: Fail to StreamInsert 1 vector with different dimension (vector type is uint8) + - case 2.2: Fail to StreamInsert 1 vector with different dimension (vector type is float32) + - case 3.1: Fail to StreamInsert 100 vector with 1 vector with different dimension (vector type is uint8) + - case 3.2: Fail to StreamInsert 100 vector with 1 vector with different dimension (vector type is float32) + - case 3.3: Fail to StreamInsert 100 vector with 50 vector with different dimension (vector type is uint8) + - case 3.4: Fail to StreamInsert 100 vector with 50 vector with different dimension (vector type is float32) + - case 3.5: Fail to StreamInsert 100 vector with all vector with different dimension (vector type is uint8) + - case 3.6: Fail to StreamInsert 100 vector with all vector with different dimension (vector type is float32) + - Boundary Value Testing + - uint8, float32 (with 100 insert request in a single StreamInsert request) + - case 1.1: Success to StreamInsert with 0 value vector (vector type is uint8) + - case 1.2: Success to StreamInsert with 0 value vector (vector type is float32) + - case 2.1: Success to StreamInsert with min value vector (vector type is uint8) + - case 2.2: Success to StreamInsert with min value vector (vector type is float32) + - case 3.1: Success to StreamInsert with max value vector (vector type is uint8) + - case 3.2: Success to StreamInsert with max value vector (vector type is float32) + - case 4.1: Fail to StreamInsert with 1 request with empty UUID (vector type is uint8) + - case 4.2: Fail to StreamInsert with 1 request with empty UUID (vector type is float32) + - case 4.3: Fail to StreamInsert with 50 request with empty UUID (vector type is uint8) + - case 4.4: Fail to StreamInsert with 50 request with empty UUID (vector type is float32) + - case 4.5: Fail to StreamInsert with all request with empty UUID (vector type is uint8) + - case 4.6: Fail to StreamInsert with all request with empty UUID (vector type is float32) + - case 5.1: Fail to StreamInsert with 1 vector with maximum dimension (vector type is uint8) + - case 5.2: Fail to StreamInsert with 1 vector with maximum dimension (vector type is float32) + - case 5.3: Fail to StreamInsert with 50 vector with maximum dimension (vector type is uint8) + - case 5.4: Fail to StreamInsert with 50 vector with maximum dimension (vector type is float32) + - case 5.5: Fail to StreamInsert with all vector with maximum dimension (vector type is uint8) + - case 5.6: Fail to StreamInsert with all vector with maximum dimension (vector type is float32) + - float32 (with 100 insert request in a single StreamInsert request) + - case 6.1: Success to StreamInsert with NaN value (vector type is float32) + - case 6.2: Success to StreamInsert with +Inf value (vector type is float32) + - case 6.3: Success to StreamInsert with -Inf value (vector type is float32) + - case 6.4: Success to StreamInsert with -0 value (vector type is float32) + - others (with 100 insert request in a single StreamInsert request) + - case 7.1: Fail to StreamInsert with 1 vector with nil insert request + - case 7.2: Fail to StreamInsert with 50 vector with nil insert request + - case 7.3: Fail to StreamInsert with all vector with nil insert request + - case 8.1: Fail to StreamInsert with 1 vector with nil vector + - case 8.2: Fail to StreamInsert with 50 vector with nil vector + - case 8.3: Fail to StreamInsert with all vector with nil vector + - case 9.1: Fail to StreamInsert with 1 vector with empty insert vector + - case 9.2: Fail to StreamInsert with 50 vector with empty insert vector + - case 9.3: Fail to StreamInsert with all vector with empty insert vector + - Decision Table Testing + - duplicated ID (with 100 insert request in a single StreamInsert request) + - case 1.1: Success to StreamInsert with 2 duplicated ID when SkipStrictExistCheck is false + - case 1.2: Success to StreamInsert with all duplicated ID when SkipStrictExistCheck is false + - case 1.3: Success to StreamInsert with 2 duplicated ID when SkipStrictExistCheck is true + - case 1.4: Success to StreamInsert with all duplicated ID when SkipStrictExistCheck is true + - duplicated vector (with 100 insert request in a single StreamInsert request) + - case 2.1: Success to StreamInsert with 2 duplicated vector when SkipStrictExistCheck is false + - case 2.2: Success to StreamInsert with all duplicated vector when SkipStrictExistCheck is false + - case 2.3: Success to StreamInsert with 2 duplicated vector when SkipStrictExistCheck is true + - case 2.4: Success to StreamInsert with all duplicated vector when SkipStrictExistCheck is true + - duplicated ID & duplicated vector (with 100 insert request in a single StreamInsert request) + - case 3.1: Success to StreamInsert with 2 duplicated ID & vector when SkipStrictExistCheck is false + - case 3.2: Success to StreamInsert with all duplicated ID & vector when SkipStrictExistCheck is false + - case 3.3: Success to StreamInsert with 2 duplicated ID & vector when SkipStrictExistCheck is true + - case 3.4: Success to StreamInsert with all duplicated ID & vector when SkipStrictExistCheck is true + // existed in NGT test cases + - existed ID (with 100 insert request in a single StreamInsert request) + - case 4.1: Fail to StreamInsert with 2 existed ID when SkipStrictExistCheck is false + - case 4.2: Fail to StreamInsert with all existed vector when SkipStrictExistCheck is false + - case 4.3: Fail to StreamInsert with 2 existed ID when SkipStrictExistCheck is true + - case 4.4: Fail to StreamInsert with all existed vector when SkipStrictExistCheck is true + - existed vector (with 100 insert request in a single StreamInsert request) + - case 4.1: Success to StreamInsert with 2 existed vector when SkipStrictExistCheck is false + - case 4.2: Success to StreamInsert with all existed vector when SkipStrictExistCheck is false + - case 4.3: Success to StreamInsert with 2 existed vector when SkipStrictExistCheck is true + - case 4.4: Success to StreamInsert with all existed vector when SkipStrictExistCheck is true + - existed ID & existed vector (with 100 insert request in a single StreamInsert request) + - case 4.1: Fail to StreamInsert with 2 existed ID & vector when SkipStrictExistCheck is false + - case 4.2: Fail to StreamInsert with all existed ID & vector when SkipStrictExistCheck is false + - case 4.3: Fail to StreamInsert with 2 existed ID & vector when SkipStrictExistCheck is true + - case 4.4: Fail to StreamInsert with all existed ID & vector when SkipStrictExistCheck is true + */ tests := []test{ // TODO test cases /* From 34ddab2724a9184b0447fff6a321ce32832a3b22 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 13 Jun 2022 14:50:41 +0900 Subject: [PATCH 02/12] update stream insert test case Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 128 +++++++----------- 1 file changed, 48 insertions(+), 80 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index 43f4fe995b..b0b6043131 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -1437,88 +1437,56 @@ func Test_server_StreamInsert(t *testing.T) { } /* - Equivalence Class Testing - - uint8, float32 - - case 1.1: Success to StreamInsert 1 vector (vector type is uint8) - - case 1.2: Success to StreamInsert 1 vector (vector type is float32) - - case 1.3: Success to StreamInsert 100 vector (vector type is uint8) - - case 1.4: Success to StreamInsert 100 vector (vector type is float32) - - case 1.5: Success to StreamInsert 0 vector (vector type is uint8) - - case 1.6: Success to StreamInsert 0 vector (vector type is float32) - - case 2.1: Fail to StreamInsert 1 vector with different dimension (vector type is uint8) - - case 2.2: Fail to StreamInsert 1 vector with different dimension (vector type is float32) - - case 3.1: Fail to StreamInsert 100 vector with 1 vector with different dimension (vector type is uint8) - - case 3.2: Fail to StreamInsert 100 vector with 1 vector with different dimension (vector type is float32) - - case 3.3: Fail to StreamInsert 100 vector with 50 vector with different dimension (vector type is uint8) - - case 3.4: Fail to StreamInsert 100 vector with 50 vector with different dimension (vector type is float32) - - case 3.5: Fail to StreamInsert 100 vector with all vector with different dimension (vector type is uint8) - - case 3.6: Fail to StreamInsert 100 vector with all vector with different dimension (vector type is float32) + - float32 + - case 1.1: Success to StreamInsert 1 vector (vector type is float32) + - case 1.2: Success to StreamInsert 100 vector + - case 1.3: Success to StreamInsert 0 vector + - case 2.1: Fail to StreamInsert 1 vector with different dimension + - case 3.1: Fail to StreamInsert 100 vector with 1 vector with different dimension + - case 3.2: Fail to StreamInsert 100 vector with 50 vector with different dimension + - case 3.3: Fail to StreamInsert 100 vector with all vector with different dimension + - uint8 + - case 4.1: Success to StreamInsert 1 vector (vector type is uint8) - Boundary Value Testing - - uint8, float32 (with 100 insert request in a single StreamInsert request) - - case 1.1: Success to StreamInsert with 0 value vector (vector type is uint8) - - case 1.2: Success to StreamInsert with 0 value vector (vector type is float32) - - case 2.1: Success to StreamInsert with min value vector (vector type is uint8) - - case 2.2: Success to StreamInsert with min value vector (vector type is float32) - - case 3.1: Success to StreamInsert with max value vector (vector type is uint8) - - case 3.2: Success to StreamInsert with max value vector (vector type is float32) - - case 4.1: Fail to StreamInsert with 1 request with empty UUID (vector type is uint8) - - case 4.2: Fail to StreamInsert with 1 request with empty UUID (vector type is float32) - - case 4.3: Fail to StreamInsert with 50 request with empty UUID (vector type is uint8) - - case 4.4: Fail to StreamInsert with 50 request with empty UUID (vector type is float32) - - case 4.5: Fail to StreamInsert with all request with empty UUID (vector type is uint8) - - case 4.6: Fail to StreamInsert with all request with empty UUID (vector type is float32) - - case 5.1: Fail to StreamInsert with 1 vector with maximum dimension (vector type is uint8) - - case 5.2: Fail to StreamInsert with 1 vector with maximum dimension (vector type is float32) - - case 5.3: Fail to StreamInsert with 50 vector with maximum dimension (vector type is uint8) - - case 5.4: Fail to StreamInsert with 50 vector with maximum dimension (vector type is float32) - - case 5.5: Fail to StreamInsert with all vector with maximum dimension (vector type is uint8) - - case 5.6: Fail to StreamInsert with all vector with maximum dimension (vector type is float32) - - float32 (with 100 insert request in a single StreamInsert request) - - case 6.1: Success to StreamInsert with NaN value (vector type is float32) - - case 6.2: Success to StreamInsert with +Inf value (vector type is float32) - - case 6.3: Success to StreamInsert with -Inf value (vector type is float32) - - case 6.4: Success to StreamInsert with -0 value (vector type is float32) - - others (with 100 insert request in a single StreamInsert request) - - case 7.1: Fail to StreamInsert with 1 vector with nil insert request - - case 7.2: Fail to StreamInsert with 50 vector with nil insert request - - case 7.3: Fail to StreamInsert with all vector with nil insert request - - case 8.1: Fail to StreamInsert with 1 vector with nil vector - - case 8.2: Fail to StreamInsert with 50 vector with nil vector - - case 8.3: Fail to StreamInsert with all vector with nil vector - - case 9.1: Fail to StreamInsert with 1 vector with empty insert vector - - case 9.2: Fail to StreamInsert with 50 vector with empty insert vector - - case 9.3: Fail to StreamInsert with all vector with empty insert vector - - Decision Table Testing - - duplicated ID (with 100 insert request in a single StreamInsert request) - - case 1.1: Success to StreamInsert with 2 duplicated ID when SkipStrictExistCheck is false - - case 1.2: Success to StreamInsert with all duplicated ID when SkipStrictExistCheck is false - - case 1.3: Success to StreamInsert with 2 duplicated ID when SkipStrictExistCheck is true - - case 1.4: Success to StreamInsert with all duplicated ID when SkipStrictExistCheck is true - - duplicated vector (with 100 insert request in a single StreamInsert request) - - case 2.1: Success to StreamInsert with 2 duplicated vector when SkipStrictExistCheck is false - - case 2.2: Success to StreamInsert with all duplicated vector when SkipStrictExistCheck is false - - case 2.3: Success to StreamInsert with 2 duplicated vector when SkipStrictExistCheck is true - - case 2.4: Success to StreamInsert with all duplicated vector when SkipStrictExistCheck is true - - duplicated ID & duplicated vector (with 100 insert request in a single StreamInsert request) - - case 3.1: Success to StreamInsert with 2 duplicated ID & vector when SkipStrictExistCheck is false - - case 3.2: Success to StreamInsert with all duplicated ID & vector when SkipStrictExistCheck is false - - case 3.3: Success to StreamInsert with 2 duplicated ID & vector when SkipStrictExistCheck is true - - case 3.4: Success to StreamInsert with all duplicated ID & vector when SkipStrictExistCheck is true + - case 1.1: Success to StreamInsert with 0 value vector (vector type is uint8) + - case 1.2: Success to StreamInsert with 0 value vector (vector type is float32) + - case 2.1: Success to StreamInsert with min value vector (vector type is uint8) + - case 2.2: Success to StreamInsert with min value vector (vector type is float32) + - case 3.1: Success to StreamInsert with max value vector (vector type is uint8) + - case 3.2: Success to StreamInsert with max value vector (vector type is float32) + + - float32 (with 100 insert request in a single StreamInsert connection) + - case 4.1: Success to StreamInsert with NaN value (vector type is float32) + - case 4.2: Success to StreamInsert with +Inf value (vector type is float32) + - case 4.3: Success to StreamInsert with -Inf value (vector type is float32) + - case 4.4: Success to StreamInsert with -0 value (vector type is float32) + - others (with 100 insert request in a single StreamInsert connection) + - case 5.1: Fail to StreamInsert with nil insert request + - case 6.1: Fail to StreamInsert with nil vector + - case 7.1: Fail to StreamInsert with empty insert vector + - case 8.1: Fail to StreamInsert with empty UUID + - case 9.1: Fail to StreamInsert with maximum dimension + - Decision Table Testing (float32) + - duplicated ID (with 100 insert request in a single StreamInsert connection) + - case 1.1: Fail to StreamInsert with duplicated ID when SkipStrictExistCheck is false + - case 1.2: Fail to StreamInsert with duplicated ID when SkipStrictExistCheck is true + - duplicated vector (with 100 insert request in a single StreamInsert connection) + - case 2.1: Success to StreamInsert with duplicated vector when SkipStrictExistCheck is false + - case 2.2: Success to StreamInsert with duplicated vector when SkipStrictExistCheck is true + - duplicated ID & duplicated vector (with 100 insert request in a single StreamInsert connection) + - case 3.1: Fail to StreamInsert with duplicated ID & vector when SkipStrictExistCheck is false + - case 3.2: Fail to StreamInsert with duplicated ID & vector when SkipStrictExistCheck is true + // existed in NGT test cases - - existed ID (with 100 insert request in a single StreamInsert request) - - case 4.1: Fail to StreamInsert with 2 existed ID when SkipStrictExistCheck is false - - case 4.2: Fail to StreamInsert with all existed vector when SkipStrictExistCheck is false - - case 4.3: Fail to StreamInsert with 2 existed ID when SkipStrictExistCheck is true - - case 4.4: Fail to StreamInsert with all existed vector when SkipStrictExistCheck is true - - existed vector (with 100 insert request in a single StreamInsert request) - - case 4.1: Success to StreamInsert with 2 existed vector when SkipStrictExistCheck is false - - case 4.2: Success to StreamInsert with all existed vector when SkipStrictExistCheck is false - - case 4.3: Success to StreamInsert with 2 existed vector when SkipStrictExistCheck is true - - case 4.4: Success to StreamInsert with all existed vector when SkipStrictExistCheck is true - - existed ID & existed vector (with 100 insert request in a single StreamInsert request) - - case 4.1: Fail to StreamInsert with 2 existed ID & vector when SkipStrictExistCheck is false - - case 4.2: Fail to StreamInsert with all existed ID & vector when SkipStrictExistCheck is false - - case 4.3: Fail to StreamInsert with 2 existed ID & vector when SkipStrictExistCheck is true - - case 4.4: Fail to StreamInsert with all existed ID & vector when SkipStrictExistCheck is true + - existed ID (with 100 insert request in a single StreamInsert connection) + - case 4.1: Fail to StreamInsert with existed ID when SkipStrictExistCheck is false + - case 4.2: Fail to StreamInsert with existed ID when SkipStrictExistCheck is true + - existed vector (with 100 insert request in a single StreamInsert connection) + - case 5.1: Success to StreamInsert with existed vector when SkipStrictExistCheck is false + - case 5.2: Success to StreamInsert with existed vector when SkipStrictExistCheck is true + - existed ID & existed vector (with 100 insert request in a single StreamInsert connection) + - case 6.1: Fail to StreamInsert with existed ID & vector when SkipStrictExistCheck is false + - case 6.2: Fail to StreamInsert with existed ID & vector when SkipStrictExistCheck is true */ tests := []test{ // TODO test cases From a5e1947cf67545fedcbae776f2c973b843e51614 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Wed, 15 Jun 2022 10:46:21 +0900 Subject: [PATCH 03/12] implement server stream mock Signed-off-by: kevindiu --- internal/test/mock/server_stream.go | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 internal/test/mock/server_stream.go diff --git a/internal/test/mock/server_stream.go b/internal/test/mock/server_stream.go new file mode 100644 index 0000000000..39422bd3a3 --- /dev/null +++ b/internal/test/mock/server_stream.go @@ -0,0 +1,57 @@ +package mock + +import ( + "context" + + "github.com/vdaas/vald/apis/grpc/v1/payload" + "github.com/vdaas/vald/internal/net/grpc" + "google.golang.org/grpc/metadata" +) + +type StreamInsertServerMock struct { + SendFunc func(*payload.Object_StreamLocation) error + RecvFunc func() (*payload.Insert_Request, error) + grpc.ServerStream +} + +func (m *StreamInsertServerMock) Send(l *payload.Object_StreamLocation) error { + return m.SendFunc(l) +} + +func (m *StreamInsertServerMock) Recv() (*payload.Insert_Request, error) { + return m.RecvFunc() +} + +// ServerStreamMock implements grpc.ServerStream mock implementation. +type ServerStreamMock struct { + SetHeaderFunc func(metadata.MD) error + SendHeaderFunc func(metadata.MD) error + SetTrailerFunc func(metadata.MD) + ContextFunc func() context.Context + SendMsgFunc func(interface{}) error + RecvMsgFunc func(interface{}) error +} + +func (m *ServerStreamMock) SetHeader(md metadata.MD) error { + return m.SetHeaderFunc(md) +} + +func (m *ServerStreamMock) SendHeader(md metadata.MD) error { + return m.SendHeaderFunc(md) +} + +func (m *ServerStreamMock) SetTrailer(md metadata.MD) { + m.SetTrailerFunc(md) +} + +func (m *ServerStreamMock) Context() context.Context { + return m.ContextFunc() +} + +func (m *ServerStreamMock) SendMsg(msg interface{}) error { + return m.SendMsgFunc(msg) +} + +func (m *ServerStreamMock) RecvMsg(msg interface{}) error { + return m.RecvMsgFunc(msg) +} From 739c86cbbb995ad1328953526b6c9c1d6364f881 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Wed, 15 Jun 2022 11:43:43 +0900 Subject: [PATCH 04/12] test implementation Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 117 ++++++++++++------ 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index b0b6043131..ba42b2bb81 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -35,6 +35,7 @@ import ( "github.com/vdaas/vald/internal/test/data/request" "github.com/vdaas/vald/internal/test/data/vector" "github.com/vdaas/vald/internal/test/goleak" + "github.com/vdaas/vald/internal/test/mock" "github.com/vdaas/vald/pkg/agent/core/ngt/service" ) @@ -1435,10 +1436,32 @@ func Test_server_StreamInsert(t *testing.T) { } return nil } + + const ( + intVecDim = 3 + f32VecDim = 3 + ) + var ( + // default NGT configuration for test + // defaultIntSvcCfg = &config.NGT{ + // Dimension: intVecDim, + // DistanceType: ngt.Angle.String(), + // ObjectType: ngt.Uint8.String(), + // KVSDB: &config.KVSDB{}, + // VQueue: &config.VQueue{}, + // } + defaultF32SvcCfg = &config.NGT{ + Dimension: f32VecDim, + DistanceType: ngt.Angle.String(), + ObjectType: ngt.Float.String(), + KVSDB: &config.KVSDB{}, + VQueue: &config.VQueue{}, + } + ) /* - Equivalence Class Testing - float32 - - case 1.1: Success to StreamInsert 1 vector (vector type is float32) + - case 1.1: Success to StreamInsert 1 vector - case 1.2: Success to StreamInsert 100 vector - case 1.3: Success to StreamInsert 0 vector - case 2.1: Fail to StreamInsert 1 vector with different dimension @@ -1446,7 +1469,7 @@ func Test_server_StreamInsert(t *testing.T) { - case 3.2: Fail to StreamInsert 100 vector with 50 vector with different dimension - case 3.3: Fail to StreamInsert 100 vector with all vector with different dimension - uint8 - - case 4.1: Success to StreamInsert 1 vector (vector type is uint8) + - case 4.1: Success to StreamInsert 1 vector - Boundary Value Testing - case 1.1: Success to StreamInsert with 0 value vector (vector type is uint8) - case 1.2: Success to StreamInsert with 0 value vector (vector type is float32) @@ -1489,45 +1512,57 @@ func Test_server_StreamInsert(t *testing.T) { - case 6.2: Fail to StreamInsert with existed ID & vector when SkipStrictExistCheck is true */ tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - stream: nil, - }, - fields: fields { - name: "", - ip: "", - ngt: nil, - eg: nil, - streamConcurrency: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - stream: nil, - }, - fields: fields { - name: "", - ip: "", - ngt: nil, - eg: nil, - streamConcurrency: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ + func() test { + ngt, err := service.New(defaultF32SvcCfg, + service.WithErrGroup(errgroup.Get()), + ) + if err != nil { + t.Error(err) + } + + // h, err := New( + // WithNGT(ngt), + // ) + // if err != nil { + // t.Error(err) + // } + + // s := grpc.NewServer() + // core.RegisterAgentServer(s, h) + // vald.RegisterValdServer(s, h) + + stream := &mock.StreamInsertServerMock{ + ServerStream: &mock.ServerStreamMock{ + ContextFunc: func() context.Context { + return context.Background() + }, + RecvMsgFunc: func(i interface{}) error { + return nil + }, + SendMsgFunc: func(i interface{}) error { + return nil + }, + }, + } + + return test{ + name: "Equivalence Class Testing case 1.1: Success to StreamInsert 1 vector", + args: args{ + stream: stream, + }, + fields: fields{ + name: "", + ip: "", + ngt: ngt, + eg: errgroup.Get(), + streamConcurrency: 1, + }, + want: want{}, + afterFunc: func(a args) { + // s.Stop() + }, + } + }(), } for _, tc := range tests { From e7239b1e6076e4a993c4d532a6474509eeb21eb8 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 11:26:05 +0900 Subject: [PATCH 05/12] impl equivalence class test cases Signed-off-by: kevindiu --- internal/test/data/request/object.go | 17 + .../core/ngt/handler/grpc/insert_test.go | 399 +++++++++++++++--- 2 files changed, 361 insertions(+), 55 deletions(-) diff --git a/internal/test/data/request/object.go b/internal/test/data/request/object.go index 75ca15185b..4dc8697ba7 100644 --- a/internal/test/data/request/object.go +++ b/internal/test/data/request/object.go @@ -36,3 +36,20 @@ func GenObjectLocations(num int, name string, ipAddr string) *payload.Object_Loc } return result } + +func GenObjectStreamLocation(num int, name string, ipAddr string) []*payload.Object_StreamLocation { + result := make([]*payload.Object_StreamLocation, num) + + for i := 0; i < num; i++ { + result[i] = &payload.Object_StreamLocation{ + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: name, + Uuid: "uuid-" + strconv.Itoa(i+1), + Ips: []string{ipAddr}, + }, + }, + } + } + return result +} diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index ba42b2bb81..aa2432d386 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -18,25 +18,41 @@ package grpc import ( "context" "fmt" + "io" "math" "reflect" + "sort" "strings" "testing" "github.com/vdaas/vald/apis/grpc/v1/payload" - "github.com/vdaas/vald/apis/grpc/v1/vald" "github.com/vdaas/vald/internal/config" "github.com/vdaas/vald/internal/core/algorithm/ngt" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/net" + "github.com/vdaas/vald/internal/net/grpc/codes" "github.com/vdaas/vald/internal/net/grpc/errdetails" "github.com/vdaas/vald/internal/net/grpc/status" + "github.com/vdaas/vald/internal/test/comparator" "github.com/vdaas/vald/internal/test/data/request" "github.com/vdaas/vald/internal/test/data/vector" "github.com/vdaas/vald/internal/test/goleak" "github.com/vdaas/vald/internal/test/mock" "github.com/vdaas/vald/pkg/agent/core/ngt/service" + + grpcStatus "google.golang.org/genproto/googleapis/rpc/status" +) + +var ( + objectStreamLocationComparators = []comparator.Option{ + comparator.IgnoreUnexported(payload.Object_StreamLocation{}), + comparator.IgnoreUnexported(payload.Object_Location{}), + comparator.Comparer(func(x, y grpcStatus.Status) bool { + // ignore checking the detail of status + return x.Code == y.Code + }), + } ) func Test_server_Insert(t *testing.T) { @@ -1409,47 +1425,78 @@ func Test_server_Insert(t *testing.T) { func Test_server_StreamInsert(t *testing.T) { t.Parallel() type args struct { - stream vald.Insert_StreamInsertServer + insertReqs []*payload.Insert_Request } type fields struct { name string ip string - ngt service.NGT - eg errgroup.Group streamConcurrency int + ngtCfg *config.NGT + ngtOpts []service.Option } type want struct { - err error + errCode codes.Code + rpcResp []*payload.Object_StreamLocation } type test struct { name string args args fields fields want want - checkFunc func(want, error) error + checkFunc func(want, []*payload.Object_StreamLocation, error) error beforeFunc func(args) afterFunc func(args) } - defaultCheckFunc := func(w want, err error) error { - if !errors.Is(err, w.err) { - return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + sortObjectStreamLocation := func(l []*payload.Object_StreamLocation) { + if l == nil { + return + } + sort.Slice(l, func(i, j int) bool { + if l[i] == nil || l[i].GetLocation() == nil { + return true + } + if l[j] == nil || l[j].GetLocation() == nil { + return false + } + return l[i].GetLocation().Uuid < l[j].GetLocation().Uuid + }) + } + defaultCheckFunc := func(w want, rpcResp []*payload.Object_StreamLocation, err error) error { + if err != nil { + st, ok := status.FromError(err) + if !ok { + return errors.Errorf("got error cannot convert to Status: \"%#v\"", err) + } + if st.Code() != w.errCode { + return errors.Errorf("got code: \"%#v\",\n\t\t\t\twant code: \"%#v\"", st.Code(), w.errCode) + } + } + + // sort the response by the uuid before checking + sortObjectStreamLocation(rpcResp) + sortObjectStreamLocation(w.rpcResp) + + if diff := comparator.Diff(rpcResp, w.rpcResp, objectStreamLocationComparators...); diff != "" { + return errors.Errorf(diff) } return nil } const ( - intVecDim = 3 - f32VecDim = 3 + name = "vald-agent-ngt-1" // agent name + intVecDim = 3 // int vector dimension + f32VecDim = 3 // float32 vector dimension + streamConcurrency = 10 // default stream concurrency ) var ( // default NGT configuration for test - // defaultIntSvcCfg = &config.NGT{ - // Dimension: intVecDim, - // DistanceType: ngt.Angle.String(), - // ObjectType: ngt.Uint8.String(), - // KVSDB: &config.KVSDB{}, - // VQueue: &config.VQueue{}, - // } + defaultIntSvcCfg = &config.NGT{ + Dimension: intVecDim, + DistanceType: ngt.Angle.String(), + ObjectType: ngt.Uint8.String(), + KVSDB: &config.KVSDB{}, + VQueue: &config.VQueue{}, + } defaultF32SvcCfg = &config.NGT{ Dimension: f32VecDim, DistanceType: ngt.Angle.String(), @@ -1457,6 +1504,8 @@ func Test_server_StreamInsert(t *testing.T) { KVSDB: &config.KVSDB{}, VQueue: &config.VQueue{}, } + + ip = net.LoadLocalIP() // agent ip address ) /* - Equivalence Class Testing @@ -1513,53 +1562,254 @@ func Test_server_StreamInsert(t *testing.T) { */ tests := []test{ func() test { - ngt, err := service.New(defaultF32SvcCfg, - service.WithErrGroup(errgroup.Get()), - ) + insertCnt := 1 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) if err != nil { - t.Error(err) + t.Fatal(err) } - // h, err := New( - // WithNGT(ngt), - // ) - // if err != nil { - // t.Error(err) - // } + return test{ + name: "Equivalence Class Testing case 1.1: Success to StreamInsert 1 vector", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } - // s := grpc.NewServer() - // core.RegisterAgentServer(s, h) - // vald.RegisterValdServer(s, h) + return test{ + name: "Equivalence Class Testing case 1.2: Success to StreamInsert 100 vector", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + return test{ + name: "Equivalence Class Testing case 1.3: Success to StreamInsert 0 vector", + args: args{ + insertReqs: nil, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: []*payload.Object_StreamLocation{}, + }, + } + }(), + func() test { + insertCnt := 1 + invalidDim := f32VecDim + 1 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, invalidDim, nil) + if err != nil { + t.Fatal(err) + } - stream := &mock.StreamInsertServerMock{ - ServerStream: &mock.ServerStreamMock{ - ContextFunc: func() context.Context { - return context.Background() - }, - RecvMsgFunc: func(i interface{}) error { - return nil - }, - SendMsgFunc: func(i interface{}) error { - return nil + return test{ + name: "Equivalence Class Testing case 2.1: Fail to StreamInsert 1 vector with different dimension", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: []*payload.Object_StreamLocation{ + { + Payload: &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + }, + }, }, }, } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + + invalidDim := f32VecDim + 1 + invalidReqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, 1, invalidDim, nil) + if err != nil { + t.Fatal(err) + } + + reqs.Requests[0] = invalidReqs.Requests[0] return test{ - name: "Equivalence Class Testing case 1.1: Success to StreamInsert 1 vector", + name: "Equivalence Class Testing case 3.1: Fail to StreamInsert 100 vector with 1 vector with different dimension", args: args{ - stream: stream, + insertReqs: reqs.Requests, }, fields: fields{ - name: "", - ip: "", - ngt: ngt, - eg: errgroup.Get(), - streamConcurrency: 1, + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, }, - want: want{}, - afterFunc: func(a args) { - // s.Stop() + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + + l[0] = &payload.Object_StreamLocation{ + Payload: &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + }, + } + + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + + invalidDim := f32VecDim + 1 + invalidReqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, 50, invalidDim, nil) + if err != nil { + t.Fatal(err) + } + + for i := 0; i < 50; i++ { + reqs.Requests[i] = invalidReqs.Requests[i] + } + + return test{ + name: "Equivalence Class Testing case 3.2: Fail to StreamInsert 100 vector with 50 vector with different dimension", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + + invalidStatus := &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + for i := 0; i < 50; i++ { + l[i] = &payload.Object_StreamLocation{ + Payload: invalidStatus, + } + } + + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + invalidDim := f32VecDim + 1 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, invalidDim, nil) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Equivalence Class Testing case 3.3: Fail to StreamInsert 100 vector with all vector with different dimension", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := make([]*payload.Object_StreamLocation, insertCnt) + + invalidStatus := &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + for i := 0; i < insertCnt; i++ { + l[i] = &payload.Object_StreamLocation{ + Payload: invalidStatus, + } + } + + return l + }(), + }, + } + }(), + func() test { + insertCnt := 1 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, intVecDim, nil) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Equivalence Class Testing case 4.1: Success to StreamInsert 1 vector", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultIntSvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), }, } }(), @@ -1580,16 +1830,55 @@ func Test_server_StreamInsert(t *testing.T) { if test.checkFunc == nil { checkFunc = defaultCheckFunc } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + eg, _ := errgroup.New(ctx) + ngt, err := service.New(test.fields.ngtCfg, + append(test.fields.ngtOpts, service.WithErrGroup(eg))..., + ) + if err != nil { + tt.Fatal(err) + } + + recvIdx := 0 + rpcResp := make([]*payload.Object_StreamLocation, 0) + stream := &mock.StreamInsertServerMock{ + ServerStream: &mock.ServerStreamMock{ + ContextFunc: func() context.Context { + return ctx + }, + RecvMsgFunc: func(i interface{}) error { + insertReqs := test.args.insertReqs + if recvIdx >= len(insertReqs) { + return io.EOF + } + + obj := i.(*payload.Insert_Request) + obj.Vector = insertReqs[recvIdx].Vector + obj.Config = insertReqs[recvIdx].Config + recvIdx++ + + return nil + }, + SendMsgFunc: func(i interface{}) error { + rpcResp = append(rpcResp, i.(*payload.Object_StreamLocation)) + return nil + }, + }, + } + s := &server{ name: test.fields.name, ip: test.fields.ip, - ngt: test.fields.ngt, - eg: test.fields.eg, + ngt: ngt, + eg: eg, streamConcurrency: test.fields.streamConcurrency, } - err := s.StreamInsert(test.args.stream) - if err := checkFunc(test.want, err); err != nil { + err = s.StreamInsert(stream) + if err := checkFunc(test.want, rpcResp, err); err != nil { tt.Errorf("error = %v", err) } }) From 4314e5e1d9363680511ed07bef09511cbbd0ac6f Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 14:45:00 +0900 Subject: [PATCH 06/12] implement boundary value test cases Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 486 ++++++++++++++++-- 1 file changed, 454 insertions(+), 32 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index aa2432d386..88b8182a41 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -1447,6 +1447,35 @@ func Test_server_StreamInsert(t *testing.T) { beforeFunc func(args) afterFunc func(args) } + + const ( + name = "vald-agent-ngt-1" // agent name + intVecDim = 3 // int vector dimension + f32VecDim = 3 // float32 vector dimension + streamConcurrency = 10 // default stream concurrency + maxVecDim = 1 << 18 // reference value for testing, this value is temporary + ) + + var ( + // default NGT configuration for test + defaultIntSvcCfg = &config.NGT{ + Dimension: intVecDim, + DistanceType: ngt.Angle.String(), + ObjectType: ngt.Uint8.String(), + KVSDB: &config.KVSDB{}, + VQueue: &config.VQueue{}, + } + defaultF32SvcCfg = &config.NGT{ + Dimension: f32VecDim, + DistanceType: ngt.Angle.String(), + ObjectType: ngt.Float.String(), + KVSDB: &config.KVSDB{}, + VQueue: &config.VQueue{}, + } + + ip = net.LoadLocalIP() // agent ip address + ) + sortObjectStreamLocation := func(l []*payload.Object_StreamLocation) { if l == nil { return @@ -1482,31 +1511,6 @@ func Test_server_StreamInsert(t *testing.T) { return nil } - const ( - name = "vald-agent-ngt-1" // agent name - intVecDim = 3 // int vector dimension - f32VecDim = 3 // float32 vector dimension - streamConcurrency = 10 // default stream concurrency - ) - var ( - // default NGT configuration for test - defaultIntSvcCfg = &config.NGT{ - Dimension: intVecDim, - DistanceType: ngt.Angle.String(), - ObjectType: ngt.Uint8.String(), - KVSDB: &config.KVSDB{}, - VQueue: &config.VQueue{}, - } - defaultF32SvcCfg = &config.NGT{ - Dimension: f32VecDim, - DistanceType: ngt.Angle.String(), - ObjectType: ngt.Float.String(), - KVSDB: &config.KVSDB{}, - VQueue: &config.VQueue{}, - } - - ip = net.LoadLocalIP() // agent ip address - ) /* - Equivalence Class Testing - float32 @@ -1665,12 +1669,11 @@ func Test_server_StreamInsert(t *testing.T) { } invalidDim := f32VecDim + 1 - invalidReqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, 1, invalidDim, nil) + invalidVecs, err := vector.GenF32Vec(vector.Gaussian, 1, invalidDim) if err != nil { t.Fatal(err) } - - reqs.Requests[0] = invalidReqs.Requests[0] + reqs.Requests[0].Vector.Vector = invalidVecs[0] return test{ name: "Equivalence Class Testing case 3.1: Fail to StreamInsert 100 vector with 1 vector with different dimension", @@ -1708,13 +1711,14 @@ func Test_server_StreamInsert(t *testing.T) { t.Fatal(err) } + invalidInsertCnt := 50 invalidDim := f32VecDim + 1 - invalidReqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, 50, invalidDim, nil) + invalidReqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, invalidInsertCnt, invalidDim, nil) if err != nil { t.Fatal(err) } - for i := 0; i < 50; i++ { + for i := 0; i < invalidInsertCnt; i++ { reqs.Requests[i] = invalidReqs.Requests[i] } @@ -1813,6 +1817,422 @@ func Test_server_StreamInsert(t *testing.T) { }, } }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(intVecDim, 0), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 1.1: Success to StreamInsert with 0 value vector (vector type is uint8)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultIntSvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(f32VecDim, 0), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 1.2: Success to StreamInsert with 0 value vector (vector type is float32)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(intVecDim, math.MinInt), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 2.1: Success to StreamInsert with min value vector (vector type is uint8)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultIntSvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(f32VecDim, -math.MaxFloat32), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 2.2: Success to StreamInsert with min value vector (vector type is float32)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(intVecDim, math.MaxUint8), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 3.1: Success to StreamInsert with max value vector (vector type is uint8)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultIntSvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + reqs := []*payload.Insert_Request{ + { + Vector: &payload.Object_Vector{ + Id: "uuid-1", + Vector: vector.GenSameValueVec(intVecDim, math.MaxFloat32), + }, + }, + } + + return test{ + name: "Boundary Value Testing case 3.2: Success to StreamInsert with max value vector (vector type is float32)", + args: args{ + insertReqs: reqs, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(1, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = vector.GenSameValueVec(intVecDim, float32(math.NaN())) + + return test{ + name: "Boundary Value Testing case 4.1: Success to StreamInsert with NaN value (vector type is float32)", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = vector.GenSameValueVec(intVecDim, float32(math.Inf(+1.0))) + + return test{ + name: "Boundary Value Testing case 4.2: Success to StreamInsert with +Inf value (vector type is float32)", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = vector.GenSameValueVec(intVecDim, float32(math.Inf(-1.0))) + + return test{ + name: "Boundary Value Testing case 4.3: Success to StreamInsert with -Inf value (vector type is float32)", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = vector.GenSameValueVec(intVecDim, float32(math.Copysign(0, -1.0))) + + return test{ + name: "Boundary Value Testing case 4.4: Success to StreamInsert with -0 value (vector type is float32)", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0] = nil + + return test{ + name: "Boundary Value Testing case 5.1: Fail to StreamInsert with nil insert request", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0].Payload = &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = nil + + return test{ + name: "Boundary Value Testing case 6.1: Fail to StreamInsert with nil vector", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0].Payload = &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + return l + }()}, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = []float32{} + + return test{ + name: "Boundary Value Testing case 7.1: Fail to StreamInsert with empty insert vector", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0].Payload = &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Id = "" + + return test{ + name: "Boundary Value Testing case 8.1: Fail to StreamInsert with empty UUID", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0].Payload = &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, nil) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = make([]float32, maxVecDim) + + return test{ + name: "Boundary Value Testing case 9.1: Fail to StreamInsert with maximum dimension", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.InvalidArgument, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0].Payload = &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(codes.InvalidArgument), + }, + } + return l + }(), + }, + } + }(), } for _, tc := range tests { @@ -1856,8 +2276,10 @@ func Test_server_StreamInsert(t *testing.T) { } obj := i.(*payload.Insert_Request) - obj.Vector = insertReqs[recvIdx].Vector - obj.Config = insertReqs[recvIdx].Config + if insertReqs[recvIdx] != nil { + obj.Vector = insertReqs[recvIdx].Vector + obj.Config = insertReqs[recvIdx].Config + } recvIdx++ return nil From 920e05d36ca47d30943c1cbac5686be9536327ee Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 16:30:20 +0900 Subject: [PATCH 07/12] implement decision table tests Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 545 +++++++++++++++--- 1 file changed, 475 insertions(+), 70 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index 88b8182a41..8731d3a776 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -1444,7 +1444,7 @@ func Test_server_StreamInsert(t *testing.T) { fields fields want want checkFunc func(want, []*payload.Object_StreamLocation, error) error - beforeFunc func(args) + beforeFunc func(*testing.T, args, *server) afterFunc func(args) } @@ -1474,8 +1474,24 @@ func Test_server_StreamInsert(t *testing.T) { } ip = net.LoadLocalIP() // agent ip address + + skipStrictExistCheckCfg = &payload.Insert_Config{ + SkipStrictExistCheck: true, + } + strictExistCheckCfg = &payload.Insert_Config{ + SkipStrictExistCheck: false, + } ) + genObjectStreamLoc := func(code codes.Code) *payload.Object_StreamLocation { + return &payload.Object_StreamLocation{ + Payload: &payload.Object_StreamLocation_Status{ + Status: &grpcStatus.Status{ + Code: int32(code), + }, + }, + } + } sortObjectStreamLocation := func(l []*payload.Object_StreamLocation) { if l == nil { return @@ -1649,15 +1665,7 @@ func Test_server_StreamInsert(t *testing.T) { }, want: want{ errCode: codes.InvalidArgument, - rpcResp: []*payload.Object_StreamLocation{ - { - Payload: &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - }, - }, - }, + rpcResp: []*payload.Object_StreamLocation{genObjectStreamLoc(codes.InvalidArgument)}, }, } }(), @@ -1690,15 +1698,7 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - - l[0] = &payload.Object_StreamLocation{ - Payload: &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - }, - } - + l[0] = genObjectStreamLoc(codes.InvalidArgument) return l }(), }, @@ -1738,15 +1738,8 @@ func Test_server_StreamInsert(t *testing.T) { rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - invalidStatus := &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } for i := 0; i < 50; i++ { - l[i] = &payload.Object_StreamLocation{ - Payload: invalidStatus, - } + l[i] = genObjectStreamLoc(codes.InvalidArgument) } return l @@ -1778,15 +1771,8 @@ func Test_server_StreamInsert(t *testing.T) { rpcResp: func() []*payload.Object_StreamLocation { l := make([]*payload.Object_StreamLocation, insertCnt) - invalidStatus := &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } for i := 0; i < insertCnt; i++ { - l[i] = &payload.Object_StreamLocation{ - Payload: invalidStatus, - } + l[i] = genObjectStreamLoc(codes.InvalidArgument) } return l @@ -2092,11 +2078,7 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - l[0].Payload = &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } + l[0] = genObjectStreamLoc(codes.InvalidArgument) return l }(), }, @@ -2125,11 +2107,7 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - l[0].Payload = &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } + l[0] = genObjectStreamLoc(codes.InvalidArgument) return l }()}, } @@ -2157,11 +2135,7 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - l[0].Payload = &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } + l[0] = genObjectStreamLoc(codes.InvalidArgument) return l }(), }, @@ -2190,11 +2164,7 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - l[0].Payload = &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } + l[0] = genObjectStreamLoc(codes.InvalidArgument) return l }(), }, @@ -2223,11 +2193,445 @@ func Test_server_StreamInsert(t *testing.T) { errCode: codes.InvalidArgument, rpcResp: func() []*payload.Object_StreamLocation { l := request.GenObjectStreamLocation(insertCnt, name, ip) - l[0].Payload = &payload.Object_StreamLocation_Status{ - Status: &grpcStatus.Status{ - Code: int32(codes.InvalidArgument), - }, - } + l[0] = genObjectStreamLoc(codes.InvalidArgument) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Id = reqs.Requests[1].Vector.Id + + return test{ + name: "Decision Table Testing case 1.1: Fail to StreamInsert with duplicated ID when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Id = reqs.Requests[1].Vector.Id + + return test{ + name: "Decision Table Testing case 1.2: Fail to StreamInsert with duplicated ID when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = reqs.Requests[1].Vector.Vector + + return test{ + name: "Decision Table Testing case 2.1: Success to StreamInsert with duplicated vector when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Vector = reqs.Requests[1].Vector.Vector + + return test{ + name: "Decision Table Testing case 2.2: Success to StreamInsert with duplicated vector when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Id = reqs.Requests[1].Vector.Id + reqs.Requests[0].Vector.Vector = reqs.Requests[1].Vector.Vector + + return test{ + name: "Decision Table Testing case 3.1: Fail to StreamInsert with duplicated ID & vector when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + reqs.Requests[0].Vector.Id = reqs.Requests[1].Vector.Id + reqs.Requests[0].Vector.Vector = reqs.Requests[1].Vector.Vector + + return test{ + name: "Decision Table Testing case 3.2: Fail to StreamInsert with duplicated ID & vector when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 4.1: Fail to StreamInsert with existed ID when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + iv, err := vector.GenF32Vec(vector.Gaussian, 1, f32VecDim) + if err != nil { + t.Fatal(err) + } + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: reqs.Requests[0].Vector.Id, + Vector: iv[0], + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 4.2: Fail to StreamInsert with existed ID when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + iv, err := vector.GenF32Vec(vector.Gaussian, 1, f32VecDim) + if err != nil { + t.Fatal(err) + } + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: reqs.Requests[0].Vector.Id, + Vector: iv[0], + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 5.1: Success to StreamInsert with existed vector when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: "non-exists-id", + Vector: reqs.Requests[0].Vector.Vector, + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 5.2: Success to StreamInsert with existed vector when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: "non-exists-id", + Vector: reqs.Requests[0].Vector.Vector, + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + rpcResp: request.GenObjectStreamLocation(insertCnt, name, ip), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, strictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 6.1: Fail to StreamInsert with existed ID & vector when SkipStrictExistCheck is false", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: reqs.Requests[0].Vector.Id, + Vector: reqs.Requests[0].Vector.Vector, + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) + return l + }(), + }, + } + }(), + func() test { + insertCnt := 100 + reqs, err := request.GenMultiInsertReq(request.Float, vector.Gaussian, insertCnt, f32VecDim, skipStrictExistCheckCfg) + if err != nil { + t.Fatal(err) + } + + return test{ + name: "Decision Table Testing case 6.2: Fail to StreamInsert with existed ID & vector when SkipStrictExistCheck is true", + args: args{ + insertReqs: reqs.Requests, + }, + fields: fields{ + name: name, + ip: ip, + streamConcurrency: streamConcurrency, + ngtCfg: defaultF32SvcCfg, + }, + beforeFunc: func(t *testing.T, a args, s *server) { + ctx := context.Background() + + ir := &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: reqs.Requests[0].Vector.Id, + Vector: reqs.Requests[0].Vector.Vector, + }, + } + if _, err := s.Insert(ctx, ir); err != nil { + t.Fatal(err) + } + if _, err := s.CreateIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: 1, + }); err != nil { + t.Fatal(err) + } + }, + want: want{ + errCode: codes.AlreadyExists, + rpcResp: func() []*payload.Object_StreamLocation { + l := request.GenObjectStreamLocation(insertCnt, name, ip) + l[0] = genObjectStreamLoc(codes.AlreadyExists) return l }(), }, @@ -2240,16 +2644,6 @@ func Test_server_StreamInsert(t *testing.T) { t.Run(test.name, func(tt *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -2299,6 +2693,17 @@ func Test_server_StreamInsert(t *testing.T) { streamConcurrency: test.fields.streamConcurrency, } + if test.beforeFunc != nil { + test.beforeFunc(tt, test.args, s) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + err = s.StreamInsert(stream) if err := checkFunc(test.want, rpcResp, err); err != nil { tt.Errorf("error = %v", err) From 1ab93c5745dd959f695b2071b8d7f7a0212d8279 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 16:43:35 +0900 Subject: [PATCH 08/12] fix gofumpt Signed-off-by: kevindiu --- .../core/ngt/handler/grpc/insert_test.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index 8731d3a776..fea6a6a578 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -44,16 +44,14 @@ import ( grpcStatus "google.golang.org/genproto/googleapis/rpc/status" ) -var ( - objectStreamLocationComparators = []comparator.Option{ - comparator.IgnoreUnexported(payload.Object_StreamLocation{}), - comparator.IgnoreUnexported(payload.Object_Location{}), - comparator.Comparer(func(x, y grpcStatus.Status) bool { - // ignore checking the detail of status - return x.Code == y.Code - }), - } -) +var objectStreamLocationComparators = []comparator.Option{ + comparator.IgnoreUnexported(payload.Object_StreamLocation{}), + comparator.IgnoreUnexported(payload.Object_Location{}), + comparator.Comparer(func(x, y grpcStatus.Status) bool { + // ignore checking the detail of status + return x.Code == y.Code + }), +} func Test_server_Insert(t *testing.T) { t.Parallel() @@ -2109,7 +2107,8 @@ func Test_server_StreamInsert(t *testing.T) { l := request.GenObjectStreamLocation(insertCnt, name, ip) l[0] = genObjectStreamLoc(codes.InvalidArgument) return l - }()}, + }(), + }, } }(), func() test { From f228b3a9b92726e69ba3816b2fc40e82d2ae10e8 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 17:02:56 +0900 Subject: [PATCH 09/12] add genObjectStreamLocation tests Signed-off-by: kevindiu --- internal/test/data/request/object.go | 1 + internal/test/data/request/object_test.go | 141 ++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/internal/test/data/request/object.go b/internal/test/data/request/object.go index 4dc8697ba7..e706e5883f 100644 --- a/internal/test/data/request/object.go +++ b/internal/test/data/request/object.go @@ -37,6 +37,7 @@ func GenObjectLocations(num int, name string, ipAddr string) *payload.Object_Loc return result } +// GenObjectStreamLocation generate ObjectStreamLocations payload with multiple name and ip with generated uuid. func GenObjectStreamLocation(num int, name string, ipAddr string) []*payload.Object_StreamLocation { result := make([]*payload.Object_StreamLocation, num) diff --git a/internal/test/data/request/object_test.go b/internal/test/data/request/object_test.go index 5aeabfacbc..f71cf39c7c 100644 --- a/internal/test/data/request/object_test.go +++ b/internal/test/data/request/object_test.go @@ -16,6 +16,7 @@ package request import ( + "reflect" "testing" "github.com/google/go-cmp/cmp" @@ -151,3 +152,143 @@ func TestGenObjectLocations(t *testing.T) { }) } } + +func TestGenObjectStreamLocation(t *testing.T) { + type args struct { + num int + name string + ipAddr string + } + type want struct { + want []*payload.Object_StreamLocation + } + type test struct { + name string + args args + want want + checkFunc func(want, []*payload.Object_StreamLocation) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, got []*payload.Object_StreamLocation) error { + if !reflect.DeepEqual(got, w.want) { + return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) + } + return nil + } + tests := []test{ + { + name: "success to generate 1 object stream location", + args: args{ + num: 1, + name: "vald-agent-01", + ipAddr: "127.0.0.1", + }, + want: want{ + want: []*payload.Object_StreamLocation{ + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-1", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + }, + }, + }, + { + name: "success to generate 5 object stream location", + args: args{ + num: 5, + name: "vald-agent-01", + ipAddr: "127.0.0.1", + }, + want: want{ + want: []*payload.Object_StreamLocation{ + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-1", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-2", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-3", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-4", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + { + Payload: &payload.Object_StreamLocation_Location{ + Location: &payload.Object_Location{ + Name: "vald-agent-01", + Uuid: "uuid-5", + Ips: []string{"127.0.0.1"}, + }, + }, + }, + }, + }, + }, + { + name: "success to generate 0 object stream location", + args: args{ + num: 0, + name: "vald-agent-01", + ipAddr: "127.0.0.1", + }, + want: want{ + want: []*payload.Object_StreamLocation{}, + }, + }, + } + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + got := GenObjectStreamLocation(test.args.num, test.args.name, test.args.ipAddr) + if err := checkFunc(test.want, got); err != nil { + tt.Errorf("error = %v", err) + } + + }) + } +} From aa14d32f11e57b7dcb5f0db0b617b4d38dfcbe86 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Mon, 20 Jun 2022 17:03:27 +0900 Subject: [PATCH 10/12] fix Signed-off-by: kevindiu --- internal/test/mock/doc.go | 18 ++++++++++++++++++ internal/test/mock/server_stream.go | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 internal/test/mock/doc.go diff --git a/internal/test/mock/doc.go b/internal/test/mock/doc.go new file mode 100644 index 0000000000..8940b48ab8 --- /dev/null +++ b/internal/test/mock/doc.go @@ -0,0 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Package mock provides mock implementation for testing. +package mock diff --git a/internal/test/mock/server_stream.go b/internal/test/mock/server_stream.go index 39422bd3a3..244e76ce93 100644 --- a/internal/test/mock/server_stream.go +++ b/internal/test/mock/server_stream.go @@ -1,3 +1,18 @@ +// +// Copyright (C) 2019-2022 vdaas.org vald team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// package mock import ( From 1c3b8024df0a48f20c794b9f7d511451ef0a5d60 Mon Sep 17 00:00:00 2001 From: Kevin Diu Date: Mon, 20 Jun 2022 18:23:34 +0900 Subject: [PATCH 11/12] Update internal/test/data/request/object_test.go --- internal/test/data/request/object_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/test/data/request/object_test.go b/internal/test/data/request/object_test.go index f71cf39c7c..4f717950f0 100644 --- a/internal/test/data/request/object_test.go +++ b/internal/test/data/request/object_test.go @@ -288,7 +288,6 @@ func TestGenObjectStreamLocation(t *testing.T) { if err := checkFunc(test.want, got); err != nil { tt.Errorf("error = %v", err) } - }) } } From a8116cc249096303b33993fa0d54142d27783626 Mon Sep 17 00:00:00 2001 From: kevindiu Date: Thu, 23 Jun 2022 10:35:28 +0900 Subject: [PATCH 12/12] use const for default uuid Signed-off-by: kevindiu --- pkg/agent/core/ngt/handler/grpc/insert_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/agent/core/ngt/handler/grpc/insert_test.go b/pkg/agent/core/ngt/handler/grpc/insert_test.go index fea6a6a578..f2365deb1e 100644 --- a/pkg/agent/core/ngt/handler/grpc/insert_test.go +++ b/pkg/agent/core/ngt/handler/grpc/insert_test.go @@ -1452,6 +1452,7 @@ func Test_server_StreamInsert(t *testing.T) { f32VecDim = 3 // float32 vector dimension streamConcurrency = 10 // default stream concurrency maxVecDim = 1 << 18 // reference value for testing, this value is temporary + uuid = "uuid-1" // default uuid ) var ( @@ -1805,7 +1806,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(intVecDim, 0), }, }, @@ -1831,7 +1832,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(f32VecDim, 0), }, }, @@ -1857,7 +1858,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(intVecDim, math.MinInt), }, }, @@ -1883,7 +1884,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(f32VecDim, -math.MaxFloat32), }, }, @@ -1909,7 +1910,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(intVecDim, math.MaxUint8), }, }, @@ -1935,7 +1936,7 @@ func Test_server_StreamInsert(t *testing.T) { reqs := []*payload.Insert_Request{ { Vector: &payload.Object_Vector{ - Id: "uuid-1", + Id: uuid, Vector: vector.GenSameValueVec(intVecDim, math.MaxFloat32), }, },