// Copyright (C) 2018 Storj Labs, Inc. // See LICENSE for copying information. package pdbclient import ( "context" "errors" "fmt" "log" "strings" "testing" "github.com/golang/mock/gomock" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes" "github.com/stretchr/testify/assert" p "storj.io/storj/pkg/paths" "storj.io/storj/pkg/storage/meta" pb "storj.io/storj/protos/pointerdb" ) const ( unauthenticated = "failed API creds" noPathGiven = "file path not given" ) var ( ctx = context.Background() ErrUnauthenticated = errors.New(unauthenticated) ErrNoFileGiven = errors.New(noPathGiven) ) func TestNewPointerDBClient(t *testing.T) { // mocked grpcClient so we don't have // to call the network to test the code ctrl := gomock.NewController(t) defer ctrl.Finish() gc := NewMockPointerDBClient(ctrl) pdb := PointerDB{grpcClient: gc} assert.NotNil(t, pdb) assert.NotNil(t, pdb.grpcClient) } func makePointer(path p.Path, auth []byte) pb.PutRequest { // rps is an example slice of RemotePieces to add to this // REMOTE pointer type. var rps []*pb.RemotePiece rps = append(rps, &pb.RemotePiece{ PieceNum: 1, NodeId: "testId", }) pr := pb.PutRequest{ Path: path.String(), Pointer: &pb.Pointer{ Type: pb.Pointer_REMOTE, Remote: &pb.RemoteSegment{ Redundancy: &pb.RedundancyScheme{ Type: pb.RedundancyScheme_RS, MinReq: 1, Total: 3, RepairThreshold: 2, SuccessThreshold: 3, }, PieceId: "testId", RemotePieces: rps, }, Size: int64(1), }, APIKey: auth, } return pr } func TestPut(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() for i, tt := range []struct { APIKey []byte path p.Path err error errString string }{ {[]byte("abc123"), p.New("file1/file2"), nil, ""}, {[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated}, {[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven}, {[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated}, {[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated}, } { putRequest := makePointer(tt.path, tt.APIKey) errTag := fmt.Sprintf("Test case #%d", i) gc := NewMockPointerDBClient(ctrl) pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey} // here we don't care what type of context we pass gc.EXPECT().Put(gomock.Any(), &putRequest).Return(nil, tt.err) err := pdb.Put(ctx, tt.path, putRequest.Pointer) if err != nil { assert.EqualError(t, err, tt.errString, errTag) } else { assert.NoError(t, err, errTag) } } } func TestGet(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() for i, tt := range []struct { APIKey []byte path p.Path err error errString string }{ {[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated}, {[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven}, {[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated}, {[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated}, {[]byte("abc123"), p.New("file1/file2"), nil, ""}, } { getPointer := makePointer(tt.path, tt.APIKey) getRequest := pb.GetRequest{Path: tt.path.String(), APIKey: tt.APIKey} data, err := proto.Marshal(getPointer.Pointer) if err != nil { log.Fatal("marshaling error: ", err) } byteData := data getResponse := pb.GetResponse{Pointer: byteData} errTag := fmt.Sprintf("Test case #%d", i) gc := NewMockPointerDBClient(ctrl) pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey} gc.EXPECT().Get(gomock.Any(), &getRequest).Return(&getResponse, tt.err) pointer, err := pdb.Get(ctx, tt.path) if err != nil { assert.True(t, strings.Contains(err.Error(), tt.errString), errTag) assert.Nil(t, pointer) } else { assert.NotNil(t, pointer) assert.NoError(t, err, errTag) } } } func TestList(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() for i, tt := range []struct { prefix string startAfter string endBefore string recursive bool limit int metaFlags uint32 APIKey string items []*pb.ListResponse_Item more bool err error errString string }{ {"", "", "", false, 0, meta.None, "", []*pb.ListResponse_Item{}, false, nil, ""}, {"", "", "", false, 0, meta.None, "", []*pb.ListResponse_Item{{}}, false, nil, ""}, {"", "", "", false, -1, meta.None, "", []*pb.ListResponse_Item{}, false, ErrUnauthenticated, unauthenticated}, {"prefix", "after", "before", false, 1, meta.None, "some key", []*pb.ListResponse_Item{ {Path: "a/b/c"}, }, true, nil, ""}, {"prefix", "after", "before", false, 1, meta.All, "some key", []*pb.ListResponse_Item{ {Path: "a/b/c", Pointer: &pb.Pointer{ Size: 1234, CreationDate: ptypes.TimestampNow(), ExpirationDate: ptypes.TimestampNow(), }}, }, true, nil, ""}, {"some/prefix", "start/after", "end/before", true, 123, meta.Size, "some key", []*pb.ListResponse_Item{ {Path: "a/b/c", Pointer: &pb.Pointer{Size: 1234}}, {Path: "x/y", Pointer: &pb.Pointer{Size: 789}}, }, true, nil, ""}, } { errTag := fmt.Sprintf("Test case #%d", i) listRequest := pb.ListRequest{ Prefix: tt.prefix, StartAfter: tt.startAfter, EndBefore: tt.endBefore, Recursive: tt.recursive, Limit: int32(tt.limit), MetaFlags: tt.metaFlags, APIKey: []byte(tt.APIKey), } listResponse := pb.ListResponse{Items: tt.items, More: tt.more} gc := NewMockPointerDBClient(ctrl) pdb := PointerDB{grpcClient: gc, APIKey: []byte(tt.APIKey)} gc.EXPECT().List(gomock.Any(), &listRequest).Return(&listResponse, tt.err) items, more, err := pdb.List(ctx, p.New(tt.prefix), p.New(tt.startAfter), p.New(tt.endBefore), tt.recursive, tt.limit, tt.metaFlags) if err != nil { assert.EqualError(t, err, tt.errString, errTag) assert.False(t, more) assert.Nil(t, items) } else { assert.NoError(t, err, errTag) assert.Equal(t, tt.more, more) assert.NotNil(t, items) assert.Equal(t, len(tt.items), len(items)) for i := 0; i < len(items); i++ { assert.Equal(t, tt.items[i].GetPath(), items[i].Path.String()) assert.Equal(t, tt.items[i].GetPointer().GetSize(), items[i].Pointer.GetSize()) assert.Equal(t, tt.items[i].GetPointer().GetCreationDate(), items[i].Pointer.GetCreationDate()) assert.Equal(t, tt.items[i].GetPointer().GetExpirationDate(), items[i].Pointer.GetExpirationDate()) } } } } func TestDelete(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() for i, tt := range []struct { APIKey []byte path p.Path err error errString string }{ {[]byte("wrong key"), p.New("file1/file2"), ErrUnauthenticated, unauthenticated}, {[]byte("abc123"), p.New(""), ErrNoFileGiven, noPathGiven}, {[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated}, {[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated}, {[]byte("abc123"), p.New("file1/file2"), nil, ""}, } { deleteRequest := pb.DeleteRequest{Path: tt.path.String(), APIKey: tt.APIKey} errTag := fmt.Sprintf("Test case #%d", i) gc := NewMockPointerDBClient(ctrl) pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey} gc.EXPECT().Delete(gomock.Any(), &deleteRequest).Return(nil, tt.err) err := pdb.Delete(ctx, tt.path) if err != nil { assert.EqualError(t, err, tt.errString, errTag) } else { assert.NoError(t, err, errTag) } } }