diff --git a/v2/container/test/client_test.go b/v2/container/test/client_test.go new file mode 100644 index 00000000..b22ed3e7 --- /dev/null +++ b/v2/container/test/client_test.go @@ -0,0 +1,816 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "errors" + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/acl" + "github.com/nspcc-dev/neofs-api-go/v2/container" + containerGRPC "github.com/nspcc-dev/neofs-api-go/v2/container/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/service" + "github.com/nspcc-dev/neofs-api-go/v2/signature" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +type testGRPCClient struct { + server containerGRPC.ContainerServiceServer +} + +type testGRPCServer struct { + key *ecdsa.PrivateKey + putResp *container.PutResponse + getResp *container.GetResponse + delResp *container.DeleteResponse + listResp *container.ListResponse + sEaclResp *container.SetExtendedACLResponse + gEaclResp *container.GetExtendedACLResponse + err error +} + +func (s *testGRPCClient) Put(ctx context.Context, in *containerGRPC.PutRequest, opts ...grpc.CallOption) (*containerGRPC.PutResponse, error) { + return s.server.Put(ctx, in) +} + +func (s *testGRPCClient) Delete(ctx context.Context, in *containerGRPC.DeleteRequest, opts ...grpc.CallOption) (*containerGRPC.DeleteResponse, error) { + return s.server.Delete(ctx, in) +} + +func (s *testGRPCClient) Get(ctx context.Context, in *containerGRPC.GetRequest, opts ...grpc.CallOption) (*containerGRPC.GetResponse, error) { + return s.server.Get(ctx, in) +} + +func (s *testGRPCClient) List(ctx context.Context, in *containerGRPC.ListRequest, opts ...grpc.CallOption) (*containerGRPC.ListResponse, error) { + return s.server.List(ctx, in) +} + +func (s *testGRPCClient) SetExtendedACL(ctx context.Context, in *containerGRPC.SetExtendedACLRequest, opts ...grpc.CallOption) (*containerGRPC.SetExtendedACLResponse, error) { + return s.server.SetExtendedACL(ctx, in) +} + +func (s *testGRPCClient) GetExtendedACL(ctx context.Context, in *containerGRPC.GetExtendedACLRequest, opts ...grpc.CallOption) (*containerGRPC.GetExtendedACLResponse, error) { + return s.server.GetExtendedACL(ctx, in) +} + +func (s *testGRPCServer) Put(_ context.Context, req *containerGRPC.PutRequest) (*containerGRPC.PutResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.PutRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.putResp); err != nil { + return nil, err + } + + return container.PutResponseToGRPCMessage(s.putResp), nil +} + +func (s *testGRPCServer) Delete(_ context.Context, req *containerGRPC.DeleteRequest) (*containerGRPC.DeleteResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.DeleteRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.delResp); err != nil { + return nil, err + } + + return container.DeleteResponseToGRPCMessage(s.delResp), nil +} + +func (s *testGRPCServer) Get(_ context.Context, req *containerGRPC.GetRequest) (*containerGRPC.GetResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.GetRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.getResp); err != nil { + return nil, err + } + + return container.GetResponseToGRPCMessage(s.getResp), nil +} + +func (s *testGRPCServer) List(_ context.Context, req *containerGRPC.ListRequest) (*containerGRPC.ListResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.ListRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.listResp); err != nil { + return nil, err + } + + return container.ListResponseToGRPCMessage(s.listResp), nil +} + +func (s *testGRPCServer) SetExtendedACL(_ context.Context, req *containerGRPC.SetExtendedACLRequest) (*containerGRPC.SetExtendedACLResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.SetExtendedACLRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.sEaclResp); err != nil { + return nil, err + } + + return container.SetExtendedACLResponseToGRPCMessage(s.sEaclResp), nil +} + +func (s *testGRPCServer) GetExtendedACL(_ context.Context, req *containerGRPC.GetExtendedACLRequest) (*containerGRPC.GetExtendedACLResponse, error) { + if s.err != nil { + return nil, s.err + } + + // verify request structure + if err := signature.VerifyServiceMessage( + container.GetExtendedACLRequestFromGRPCMessage(req), + ); err != nil { + return nil, err + } + + // sign response structure + if err := signature.SignServiceMessage(s.key, s.gEaclResp); err != nil { + return nil, err + } + + return container.GetExtendedACLResponseToGRPCMessage(s.gEaclResp), nil +} + +func testPutRequest() *container.PutRequest { + cnr := new(container.Container) + cnr.SetBasicACL(1) + + body := new(container.PutRequestBody) + body.SetContainer(cnr) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.PutRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testPutResponse() *container.PutResponse { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + body := new(container.PutResponseBody) + body.SetContainerID(cid) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.PutResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func testGetRequest() *container.GetRequest { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + body := new(container.GetRequestBody) + body.SetContainerID(cid) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.GetRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testGetResponse() *container.GetResponse { + cnr := new(container.Container) + cnr.SetAttributes([]*container.Attribute{}) // w/o this require.Equal fails due to nil and []T{} difference + + body := new(container.GetResponseBody) + body.SetContainer(cnr) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.GetResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func testDelRequest() *container.DeleteRequest { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + body := new(container.DeleteRequestBody) + body.SetContainerID(cid) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.DeleteRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testDelResponse() *container.DeleteResponse { + body := new(container.DeleteResponseBody) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.DeleteResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func testListRequest() *container.ListRequest { + ownerID := new(refs.OwnerID) + ownerID.SetValue([]byte{1, 2, 3}) + + body := new(container.ListRequestBody) + body.SetOwnerID(ownerID) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.ListRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testListResponse() *container.ListResponse { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + body := new(container.ListResponseBody) + body.SetContainerIDs([]*refs.ContainerID{cid}) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.ListResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func testSetEACLRequest() *container.SetExtendedACLRequest { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + eacl := new(acl.Table) + eacl.SetContainerID(cid) + + body := new(container.SetExtendedACLRequestBody) + body.SetEACL(eacl) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.SetExtendedACLRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testSetEACLResponse() *container.SetExtendedACLResponse { + body := new(container.SetExtendedACLResponseBody) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.SetExtendedACLResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func testGetEACLRequest() *container.GetExtendedACLRequest { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + body := new(container.GetExtendedACLRequestBody) + body.SetContainerID(cid) + + meta := new(service.RequestMetaHeader) + meta.SetTTL(1) + + req := new(container.GetExtendedACLRequest) + req.SetBody(body) + req.SetMetaHeader(meta) + + return req +} + +func testGetEACLResponse() *container.GetExtendedACLResponse { + cid := new(refs.ContainerID) + cid.SetValue([]byte{1, 2, 3}) + + eacl := new(acl.Table) + eacl.SetContainerID(cid) + eacl.SetRecords([]*acl.Record{}) // w/o this require.Equal fails due to nil and []T{} difference + + body := new(container.GetExtendedACLResponseBody) + body.SetEACL(eacl) + + meta := new(service.ResponseMetaHeader) + meta.SetTTL(1) + meta.SetXHeaders([]*service.XHeader{}) // w/o this require.Equal fails due to nil and []T{} difference + + resp := new(container.GetExtendedACLResponse) + resp.SetBody(body) + resp.SetMetaHeader(meta) + + return resp +} + +func TestGRPCClient_Put(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.Put(ctx, new(container.PutRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + + t.Run("invalid request structure", func(t *testing.T) { + req := testPutRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.Put(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testPutRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testPutResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + putResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.Put(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +} + +func TestGRPCClient_Get(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.Get(ctx, new(container.GetRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + + t.Run("invalid request structure", func(t *testing.T) { + req := testGetRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.Get(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testGetRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testGetResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + getResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.Get(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +} + +func TestGRPCClient_Delete(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.Delete(ctx, new(container.DeleteRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + + t.Run("invalid request structure", func(t *testing.T) { + req := testDelRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.Delete(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testDelRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testDelResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + delResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.Delete(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +} + +func TestGRPCClient_List(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.List(ctx, new(container.ListRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + + t.Run("invalid request structure", func(t *testing.T) { + req := testListRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.List(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testListRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testListResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + listResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.List(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +} + +func TestGRPCClient_SetEACL(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.SetExtendedACL(ctx, new(container.SetExtendedACLRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + t.Run("invalid request structure", func(t *testing.T) { + req := testSetEACLRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.SetExtendedACL(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testSetEACLRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testSetEACLResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + sEaclResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.SetExtendedACL(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +} + +func TestGRPCClient_GetEACL(t *testing.T) { + ctx := context.TODO() + + cliKey := test.DecodeKey(0) + srvKey := test.DecodeKey(1) + + t.Run("gRPC server error", func(t *testing.T) { + srvErr := errors.New("test server error") + + srv := &testGRPCServer{ + err: srvErr, + } + + cli := &testGRPCClient{ + server: srv, + } + + c, err := container.New(container.WithGRPCServiceClient(cli)) + require.NoError(t, err) + + resp, err := c.GetExtendedACL(ctx, new(container.GetExtendedACLRequest)) + require.True(t, errors.Is(err, srvErr)) + require.Nil(t, resp) + }) + t.Run("invalid request structure", func(t *testing.T) { + req := testGetEACLRequest() + + require.Error(t, signature.VerifyServiceMessage(req)) + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: new(testGRPCServer), + }, + ), + ) + require.NoError(t, err) + + resp, err := c.GetExtendedACL(ctx, req) + require.Error(t, err) + require.Nil(t, resp) + }) + + t.Run("correct response", func(t *testing.T) { + req := testGetEACLRequest() + + require.NoError(t, signature.SignServiceMessage(cliKey, req)) + + resp := testGetEACLResponse() + + c, err := container.New( + container.WithGRPCServiceClient( + &testGRPCClient{ + server: &testGRPCServer{ + key: srvKey, + gEaclResp: resp, + }, + }, + ), + ) + require.NoError(t, err) + + r, err := c.GetExtendedACL(ctx, req) + require.NoError(t, err) + + require.NoError(t, signature.VerifyServiceMessage(r)) + require.Equal(t, resp.GetBody(), r.GetBody()) + require.Equal(t, resp.GetMetaHeader(), r.GetMetaHeader()) + }) +}