diff --git a/tests/functional/test_photos.py b/tests/functional/test_photos.py index 2ac225a..34b91ab 100644 --- a/tests/functional/test_photos.py +++ b/tests/functional/test_photos.py @@ -170,9 +170,18 @@ class TestPhotos(test_base.TestBase): self.assertEqual(next_prev["next"][0].id, self.photos[2].id) def test_replace(self): - """ If photo.replace gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.client.photo.replace(None, None) + """ Test that a photo can be replaced with another """ + # Replace the first photo with a copy of the second + original_hash = self.photos[0].hash + self.assertNotEqual(original_hash, self.photos[1].hash) + self.photos[0].replace("tests/data/test_photo2.jpg", + allowDuplicate=True) + # Check that its new hash is correct + self.assertEqual(self.photos[0].hash, self.photos[1].hash) + # Put it back + self.photos[0].replace("tests/data/test_photo1.jpg", + allowDuplicate=True) + self.assertEqual(self.photos[0].hash, original_hash) def test_replace_encoded(self): """ If photo.replace_encoded gets implemented, write a test! """ diff --git a/tests/unit/test_photos.py b/tests/unit/test_photos.py index ecc1604..4415ccd 100644 --- a/tests/unit/test_photos.py +++ b/tests/unit/test_photos.py @@ -180,41 +180,107 @@ class TestPhotoDeleteSource(TestPhotos): class TestPhotoReplace(TestPhotos): @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_replace(self, _): - """ If photo.replace gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.client.photo.replace(self.test_photos[0], self.test_file) + def test_photo_replace(self, mock_post): + """Check that an existing photo can be replaced""" + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + result = self.client.photo.replace(self.test_photos[1], + self.test_file, title="Test") + # It's not possible to compare the file object, + # so check each parameter individually + endpoint = mock_post.call_args[0] + title = mock_post.call_args[1]["title"] + files = mock_post.call_args[1]["files"] + self.assertEqual(endpoint, + ("/photo/%s/replace.json" % self.test_photos[1].id,)) + self.assertEqual(title, "Test") + self.assertIn("photo", files) + self.assertEqual(result.get_fields(), self.test_photos_dict[0]) @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_replace_id(self, _): - """ If photo.replace gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.client.photo.replace("1a", self.test_file) + def test_photo_replace_id(self, mock_post): + """Check that an existing photo can be replaced using its ID""" + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + result = self.client.photo.replace(self.test_photos[1].id, + self.test_file, title="Test") + # It's not possible to compare the file object, + # so check each parameter individually + endpoint = mock_post.call_args[0] + title = mock_post.call_args[1]["title"] + files = mock_post.call_args[1]["files"] + self.assertEqual(endpoint, + ("/photo/%s/replace.json" % self.test_photos[1].id,)) + self.assertEqual(title, "Test") + self.assertIn("photo", files) + self.assertEqual(result.get_fields(), self.test_photos_dict[0]) @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_object_replace(self, _): - """ If photo.replace gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.test_photos[0].replace(self.test_file) + def test_photo_object_replace(self, mock_post): + """ + Check that an existing photo can be replaced when using the + Photo object directly. + """ + photo_id = self.test_photos[1].id + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + self.test_photos[1].replace(self.test_file, title="Test") + # It's not possible to compare the file object, + # so check each parameter individually + endpoint = mock_post.call_args[0] + title = mock_post.call_args[1]["title"] + files = mock_post.call_args[1]["files"] + self.assertEqual(endpoint, ("/photo/%s/replace.json" % photo_id,)) + self.assertEqual(title, "Test") + self.assertIn("photo", files) + self.assertEqual(self.test_photos[1].get_fields(), + self.test_photos_dict[0]) @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_replace_encoded(self, _): - """ If photo.replace_encoded gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.client.photo.replace_encoded(self.test_photos[0], - self.test_file) + def test_photo_replace_encoded(self, mock_post): + """ + Check that a photo can be uploaded using Base64 encoding to + replace an existing photo. + """ + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + result = self.client.photo.replace_encoded(self.test_photos[1], + self.test_file, title="Test") + with open(self.test_file, "rb") as in_file: + encoded_file = base64.b64encode(in_file.read()) + mock_post.assert_called_with("/photo/%s/replace.json" + % self.test_photos[1].id, + photo=encoded_file, title="Test") + self.assertEqual(result.get_fields(), self.test_photos_dict[0]) @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_replace_encoded_id(self, _): - """ If photo.replace_encoded gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.client.photo.replace_encoded("1a", self.test_file) + def test_photo_replace_encoded_id(self, mock_post): + """ + Check that a photo can be uploaded using Base64 encoding to + replace an existing photo using its ID. + """ + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + result = self.client.photo.replace_encoded(self.test_photos[1].id, + self.test_file, title="Test") + with open(self.test_file, "rb") as in_file: + encoded_file = base64.b64encode(in_file.read()) + mock_post.assert_called_with("/photo/%s/replace.json" + % self.test_photos[1].id, + photo=encoded_file, title="Test") + self.assertEqual(result.get_fields(), self.test_photos_dict[0]) @mock.patch.object(trovebox.Trovebox, 'post') - def test_photo_object_replace_encoded(self, _): - """ If photo.replace_encoded gets implemented, write a test! """ - with self.assertRaises(NotImplementedError): - self.test_photos[0].replace_encoded(photo_file=self.test_file) + def test_photo_object_replace_encoded(self, mock_post): + """ + Check that a photo can be uploaded using Base64 encoding to + replace an existing photo when using the Photo object directly. + """ + photo_id = self.test_photos[1].id + mock_post.return_value = self._return_value(self.test_photos_dict[0]) + self.test_photos[1].replace_encoded(self.test_file, title="Test") + with open(self.test_file, "rb") as in_file: + encoded_file = base64.b64encode(in_file.read()) + mock_post.assert_called_with("/photo/%s/replace.json" + % photo_id, + photo=encoded_file, title="Test") + self.assertEqual(self.test_photos[1].get_fields(), + self.test_photos_dict[0]) class TestPhotoUpdate(TestPhotos): @mock.patch.object(trovebox.Trovebox, 'post') diff --git a/trovebox/api/api_photo.py b/trovebox/api/api_photo.py index 7a3d42a..8f3286e 100644 --- a/trovebox/api/api_photo.py +++ b/trovebox/api/api_photo.py @@ -84,12 +84,34 @@ class ApiPhoto(ApiBase): **kwds)["result"] def replace(self, photo, photo_file, **kwds): - """ Not yet implemented """ - raise NotImplementedError() + """ + Endpoint: /photo//replace.json + + Uploads the specified photo file to replace an existing photo. + """ + with open(photo_file, 'rb') as in_file: + result = self._client.post("/photo/%s/replace.json" % + self._extract_id(photo), + files={'photo': in_file}, + **kwds)["result"] + return Photo(self._client, result) def replace_encoded(self, photo, photo_file, **kwds): - """ Not yet implemented """ - raise NotImplementedError() + """ + Endpoint: /photo//replace.json + + Base64-encodes and uploads the specified photo filename to + replace an existing photo. + """ + with open(photo_file, "rb") as in_file: + encoded_photo = base64.b64encode(in_file.read()) + result = self._client.post("/photo/%s/replace.json" % + self._extract_id(photo), + photo=encoded_photo, + **kwds)["result"] + return Photo(self._client, result) + +# def replace_from_url(self, url, **kwds): def update(self, photo, **kwds): """ @@ -142,6 +164,8 @@ class ApiPhoto(ApiBase): **kwds)["result"] return Photo(self._client, result) +# def upload_from_url(self, url, **kwds): + def dynamic_url(self, photo, **kwds): """ Not yet implemented """ raise NotImplementedError() diff --git a/trovebox/objects/photo.py b/trovebox/objects/photo.py index 2f4987c..89aa769 100644 --- a/trovebox/objects/photo.py +++ b/trovebox/objects/photo.py @@ -30,12 +30,24 @@ class Photo(TroveboxObject): return self._client.photo.delete_source(self, **kwds) def replace(self, photo_file, **kwds): - """ Not implemented yet """ - raise NotImplementedError() + """ + Endpoint: /photo//replace.json + + Uploads the specified photo file to replace this photo. + """ + result = self._client.photo.replace(self, photo_file, **kwds) + self._replace_fields(result.get_fields()) def replace_encoded(self, photo_file, **kwds): - """ Not implemented yet """ - raise NotImplementedError() + """ + Endpoint: /photo//replace.json + + Base64-encodes and uploads the specified photo file to + replace this photo. + """ + result = self._client.photo.replace_encoded(self, photo_file, + **kwds) + self._replace_fields(result.get_fields()) def update(self, **kwds): """