Skip to content

Commit

Permalink
Merge branch 'release-0.6.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
sneakypete81 committed Feb 2, 2014
2 parents f497adc + ab7d89c commit 209a1da
Show file tree
Hide file tree
Showing 17 changed files with 212 additions and 90 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
Trovebox Python Library Changelog
=================================

v0.6.2
======
* Support Unicode tags (#74, #77)
* Ensure lists inside parameters are UTF-8 encoded (#74, #77)
* Fix repr unicode handling (#75)
* Support unicode filenames (#72, #73)
* Add Pypy to unit testing list (#78)

v0.6.1
======
* Perform user expansion when uploading files from the CLI (#59, #70)
Expand Down
Binary file added tests/data/test_ünicode_photo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions tests/functional/test_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,27 @@ def test_upload_from_url(self):
photos[1].delete()
self.photos[0].update(permission=False)

# Unicode filename upload not working due to frontend bug 1433
@unittest.expectedFailure
def test_upload_unicode_filename(self):
"""Test that a photo with a unicode filename can be uploaded"""
ret_val = self.client.photo.upload(u"tests/data/test_\xfcnicode_photo.jpg",
title=self.TEST_TITLE)
# Check that there are now four photos
self.photos = self.client.photos.list()
self.assertEqual(len(self.photos), 4)

# Check that the upload return value was correct
pathOriginals = [photo.pathOriginal for photo in self.photos]
self.assertIn(ret_val.pathOriginal, pathOriginals)

# Delete the photo
ret_val.delete()

# Check that it's gone
self.photos = self.client.photos.list()
self.assertEqual(len(self.photos), 3)

def test_update(self):
""" Update a photo by editing the title """
title = "\xfcmlaut" # umlauted umlaut
Expand Down
1 change: 1 addition & 0 deletions tests/unit/data/ünicode_test_file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test File
8 changes: 4 additions & 4 deletions tests/unit/test_activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ def test_zero_rows(self, mock_get):

@mock.patch.object(trovebox.Trovebox, 'get')
def test_options(self, mock_get):
"""Check that the activity list optionss are applied properly"""
"""Check that the activity list options are applied properly"""
mock_get.return_value = self._return_value(self.test_activities_dict)
self.client.activities.list(options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict element can be any order
self.assertIn(mock_get.call_args[0],
[("/activities/foo-bar/test1-test2/list.json",),
("/activities/test1-test2/foo-bar/list.json",)])
[("/activities/foo-bar/test1-%C3%BCmlaut/list.json",),
("/activities/test1-%C3%BCmlaut/foo-bar/list.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})

class TestActivitiesPurge(TestActivities):
Expand Down
41 changes: 41 additions & 0 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def raise_exception(_):

class TestCli(unittest.TestCase):
test_file = os.path.join("tests", "unit", "data", "test_file.txt")
test_unicode_file = os.path.join("tests", "unit", "data",
"\xfcnicode_test_file.txt")

@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
Expand Down Expand Up @@ -107,6 +109,45 @@ def test_post_missing_files(self, _, mock_trovebox):
with self.assertRaises(IOError):
main(["-X", "POST", "-F", "photo=@%s.missing" % self.test_file])

@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_post_unicode_files(self, _, mock_trovebox):
"""Check that unicode filenames are posted correctly"""
post = mock_trovebox.return_value.post

# Python 2.x provides encoded commandline arguments
file_param = "photo=@%s" % self.test_unicode_file
if sys.version < '3':
file_param = file_param.encode(sys.getfilesystemencoding())

main(["-X", "POST", "-F", "photo=@%s" % self.test_unicode_file])
# It's not possible to directly compare the file object,
# so check it manually
files = post.call_args[1]["files"]
self.assertEqual(list(files.keys()), ["photo"])
self.assertEqual(files["photo"].name, self.test_unicode_file)

@unittest.skipIf(sys.version >= '3',
"Python3 only uses unicode commandline arguments")
@mock.patch('trovebox.main.sys.getfilesystemencoding')
@mock.patch.object(trovebox.main.trovebox, "Trovebox")
@mock.patch('sys.stdout', new_callable=io.StringIO)
def test_post_utf8_files(self, _, mock_trovebox, mock_getfilesystemencoding):
"""Check that utf-8 encoded filenames are posted correctly"""
post = mock_trovebox.return_value.post
# Make the system think its filesystemencoding is utf-8
mock_getfilesystemencoding.return_value = "utf-8"

file_param = "photo=@%s" % self.test_unicode_file
file_param = file_param.encode("utf-8")

main(["-X", "POST", "-F", file_param])
# It's not possible to directly compare the file object,
# so check it manually
files = post.call_args[1]["files"]
self.assertEqual(list(files.keys()), ["photo"])
self.assertEqual(files["photo"].name, self.test_unicode_file)

@mock.patch.object(sys, "exit", raise_exception)
@mock.patch('sys.stderr', new_callable=io.StringIO)
def test_unknown_arg(self, mock_stderr):
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,17 @@ def test_get_parameter_processing(self):
self.client.get(self.test_endpoint,
photo=photo, album=album, tag=tag,
list_=[photo, album, tag],
list2=["1", "2", "3"],
list2=["1", False, 3],
unicode_list=["1", "2", "\xfcmlaut"],
boolean=True,
unicode_="\xfcmlaut")
params = self._last_request().querystring
self.assertEqual(params["photo"], ["photo_id"])
self.assertEqual(params["album"], ["album_id"])
self.assertEqual(params["tag"], ["tag_id"])
self.assertEqual(params["list_"], ["photo_id,album_id,tag_id"])
self.assertEqual(params["list2"], ["1,2,3"])
self.assertEqual(params["list2"], ["1,0,3"])
self.assertIn(params["unicode_list"], [["1,2,\xc3\xbcmlaut"], ["1,2,\xfcmlaut"]])
self.assertEqual(params["boolean"], ["1"])
self.assertIn(params["unicode_"], [["\xc3\xbcmlaut"], ["\xfcmlaut"]])

Expand Down
51 changes: 28 additions & 23 deletions tests/unit/test_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,27 @@ def test_zero_rows(self, mock_get):

@mock.patch.object(trovebox.Trovebox, 'get')
def test_options(self, mock_get):
"""Check that the activity list options are applied properly"""
"""Check that the photo list options are applied properly"""
mock_get.return_value = self._return_value(self.test_photos_dict)
self.client.photos.list(options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict element can be any order
self.assertIn(mock_get.call_args[0],
[("/photos/foo-bar/test1-test2/list.json",),
("/photos/test1-test2/foo-bar/list.json",)])
[("/photos/foo-bar/test1-%C3%BCmlaut/list.json",),
("/photos/test1-%C3%BCmlaut/foo-bar/list.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})

class TestPhotosShare(TestPhotos):
@mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_share(self, mock_post):
self.client.photos.share(options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict element can be any order
self.assertIn(mock_post.call_args[0],
[("/photos/foo-bar/test1-test2/share.json",),
("/photos/test1-test2/foo-bar/share.json",)])
[("/photos/foo-bar/test1-%C3%BCmlaut/share.json",),
("/photos/test1-%C3%BCmlaut/foo-bar/share.json",)])
self.assertEqual(mock_post.call_args[1], {"foo": "bar"})

class TestPhotosUpdate(TestPhotos):
Expand Down Expand Up @@ -363,12 +363,12 @@ def test_photo_view(self, mock_get):
mock_get.return_value = self._return_value(self.test_photos_dict[1])
result = self.client.photo.view(self.test_photos[0],
options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
returnSizes="20x20")
# Dict elemet can be in any order
self.assertIn(mock_get.call_args[0],
[("/photo/1a/foo-bar/test1-test2/view.json",),
("/photo/1a/test1-test2/foo-bar/view.json",)])
[("/photo/1a/foo-bar/test1-%C3%BCmlaut/view.json",),
("/photo/1a/test1-%C3%BCmlaut/foo-bar/view.json",)])
self.assertEqual(mock_get.call_args[1], {"returnSizes": "20x20"})
self.assertEqual(result.get_fields(), self.test_photos_dict[1])

Expand All @@ -378,13 +378,13 @@ def test_photo_view_id(self, mock_get):
mock_get.return_value = self._return_value(self.test_photos_dict[1])
result = self.client.photo.view("1a",
options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
returnSizes="20x20")

# Dict elemet can be in any order
self.assertIn(mock_get.call_args[0],
[("/photo/1a/foo-bar/test1-test2/view.json",),
("/photo/1a/test1-test2/foo-bar/view.json",)])
[("/photo/1a/foo-bar/test1-%C3%BCmlaut/view.json",),
("/photo/1a/test1-%C3%BCmlaut/foo-bar/view.json",)])
self.assertEqual(mock_get.call_args[1], {"returnSizes": "20x20"})
self.assertEqual(result.get_fields(), self.test_photos_dict[1])

Expand Down Expand Up @@ -419,7 +419,7 @@ def test_photo_upload(self, mock_post):
files = mock_post.call_args[1]["files"]
self.assertEqual(endpoint, ("/photo/upload.json",))
self.assertEqual(title, "Test")
self.assertIn("photo", files)
self.assertEqual(files["photo"].name, self.test_file)
self.assertEqual(result.get_fields(), self.test_photos_dict[0])

class TestPhotoUploadEncoded(TestPhotos):
Expand Down Expand Up @@ -455,12 +455,12 @@ def test_photo_next_previous(self, mock_get):
"previous": [self.test_photos_dict[1]]})
result = self.client.photo.next_previous(self.test_photos[0],
options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict elemet can be in any order
self.assertIn(mock_get.call_args[0],
[("/photo/1a/nextprevious/foo-bar/test1-test2.json",),
("/photo/1a/nextprevious/test1-test2/foo-bar.json",)])
[("/photo/1a/nextprevious/foo-bar/test1-%C3%BCmlaut.json",),
("/photo/1a/nextprevious/test1-%C3%BCmlaut/foo-bar.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})
self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0])
Expand All @@ -478,12 +478,12 @@ def test_photo_next_previous_id(self, mock_get):
"previous": [self.test_photos_dict[1]]})
result = self.client.photo.next_previous("1a",
options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict elemet can be in any order
self.assertIn(mock_get.call_args[0],
[("/photo/1a/nextprevious/foo-bar/test1-test2.json",),
("/photo/1a/nextprevious/test1-test2/foo-bar.json",)])
[("/photo/1a/nextprevious/foo-bar/test1-%C3%BCmlaut.json",),
("/photo/1a/nextprevious/test1-%C3%BCmlaut/foo-bar.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})
self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0])
Expand All @@ -500,12 +500,12 @@ def test_photo_object_next_previous(self, mock_get):
{"next": [self.test_photos_dict[0]],
"previous": [self.test_photos_dict[1]]})
result = self.test_photos[0].next_previous(options={"foo": "bar",
"test1": "test2"},
"test1": "\xfcmlaut"},
foo="bar")
# Dict elemet can be in any order
self.assertIn(mock_get.call_args[0],
[("/photo/1a/nextprevious/foo-bar/test1-test2.json",),
("/photo/1a/nextprevious/test1-test2/foo-bar.json",)])
[("/photo/1a/nextprevious/foo-bar/test1-%C3%BCmlaut.json",),
("/photo/1a/nextprevious/test1-%C3%BCmlaut/foo-bar.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})
self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0])
Expand Down Expand Up @@ -606,6 +606,11 @@ def test_photo_object_repr_with_id_and_name(self):
"name": "Test Name"})
self.assertEqual(repr(photo), "<Photo name='Test Name'>")

def test_photo_object_repr_with_unicode_id(self):
""" Ensure that a unicode id is correctly represented """
photo = trovebox.objects.photo.Photo(self.client, {"id": "\xfcmlaut"})
self.assertIn(repr(photo), [b"<Photo id='\xc3\xbcmlaut'>", "<Photo id='\xfcmlaut'>"])

@mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_object_create_attribute(self, _):
"""
Expand Down
24 changes: 22 additions & 2 deletions tests/unit/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
class TestTags(unittest.TestCase):
test_host = "test.example.com"
test_tags = None
test_tags_dict = [{"count": 11, "id":"tag1"},
{"count": 5, "id":"tag2"}]
test_tags_dict = [{"count": 11, "id": "tag1"},
{"count": 5, "id": "tag2"}]

test_tag_unicode_dict = {"id": "\xfcmlaut"}

def setUp(self):
self.client = trovebox.Trovebox(host=self.test_host)
self.test_tags = [trovebox.objects.tag.Tag(self.client, tag)
for tag in self.test_tags_dict]
self.test_tag_unicode = trovebox.objects.tag.Tag(self.client,
self.test_tag_unicode_dict)

@staticmethod
def _return_value(result, message="", code=200):
Expand Down Expand Up @@ -89,6 +93,14 @@ def test_tag_object_delete(self, mock_post):
self.assertEqual(tag.get_fields(), {})
self.assertEqual(tag.id, None)

@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_object_delete_unicode(self, mock_post):
"""Check that a unicode tag can be deleted using its ID"""
mock_post.return_value = self._return_value(True)
result = self.client.tag.delete(self.test_tag_unicode)
mock_post.assert_called_with("/tag/%C3%BCmlaut/delete.json")
self.assertEqual(result, True)

class TestTagUpdate(TestTags):
@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_update(self, mock_post):
Expand Down Expand Up @@ -118,3 +130,11 @@ def test_tag_object_update(self, mock_post):
self.assertEqual(tag.id, "tag2")
self.assertEqual(tag.count, 5)

@mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_object_update_unicode(self, mock_post):
"""Check that a unicode tag can be updated using its ID"""
mock_post.return_value = self._return_value(self.test_tag_unicode_dict)
result = self.client.tag.update(self.test_tag_unicode, name="Test")
mock_post.assert_called_with("/tag/%C3%BCmlaut/update.json", name="Test")
self.assertEqual(result.id, "\xfcmlaut")

2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py26, py27, py33, coverage
envlist = py26, py27, py33, pypy, coverage

[testenv]
commands = python -m unittest discover tests/unit
Expand Down
Loading

0 comments on commit 209a1da

Please sign in to comment.