1 | """ |
||
2 | Copyright George Sibble 2018 |
||
3 | """ |
||
4 | 1 | import b2blaze.b2lib |
|
5 | 1 | from datetime import datetime |
|
6 | 1 | import pytest |
|
7 | 1 | from b2blaze.b2_exceptions import B2Exception, B2RequestError, B2FileNotFoundError |
|
8 | |||
9 | 1 | class TestB2(object): |
|
10 | """ Tests for the b2blaze library """ |
||
11 | |||
12 | 1 | @classmethod |
|
13 | def setup_class(cls): |
||
14 | """ |
||
15 | |||
16 | :return: None |
||
17 | """ |
||
18 | 1 | cls.b2 = b2blaze.b2lib.B2() |
|
19 | 1 | timestamp = datetime.now().strftime('%Y-%m-%d-%H%M%S') |
|
20 | 1 | cls.bucket_name = 'testbucket-' + timestamp |
|
21 | 1 | print('test bucket: ', cls.bucket_name) |
|
22 | |||
23 | # Helper methods |
||
24 | 1 | def test_create_b2_instance(self): |
|
25 | """Create a B2 instance """ |
||
26 | 1 | b2 = b2blaze.b2lib.B2() |
|
27 | |||
28 | |||
29 | 1 | @classmethod |
|
30 | def create_bucket(cls): |
||
31 | 1 | return cls.b2.buckets.create(cls.bucket_name, security=cls.b2.buckets.public) |
|
32 | |||
33 | 1 | @classmethod |
|
34 | def getbucket(cls): |
||
35 | 1 | return cls.b2.buckets.get(bucket_name=cls.bucket_name) or cls.create_bucket() |
|
36 | |||
37 | 1 | @classmethod |
|
38 | 1 | def upload_textfile(cls, contents="hello there", file_name='test/hello.txt'): |
|
39 | """ Upload text file with name 'test/hello.txt' """ |
||
40 | 1 | contents=contents.encode('utf-8') # These fail unless encoded to UTF8 |
|
41 | 1 | bucket = cls.getbucket() |
|
42 | 1 | return bucket.files.upload(contents=contents, file_name=file_name) |
|
43 | |||
44 | |||
45 | 1 | @classmethod |
|
46 | def is_b2_file(cls, obj): |
||
47 | """ hacky method for checking object class/type is B2File""" |
||
48 | 1 | if 'B2File' in str(type(obj)): |
|
49 | 1 | return True |
|
50 | return False |
||
51 | |||
52 | ## Tests ## |
||
53 | 1 | @pytest.mark.bucket |
|
54 | 1 | @pytest.mark.files |
|
55 | 1 | @pytest.mark.versions |
|
56 | def test_create_bucket(self): |
||
57 | """ Create a bucket by name. """ |
||
58 | 1 | self.bucket = self.b2.buckets.create(self.bucket_name, security=self.b2.buckets.public) |
|
59 | assert self.bucket |
||
60 | |||
61 | 1 | @pytest.mark.bucket |
|
62 | def test_get_bucket(self): |
||
63 | """ Get a bucket by name """ |
||
64 | 1 | bucket = self.getbucket() |
|
65 | 1 | assert bucket |
|
66 | |||
67 | 1 | @pytest.mark.bucket |
|
68 | def test_get_all_buckets(self): |
||
69 | """ Get buckets. Number of buckets returned should be >1 """ |
||
70 | 1 | buckets = self.b2.buckets.all() |
|
71 | 1 | assert len(buckets) > 1, "Number of buckets returned should be >1" |
|
72 | |||
73 | 1 | @pytest.mark.bucket |
|
74 | def test_get_nonexistent_bucket(self): |
||
75 | """ Get a bucket which doesn't exist should return None """ |
||
76 | 1 | bucket = self.b2.buckets.get(bucket_name='this doesnt exist') |
|
77 | 1 | assert not bucket |
|
78 | |||
79 | 1 | @pytest.mark.files |
|
80 | def test_create_file_and_retrieve_by_id(self): |
||
81 | """ Create a file and retrieve by ID """ |
||
82 | 1 | bucket = self.getbucket() |
|
83 | 1 | contents='Hello World!'.encode('utf-8') # These fail unless encoded to UTF8 |
|
84 | 1 | file = bucket.files.upload(contents=contents, file_name='test/hello.txt') |
|
85 | 1 | file2 = bucket.files.get(file_id=file.file_id) |
|
86 | |||
87 | # It should be a B2File |
||
88 | 1 | assert self.is_b2_file(file2), 'Should be a B2File object' |
|
89 | |||
90 | |||
91 | 1 | @pytest.mark.files |
|
92 | def test_direct_upload_file(self): |
||
93 | """ Upload binary file """ |
||
94 | 1 | bucket = self.getbucket() |
|
95 | 1 | binary_file = open('b2blaze/test_pic.jpg', 'rb') |
|
96 | 1 | uploaded_file = bucket.files.upload(contents=binary_file, file_name='test_pic2.jpg') |
|
97 | 1 | binary_file.close() |
|
98 | 1 | assert self.is_b2_file(uploaded_file) |
|
99 | |||
100 | |||
101 | 1 | @pytest.mark.files |
|
102 | def test_get_all_files(self): |
||
103 | """ Get all files from a bucket. Returned objects are B2Files """ |
||
104 | 1 | bucket = self.getbucket() |
|
105 | 1 | files = bucket.files.all() |
|
106 | 1 | print('test_get_files: all files: ', len(files)) |
|
107 | |||
108 | # check type |
||
109 | 1 | assert self.is_b2_file(files[0]), 'Should be a B2File object' |
|
110 | |||
111 | |||
112 | |||
113 | 1 | @pytest.mark.versions |
|
114 | 1 | @pytest.mark.files |
|
115 | def test_get_all_file_versions(self): |
||
116 | """ Get all file versions from a bucket """ |
||
117 | 1 | bucket = self.getbucket() |
|
118 | 1 | file = self.upload_textfile() |
|
119 | 1 | files = bucket.files.all_file_versions() |
|
120 | 1 | print('test_get_all_file_versions: all versions: ', len(files['file_versions'])) |
|
121 | 1 | assert len(files['file_versions']) > 0, 'File versions should exist' |
|
122 | |||
123 | |||
124 | 1 | @pytest.mark.files |
|
125 | def test_get_file_by_name(self): |
||
126 | """ Get file by name """ |
||
127 | 1 | bucket = self.getbucket() |
|
128 | 1 | file = self.upload_textfile() |
|
129 | |||
130 | # check type |
||
131 | 1 | assert self.is_b2_file(file), 'Should be a B2File object' |
|
132 | |||
133 | |||
134 | 1 | @pytest.mark.files |
|
135 | def test_get_file_by_id(self): |
||
136 | """ Get file by id """ |
||
137 | 1 | bucket = self.getbucket() |
|
138 | 1 | file = self.upload_textfile() |
|
139 | |||
140 | # check type |
||
141 | 1 | assert self.is_b2_file(file), 'Should be a B2File object' |
|
142 | |||
143 | |||
144 | 1 | @pytest.mark.versions |
|
145 | 1 | @pytest.mark.files |
|
146 | def test_get_file_versions(self): |
||
147 | """ Get all versions of a file via the file.get_versions method. |
||
148 | Returned data should be a list, and items should be of type B2File |
||
149 | """ |
||
150 | 1 | bucket = self.getbucket() |
|
151 | 1 | file = bucket.files.get(file_name='test/hello.txt') |
|
152 | 1 | versions = file.get_versions() |
|
153 | 1 | assert len(versions) > 0, 'File should have multiple versions' |
|
154 | |||
155 | # check type |
||
156 | 1 | assert self.is_b2_file(versions[0]), 'Should be a B2File object' |
|
157 | |||
158 | |||
159 | 1 | @pytest.mark.versions |
|
160 | 1 | @pytest.mark.files |
|
161 | def test_bucket_get_file_versions_by_name(self): |
||
162 | """ Get all versions of a file by name file_list.get_versions method. |
||
163 | Returned data should be a list, and items should be of type B2File |
||
164 | """ |
||
165 | 1 | bucket = self.getbucket() |
|
166 | 1 | versions = bucket.files.get_versions(file_name='test/hello.txt') |
|
167 | assert len(versions) > 0, 'File should have multiple versions' |
||
168 | assert self.is_b2_file(versions[0]), 'Should be a B2File object' |
||
169 | |||
170 | |||
171 | 1 | @pytest.mark.versions |
|
172 | 1 | @pytest.mark.files |
|
173 | def test_bucket_get_file_versions_by_id(self): |
||
174 | """ Get all versions of a file by id file_list.get_versions method. |
||
175 | Returned data should be a list, and items should be of type B2File |
||
176 | """ |
||
177 | 1 | bucket = self.getbucket() |
|
178 | 1 | file = bucket.files.get(file_name='test/hello.txt') |
|
179 | versions = bucket.files.get_versions(file_id=file.file_id) |
||
180 | assert len(versions) > 0, 'File should have multiple versions' |
||
181 | assert self.is_b2_file(versions[0]), 'Should be a B2File object' |
||
182 | |||
183 | |||
184 | 1 | @pytest.mark.files |
|
185 | 1 | @pytest.mark.b2errors |
|
186 | def test_get_file_doesnt_exist(self): |
||
187 | """ Get file which doesn't exist should raise B2FileNotFoundError, get by ID should raise B2RequestError """ |
||
188 | 1 | bucket = self.getbucket() |
|
189 | 1 | with pytest.raises(B2FileNotFoundError): |
|
190 | 1 | file = bucket.files.get(file_name='nope.txt') |
|
191 | 1 | with pytest.raises(B2RequestError): |
|
192 | 1 | file2 = bucket.files.get(file_id='abcd') |
|
193 | |||
194 | |||
195 | 1 | @pytest.mark.files |
|
196 | def test_download_file(self): |
||
197 | """ Get file by id """ |
||
198 | 1 | bucket = self.getbucket() |
|
199 | 1 | file = self.upload_textfile() |
|
200 | 1 | data = file.download() |
|
201 | 1 | assert len(data.read()) > 0 |
|
202 | |||
203 | |||
204 | 1 | @pytest.mark.files |
|
205 | def test_download_url(self): |
||
206 | """ Download file url should be publicly GET accessible """ |
||
207 | 1 | import requests |
|
208 | 1 | bucket = self.getbucket() |
|
209 | 1 | file = self.upload_textfile() |
|
210 | 1 | url = file.url |
|
211 | 1 | downloaded_file = requests.get(url) |
|
212 | 1 | if downloaded_file.status_code != 200: |
|
213 | print(downloaded_file.json()) |
||
214 | raise ValueError |
||
215 | |||
216 | |||
217 | 1 | @pytest.mark.files |
|
218 | 1 | @pytest.mark.b2errors |
|
219 | def test_hide_file(self): |
||
220 | """ Should create + upload, then hide / soft-delete a file by name. |
||
221 | File should no longer be returned when searched by name in bucket. |
||
222 | """ |
||
223 | 1 | bucket = self.getbucket() |
|
224 | 1 | upload = self.upload_textfile(contents='Delete this', file_name='test/deleteme.txt') |
|
225 | |||
226 | # Delete |
||
227 | 1 | print('test_delete_file: upload.file_id', upload.file_id) |
|
228 | 1 | print('test_delete_file: upload.file_name', upload.file_name) |
|
229 | 1 | upload.hide() |
|
230 | |||
231 | # Refresh bucket; getting the the file should fail |
||
232 | 1 | with pytest.raises(B2FileNotFoundError): |
|
233 | 1 | bucket = self.getbucket() |
|
234 | 1 | file = bucket.files.get(file_name=upload.file_name) |
|
235 | 1 | assert not file, 'Deleted file should not be in files list' |
|
236 | |||
237 | |||
238 | 1 | @pytest.mark.files |
|
239 | 1 | @pytest.mark.versions |
|
240 | def test_delete_file_version(self): |
||
241 | """ Delete a file version by name. It should still exist when searched.""" |
||
242 | 1 | bucket = self.getbucket() |
|
243 | |||
244 | # Upload file & delete |
||
245 | 1 | file = self.upload_textfile() |
|
246 | 1 | file2 = self.upload_textfile() |
|
247 | |||
248 | # Update versions |
||
249 | 1 | versions = file.get_versions() |
|
250 | |||
251 | 1 | assert len(versions) > 1, 'File should should have multiple version' |
|
252 | |||
253 | # Delete version |
||
254 | print('test_delete_file_version: file_name', file.file_name) |
||
255 | print('test_delete_file_version: file_id', file.file_id) |
||
256 | file.delete() |
||
257 | |||
258 | # Refresh bucket; getting the the file should fail |
||
259 | file2 = bucket.files.get(file_name=file.file_name) |
||
260 | assert file2, 'Deleted file version only, file should still exist' |
||
261 | assert self.is_b2_file(file2), 'Should be a B2File object' |
||
262 | |||
263 | |||
264 | # @pytest.mark.versions |
||
265 | 1 | @pytest.mark.files |
|
266 | def test_delete_all_file_versions(self): |
||
267 | """ Delete all versions of a file. It should be gone completely from bucket.""" |
||
268 | 1 | bucket = self.getbucket() |
|
269 | |||
270 | # Create file, make sure we have multiple versions |
||
271 | 1 | contents='Hello World!'.encode('utf-8') # These fail unless encoded to UTF8 |
|
272 | 1 | upload = bucket.files.upload(contents=contents, file_name='test/hello.txt') |
|
273 | |||
274 | # Get |
||
275 | # versions = bucket.files.get_versions(file_name='test/hello.txt') |
||
276 | 1 | file = bucket.files.get(file_name='test/hello.txt') |
|
277 | 1 | versions = file.get_versions() |
|
278 | 1 | assert len(versions) > 0, 'File should should have multiple version' |
|
279 | |||
280 | # Delete |
||
281 | 1 | print('test_delete_all_file_versions: file_name', file.file_name) |
|
282 | 1 | print('test_delete_all_file_versions: file_id', file.file_id) |
|
283 | 1 | file.delete_all_versions(confirm=True) |
|
284 | |||
285 | # Refresh bucket; getting the the file should fail |
||
286 | 1 | with pytest.raises(B2FileNotFoundError): |
|
287 | 1 | bucket = self.getbucket() |
|
288 | 1 | file2 = bucket.files.get(file_name=file.file_name) |
|
289 | assert not file2, 'Deleted all file versions, file should not exist' |
||
290 | |||
291 | |||
292 | 1 | @pytest.mark.bucket |
|
293 | def test_delete_non_empty_bucket(self): |
||
294 | """ Delete bucket should fail on bucket non-empty """ |
||
295 | 1 | bucket = self.getbucket() |
|
296 | |||
297 | # Upload file |
||
298 | 1 | self.upload_textfile() |
|
299 | 1 | assert len(bucket.files.all()) > 0, "Bucket should still contain files" |
|
300 | |||
301 | # Should raise exception on non-empty without confirm |
||
302 | 1 | with pytest.raises(B2RequestError): |
|
303 | 1 | bucket.delete() # Try to delete without confirmation |
|
304 | |||
305 | # Bucket should still exist |
||
306 | 1 | assert self.b2.buckets.get(bucket_name=bucket.bucket_name), 'bucket should still exist' |
|
307 | |||
308 | # # Delete with confirmation |
||
309 | # bucket.delete(delete_files=True, confirm_non_empty=True) |
||
310 | |||
311 | # # Bucket should be gone |
||
312 | # assert self.b2.buckets.get(bucket_name=bucket.bucket_name), 'bucket should not exist' |
||
313 | |||
314 | |||
315 | 1 | @pytest.mark.bucket |
|
316 | 1 | @pytest.mark.files |
|
317 | 1 | @pytest.mark.versions |
|
318 | def test_bucket_delete_all_files(self): |
||
319 | """ Delete all files from bucket. """ |
||
320 | 1 | bucket = self.getbucket() |
|
321 | 1 | self.upload_textfile() |
|
322 | |||
323 | 1 | files = bucket.files.all() |
|
324 | 1 | assert len(files) > 0, 'Bucket should still contain files' |
|
325 | |||
326 | # Delete all files |
||
327 | 1 | bucket.files.delete_all(confirm=True) |
|
328 | 1 | assert len(bucket.files.all()) == 0, 'Bucket should be empty' |
|
329 | |||
330 | |||
331 | 1 | @pytest.mark.bucket |
|
332 | def test_delete_bucket(self): |
||
333 | """ Delete empty bucket""" |
||
334 | 1 | bucket = self.getbucket() |
|
335 | |||
336 | # Ascertain it's empty |
||
337 | 1 | files_new = bucket.files.all(include_hidden=True) |
|
338 | 1 | assert len(files_new) == 0, "Bucket should contain no files but contains {}".format(len(files_new)) |
|
339 | |||
340 | # Delete |
||
341 | 1 | bucket.delete() |
|
342 | |||
343 | # Confirm bucket is gone. bucket.get() nonexistent should return None. |
||
344 | 1 | assert not self.b2.buckets.get(bucket_name=bucket.bucket_name), 'Deleted bucket still exists' |
|
345 | |||
346 | 1 | def main(): |
|
347 | import pytest |
||
348 | pytest_args = [ __file__, '--verbose'] |
||
349 | pytest.main(pytest_args) |
||
350 | |||
351 | if __name__ == '__main__': |
||
352 | main() |