github.com/uber/kraken@v0.1.4/lib/backend/hdfsbackend/client_test.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package hdfsbackend 15 16 import ( 17 "bytes" 18 "errors" 19 "path" 20 "testing" 21 22 "github.com/uber/kraken/core" 23 "github.com/uber/kraken/lib/backend/hdfsbackend/webhdfs" 24 "github.com/uber/kraken/mocks/lib/backend/hdfsbackend/webhdfs" 25 "github.com/uber/kraken/utils/mockutil" 26 "github.com/uber/kraken/utils/randutil" 27 28 "github.com/golang/mock/gomock" 29 "github.com/stretchr/testify/require" 30 ) 31 32 type clientMocks struct { 33 webhdfs *mockwebhdfs.MockClient 34 } 35 36 func newClientMocks(t *testing.T) (*clientMocks, func()) { 37 ctrl := gomock.NewController(t) 38 return &clientMocks{ 39 webhdfs: mockwebhdfs.NewMockClient(ctrl), 40 }, ctrl.Finish 41 } 42 43 func (m *clientMocks) new() *Client { 44 c, err := NewClient(Config{ 45 NameNodes: []string{"some-name-node"}, 46 RootDirectory: "/root", 47 NamePath: "identity", 48 testing: true, 49 }, WithWebHDFS(m.webhdfs)) 50 if err != nil { 51 panic(err) 52 } 53 return c 54 } 55 56 func TestClientFactory(t *testing.T) { 57 require := require.New(t) 58 59 config := Config{ 60 NameNodes: []string{"some-name-node"}, 61 RootDirectory: "/root", 62 NamePath: "identity", 63 testing: true, 64 } 65 f := factory{} 66 _, err := f.Create(config, nil) 67 require.NoError(err) 68 } 69 70 func TestClientStat(t *testing.T) { 71 require := require.New(t) 72 73 mocks, cleanup := newClientMocks(t) 74 defer cleanup() 75 76 client := mocks.new() 77 78 mocks.webhdfs.EXPECT().GetFileStatus("/root/test").Return(webhdfs.FileStatus{Length: 32}, nil) 79 80 info, err := client.Stat(core.NamespaceFixture(), "test") 81 require.NoError(err) 82 require.Equal(core.NewBlobInfo(32), info) 83 } 84 85 func TestClientDownload(t *testing.T) { 86 require := require.New(t) 87 88 mocks, cleanup := newClientMocks(t) 89 defer cleanup() 90 91 client := mocks.new() 92 93 data := randutil.Text(32) 94 95 mocks.webhdfs.EXPECT().Open("/root/test", mockutil.MatchWriter(data)).Return(nil) 96 97 var b bytes.Buffer 98 require.NoError(client.Download(core.NamespaceFixture(), "test", &b)) 99 require.Equal(data, b.Bytes()) 100 } 101 102 func TestClientUpload(t *testing.T) { 103 require := require.New(t) 104 105 mocks, cleanup := newClientMocks(t) 106 defer cleanup() 107 108 client := mocks.new() 109 110 data := randutil.Text(32) 111 112 mocks.webhdfs.EXPECT().Create( 113 mockutil.MatchRegex("/root/_uploads/.+"), mockutil.MatchReader(data)).Return(nil) 114 115 mocks.webhdfs.EXPECT().Mkdirs("/root").Return(nil) 116 117 mocks.webhdfs.EXPECT().Rename(mockutil.MatchRegex("/root/_uploads/.+"), "/root/test").Return(nil) 118 119 require.NoError(client.Upload(core.NamespaceFixture(), "test", bytes.NewReader(data))) 120 } 121 122 func TestClientList(t *testing.T) { 123 // Tests against the following directory structure: 124 // 125 // root/ 126 // foo/ 127 // bar.txt 128 // baz.txt 129 // cats/ 130 // meow.txt 131 // emtpy/ 132 133 tests := []struct { 134 desc string 135 prefix string 136 expected []string 137 }{ 138 {"root", "", []string{"foo/bar.txt", "foo/baz.txt", "foo/cats/meow.txt"}}, 139 {"directory", "foo/cats", []string{"foo/cats/meow.txt"}}, 140 {"emtpy directory", "empty", nil}, 141 } 142 for _, test := range tests { 143 t.Run(test.desc, func(t *testing.T) { 144 require := require.New(t) 145 146 mocks, cleanup := newClientMocks(t) 147 defer cleanup() 148 149 client := mocks.new() 150 151 mocks.webhdfs.EXPECT().ListFileStatus("/root").Return([]webhdfs.FileStatus{{ 152 PathSuffix: "foo", 153 Type: "DIRECTORY", 154 }, { 155 PathSuffix: "empty", 156 Type: "DIRECTORY", 157 }}, nil).MaxTimes(1) 158 159 mocks.webhdfs.EXPECT().ListFileStatus("/root/foo").Return([]webhdfs.FileStatus{{ 160 PathSuffix: "bar.txt", 161 Type: "FILE", 162 }, { 163 PathSuffix: "baz.txt", 164 Type: "FILE", 165 }, { 166 PathSuffix: "cats", 167 Type: "DIRECTORY", 168 }}, nil).MaxTimes(1) 169 170 mocks.webhdfs.EXPECT().ListFileStatus("/root/foo/cats").Return([]webhdfs.FileStatus{{ 171 PathSuffix: "meow.txt", 172 Type: "FILE", 173 }}, nil).MaxTimes(1) 174 175 mocks.webhdfs.EXPECT().ListFileStatus("/root/empty").Return(nil, nil).MaxTimes(1) 176 177 result, err := client.List(test.prefix) 178 require.NoError(err) 179 require.Equal(test.expected, result.Names) 180 }) 181 } 182 } 183 184 func genRandomDirs(n int) []webhdfs.FileStatus { 185 var dirs []webhdfs.FileStatus 186 for i := 0; i < n; i++ { 187 dirs = append(dirs, webhdfs.FileStatus{ 188 PathSuffix: string(randutil.Text(6)), 189 Type: "DIRECTORY", 190 }) 191 } 192 return dirs 193 } 194 195 func initDirectoryTree(mocks *clientMocks, dir string, width, depth int) { 196 if depth == 0 { 197 mocks.webhdfs.EXPECT().ListFileStatus(dir). 198 Return(nil, errors.New("some error")).MaxTimes(1) 199 return 200 } 201 children := genRandomDirs(width) 202 mocks.webhdfs.EXPECT().ListFileStatus(dir).Return(children, nil).MaxTimes(1) 203 for _, c := range children { 204 initDirectoryTree(mocks, path.Join(dir, c.PathSuffix), width, depth-1) 205 } 206 } 207 208 func TestClientListErrorDoesNotLeakGoroutines(t *testing.T) { 209 require := require.New(t) 210 211 mocks, cleanup := newClientMocks(t) 212 defer cleanup() 213 214 client := mocks.new() 215 216 initDirectoryTree(mocks, "/root", 10, 3) // 1000 nodes. 217 218 _, err := client.List("") 219 require.Error(err) 220 }