Added tests for Python API library
This commit is contained in:
parent
854c4fd605
commit
ba5be69bec
10 changed files with 507 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
*~
|
||||
*.pyc
|
||||
build
|
||||
dist
|
||||
*.egg-info
|
||||
|
||||
tests/tokens.py
|
||||
|
|
61
tests/README.markdown
Normal file
61
tests/README.markdown
Normal file
|
@ -0,0 +1,61 @@
|
|||
Tests for the Open Photo API / Python Library
|
||||
=======================
|
||||
#### OpenPhoto, a photo service for the masses
|
||||
|
||||
----------------------------------------
|
||||
<a name="requirements"></a>
|
||||
### Requirements
|
||||
A computer, Python 2.7 and an empty OpenPhoto instance.
|
||||
|
||||
---------------------------------------
|
||||
<a name="setup"></a>
|
||||
### Setting up
|
||||
|
||||
Create a tests/tokens.py file containing the following:
|
||||
|
||||
# tests/token.py
|
||||
consumer_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
consumer_secret = "xxxxxxxxxx"
|
||||
token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
token_secret = "xxxxxxxxxx"
|
||||
host = "your_hostname"
|
||||
|
||||
Make sure this is an empty test server, **not a production OpenPhoto server!!!**
|
||||
|
||||
---------------------------------------
|
||||
<a name="running"></a>
|
||||
### Running the tests
|
||||
|
||||
cd /path/to/openphoto-python
|
||||
python -m unittest discover -c
|
||||
|
||||
The "-c" lets you stop the tests gracefully with \[CTRL\]-c.
|
||||
|
||||
The easiest way to run a subset of the tests is with nose:
|
||||
|
||||
cd /path/to/openphoto-python
|
||||
nosetests -v -s tests/test_albums.py:TestAlbums.test_view
|
||||
|
||||
---------------------------------------
|
||||
<a name="test_details"></a>
|
||||
### Test Details
|
||||
|
||||
These tests are intended to verify the Python library. They don't provide comprehensive testing of the OpenPhoto API, there are PHP unit tests for that.
|
||||
|
||||
Each test class is run as follows:
|
||||
|
||||
**SetUpClass:**
|
||||
|
||||
Check that the server is empty
|
||||
|
||||
**SetUp:**
|
||||
|
||||
Ensure there are:
|
||||
|
||||
* Three test photos
|
||||
* A single test tag applied to each
|
||||
* A single album containing all three photos
|
||||
|
||||
**TearDownClass:**
|
||||
|
||||
Remove all photos, tags and albums
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
88
tests/test_albums.py
Normal file
88
tests/test_albums.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
import unittest
|
||||
import openphoto
|
||||
import test_base
|
||||
|
||||
class TestAlbums(test_base.TestBase):
|
||||
|
||||
def test_create_delete(self):
|
||||
""" Create an album then delete it """
|
||||
album_name = "create_delete_album"
|
||||
album = self.client.album.create(album_name, visible=True)
|
||||
|
||||
# Check the return value
|
||||
self.assertEqual(album.name, album_name)
|
||||
# Check that the album now exists
|
||||
self.assertIn(album_name, [a.name for a in self.client.albums.list()])
|
||||
|
||||
# Delete the album
|
||||
self.client.album.delete(album.id)
|
||||
# Check that the album is now gone
|
||||
self.assertNotIn(album_name, [a.name for a in self.client.albums.list()])
|
||||
|
||||
# Create it again, and delete it using the Album object
|
||||
album = self.client.album.create(album_name, visible=True)
|
||||
album.delete()
|
||||
# Check that the album is now gone
|
||||
self.assertNotIn(album_name, [a.name for a in self.client.albums.list()])
|
||||
|
||||
def test_update(self):
|
||||
""" Test that an album can be updated """
|
||||
# Update the album using the OpenPhoto class, passing in the album object
|
||||
new_name = "New Name"
|
||||
self.client.album.update(self.albums[0], name=new_name)
|
||||
|
||||
# Check that the album is updated
|
||||
self.albums = self.client.albums.list()
|
||||
self.assertEqual(self.albums[0].name, new_name)
|
||||
|
||||
# Update the album using the OpenPhoto class, passing in the album id
|
||||
new_name = "Another New Name"
|
||||
self.client.album.update(self.albums[0].id, name=new_name)
|
||||
|
||||
# Check that the album is updated
|
||||
self.albums = self.client.albums.list()
|
||||
self.assertEqual(self.albums[0].name, new_name)
|
||||
|
||||
# Update the album using the Album object directly
|
||||
self.albums[0].update(name=self.TEST_ALBUM)
|
||||
|
||||
# Check that the album is updated
|
||||
self.albums = self.client.albums.list()
|
||||
self.assertEqual(self.albums[0].name, self.TEST_ALBUM)
|
||||
|
||||
def test_view(self):
|
||||
""" Test the album view """
|
||||
album = self.albums[0]
|
||||
self.assertFalse(hasattr(album, "photos"))
|
||||
|
||||
# Get the photos in the album using the Album object directly
|
||||
album.view()
|
||||
# Make sure all photos are in the album
|
||||
for photo in self.photos:
|
||||
self.assertIn(photo.id, [p.id for p in album.photos])
|
||||
|
||||
@unittest.expectedFailure # Private albums are not visible - issue #929
|
||||
def test_private(self):
|
||||
""" Test that private albums can be created, and are visible """
|
||||
# Create and check that the album now exists
|
||||
album = self.client.album.create(album_name, visible=False)
|
||||
self.assertIn(album_name, self.client.albums.list())
|
||||
|
||||
# Delete and check that the album is now gone
|
||||
album.delete()
|
||||
self.assertNotIn(album_name, self.client.albums.list())
|
||||
|
||||
def test_form(self):
|
||||
""" If album.form gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.album.form(None)
|
||||
|
||||
def test_add_photos(self):
|
||||
""" If album.add_photos gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.album.add_photos(None, None)
|
||||
|
||||
def test_remove_photos(self):
|
||||
""" If album.remove_photos gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.album.remove_photos(None, None)
|
128
tests/test_base.py
Normal file
128
tests/test_base.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
import unittest
|
||||
import openphoto
|
||||
|
||||
try:
|
||||
import tokens
|
||||
except ImportError:
|
||||
print ("********************************************************************\n"
|
||||
"You need to create a 'tokens.py' file containing the following:\n\n"
|
||||
" host = \"<test_url>\"\n"
|
||||
" consumer_key = \"<test_consumer_key>\"\n"
|
||||
" consumer_secret = \"<test_consumer_secret>\"\n"
|
||||
" token = \"<test_token>\"\n"
|
||||
" token_secret = \"<test_token_secret>\"\n"
|
||||
" host = \"<hostname>\"\n\n"
|
||||
"WARNING: Don't use a production OpenPhoto instance for this!\n"
|
||||
"********************************************************************\n")
|
||||
raise
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
TEST_TITLE = "Test Image - delete me!"
|
||||
TEST_TAG = "test_tag"
|
||||
TEST_ALBUM = "test_album"
|
||||
MAXIMUM_TEST_PHOTOS = 4 # Never have more the 4 photos on the test server
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
unittest.TestCase.__init__(self, *args, **kwds)
|
||||
self.photos = []
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
""" Ensure there is nothing on the server before running any tests """
|
||||
cls.client = openphoto.OpenPhoto(tokens.host,
|
||||
tokens.consumer_key, tokens.consumer_secret,
|
||||
tokens.token, tokens.token_secret)
|
||||
|
||||
if cls.client.photos.list() != []:
|
||||
raise ValueError("The test server (%s) contains photos. "
|
||||
"Please delete them before running the tests"
|
||||
% tokens.host)
|
||||
|
||||
if cls.client.tags.list() != []:
|
||||
raise ValueError("The test server (%s) contains tags. "
|
||||
"Please delete them before running the tests"
|
||||
% tokens.host)
|
||||
|
||||
if cls.client.albums.list() != []:
|
||||
raise ValueError("The test server (%s) contains albums. "
|
||||
"Please delete them before running the tests"
|
||||
% tokens.host)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
""" Once all tests have finished, delete all photos, tags and albums"""
|
||||
cls._delete_all()
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Ensure the three test photos are present before each test.
|
||||
Give them each a tag.
|
||||
Put them into an album.
|
||||
"""
|
||||
self.photos = self.client.photos.list()
|
||||
if len(self.photos) != 3:
|
||||
print "[Regenerating Photos]"
|
||||
if len(self.photos) > 0:
|
||||
self._delete_all()
|
||||
self._create_test_photos()
|
||||
self.photos = self.client.photos.list()
|
||||
|
||||
self.tags = self.client.tags.list()
|
||||
if (len(self.tags) != 1 or
|
||||
self.tags[0].id != self.TEST_TAG or
|
||||
self.tags[0].count != "3"):
|
||||
print "[Regenerating Tags]"
|
||||
self._delete_all()
|
||||
self._create_test_photos()
|
||||
self.photos = self.client.photos.list()
|
||||
self.tags = self.client.tags.list()
|
||||
if len(self.tags) != 1:
|
||||
print "Tags: %s" % self.tags
|
||||
raise Exception("Tag creation failed")
|
||||
|
||||
self.albums = self.client.albums.list()
|
||||
if (len(self.albums) != 1 or
|
||||
self.albums[0].name != self.TEST_ALBUM or
|
||||
self.albums[0].count != "3"):
|
||||
print "[Regenerating Albums]"
|
||||
self._delete_all()
|
||||
self._create_test_photos()
|
||||
self.photos = self.client.photos.list()
|
||||
self.tags = self.client.tags.list()
|
||||
self.albums = self.client.albums.list()
|
||||
if len(self.albums) != 1:
|
||||
print "Albums: %s" % self.albums
|
||||
raise Exception("Album creation failed")
|
||||
|
||||
@classmethod
|
||||
def _create_test_photos(cls):
|
||||
""" Upload three test photos """
|
||||
album = cls.client.album.create(cls.TEST_ALBUM, visible=True)
|
||||
photos = [
|
||||
cls.client.photo.upload_encoded("tests/test_photo1.jpg",
|
||||
title=cls.TEST_TITLE,
|
||||
tags=cls.TEST_TAG),
|
||||
cls.client.photo.upload_encoded("tests/test_photo2.jpg",
|
||||
title=cls.TEST_TITLE,
|
||||
tags=cls.TEST_TAG),
|
||||
cls.client.photo.upload_encoded("tests/test_photo3.jpg",
|
||||
title=cls.TEST_TITLE,
|
||||
tags=cls.TEST_TAG),
|
||||
]
|
||||
# Remove the auto-generated month/year tags
|
||||
tags_to_remove = [p for p in photos[0].tags if p != cls.TEST_TAG]
|
||||
for photo in photos:
|
||||
photo.update(tagsRemove=tags_to_remove, albums=album.id)
|
||||
|
||||
@classmethod
|
||||
def _delete_all(cls):
|
||||
photos = cls.client.photos.list()
|
||||
if len(photos) > cls.MAXIMUM_TEST_PHOTOS:
|
||||
raise ValueError("There too many photos on the test server - must always be less than %d."
|
||||
% cls.MAXIMUM_TEST_PHOTOS)
|
||||
for photo in photos:
|
||||
photo.delete()
|
||||
for tag in cls.client.tags.list():
|
||||
tag.delete()
|
||||
for album in cls.client.albums.list():
|
||||
album.delete()
|
BIN
tests/test_photo1.jpg
Normal file
BIN
tests/test_photo1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
tests/test_photo2.jpg
Normal file
BIN
tests/test_photo2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 854 B |
BIN
tests/test_photo3.jpg
Normal file
BIN
tests/test_photo3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 579 B |
157
tests/test_photos.py
Normal file
157
tests/test_photos.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
import unittest
|
||||
import openphoto
|
||||
import test_base
|
||||
|
||||
class TestPhotos(test_base.TestBase):
|
||||
def test_delete_upload(self):
|
||||
""" Test photo deletion and upload """
|
||||
# Delete one photo using the OpenPhoto class, passing in the id
|
||||
self.client.photo.delete(self.photos[0].id)
|
||||
# Delete one photo using the OpenPhoto class, passing in the object
|
||||
self.client.photo.delete(self.photos[1])
|
||||
# And another using the Photo object directly
|
||||
self.photos[2].delete()
|
||||
|
||||
# Check that they're gone
|
||||
self.assertEqual(self.client.photos.list(), [])
|
||||
|
||||
# Re-upload the photos
|
||||
ret_val = self.client.photo.upload_encoded("tests/test_photo1.jpg",
|
||||
title=self.TEST_TITLE)
|
||||
self.client.photo.upload_encoded("tests/test_photo2.jpg",
|
||||
title=self.TEST_TITLE)
|
||||
self.client.photo.upload_encoded("tests/test_photo3.jpg",
|
||||
title=self.TEST_TITLE)
|
||||
|
||||
# Check there are now three photos
|
||||
self.photos = self.client.photos.list()
|
||||
self.assertEqual(len(self.photos), 3)
|
||||
|
||||
# Check that the upload return value was correct
|
||||
pathOriginals = [photo.pathOriginal for photo in self.photos]
|
||||
self.assertIn(ret_val.pathOriginal, pathOriginals)
|
||||
|
||||
# Delete all photos in one go
|
||||
self.client.photos.delete(self.photos)
|
||||
|
||||
# Check they're gone
|
||||
self.photos = self.client.photos.list()
|
||||
self.assertEqual(len(self.photos), 0)
|
||||
|
||||
# Regenerate the original test photos
|
||||
self._delete_all()
|
||||
self._create_test_photos()
|
||||
|
||||
def test_edit(self):
|
||||
""" Check that the edit request returns an HTML form """
|
||||
# Test using the OpenPhoto class
|
||||
html = self.client.photo.edit(self.photos[0])
|
||||
self.assertIn("<form", html.lower())
|
||||
|
||||
# And the Photo object directly
|
||||
html = self.photos[0].edit()
|
||||
self.assertIn("<form", html.lower())
|
||||
|
||||
def test_upload_duplicate(self):
|
||||
""" Ensure that duplicate photos are rejected """
|
||||
# Attempt to upload a duplicate
|
||||
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
|
||||
self.client.photo.upload_encoded("tests/test_photo1.jpg",
|
||||
title=self.TEST_TITLE)
|
||||
|
||||
# Check there are still three photos
|
||||
self.photos = self.client.photos.list()
|
||||
self.assertEqual(len(self.photos), 3)
|
||||
|
||||
def test_update(self):
|
||||
""" Update a photo by editing the title """
|
||||
title = u"\xfcmlaut" # umlauted umlaut
|
||||
# Get a photo and check that it doesn't have the magic title
|
||||
photo = self.photos[0]
|
||||
self.assertNotEqual(photo.title, title)
|
||||
|
||||
# Add the title to a photo using the OpenPhoto class
|
||||
ret_val = self.client.photo.update(photo, title=title)
|
||||
|
||||
# Check that it's there
|
||||
self.photos = self.client.photos.list()
|
||||
photo = self.photos[0]
|
||||
self.assertEqual(photo.title, title)
|
||||
|
||||
# Check that the return value was correct
|
||||
self.assertEqual(ret_val.pathOriginal, photo.pathOriginal)
|
||||
|
||||
# Revert the title using the Photo object directly
|
||||
photo.update(title=self.TEST_TITLE)
|
||||
|
||||
# Check that it's gone back
|
||||
self.photos = self.client.photos.list()
|
||||
self.assertEqual(self.photos[0].title, self.TEST_TITLE)
|
||||
|
||||
def test_update_multiple(self):
|
||||
""" Update multiple photos by adding tags """
|
||||
tag_id = "update_photo_tag"
|
||||
# Get a couple of photos
|
||||
photos = self.photos[:2]
|
||||
|
||||
# Add the tag using a list of photo objects
|
||||
self.client.photos.update(photos, tagsAdd=tag_id)
|
||||
|
||||
# Check that it's there
|
||||
for photo in self.client.photos.list()[:2]:
|
||||
self.assertIn(tag_id, photo.tags)
|
||||
|
||||
# Remove the tags using a list of photo ids
|
||||
self.client.photos.update([photo.id for photo in photos],
|
||||
tagsRemove=tag_id)
|
||||
|
||||
def test_view(self):
|
||||
""" Test photo view """
|
||||
# Check that our magic sizes aren't present
|
||||
photo = self.photos[0]
|
||||
self.assertFalse(hasattr(photo, "path9x9"))
|
||||
self.assertFalse(hasattr(photo, "path19x19"))
|
||||
|
||||
# View at a particular size using the OpenPhoto class
|
||||
photo = self.client.photo.view(photo, returnSizes="9x9")
|
||||
self.assertTrue(hasattr(photo, "path9x9"))
|
||||
|
||||
# View at a particular size using the Photo object directly
|
||||
photo.view(returnSizes="19x19")
|
||||
self.assertTrue(hasattr(photo, "path19x19"))
|
||||
|
||||
def test_next_previous(self):
|
||||
""" Test the next/previous links of the middle photo """
|
||||
next_prev = self.client.photo.next_previous(self.photos[1])
|
||||
self.assertEqual(next_prev["previous"].id, self.photos[0].id)
|
||||
self.assertEqual(next_prev["next"].id, self.photos[2].id)
|
||||
|
||||
# Do the same using the Photo object directly
|
||||
next_prev = self.photos[1].next_previous()
|
||||
self.assertEqual(next_prev["previous"].id, self.photos[0].id)
|
||||
self.assertEqual(next_prev["next"].id, self.photos[2].id)
|
||||
|
||||
def test_replace(self):
|
||||
""" If photo.replace gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.photo.replace(None, None)
|
||||
|
||||
def test_replace_encoded(self):
|
||||
""" If photo.replace_encoded gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.photo.replace_encoded(None, None)
|
||||
|
||||
def test_upload(self):
|
||||
""" If photo.upload gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.photo.upload(None)
|
||||
|
||||
def test_dynamic_url(self):
|
||||
""" If photo.dynamic_url gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.photo.dynamic_url(None)
|
||||
|
||||
def test_transform(self):
|
||||
""" If photo.transform gets implemented, write a test! """
|
||||
with self.assertRaises(openphoto.NotImplementedError):
|
||||
self.client.photo.transform(None)
|
71
tests/test_tags.py
Normal file
71
tests/test_tags.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import unittest
|
||||
import openphoto
|
||||
import test_base
|
||||
|
||||
class TestTags(test_base.TestBase):
|
||||
@unittest.expectedFailure # Tag create fails - Issue #927
|
||||
# NOTE: the below has not been tested/debugged, since it fails at the first step
|
||||
def test_create_delete(self, tag_name="create_tag"):
|
||||
""" Create a tag then delete it """
|
||||
# Create a tag
|
||||
tag = self.client.tag.create(tag_name)
|
||||
|
||||
# Check the return value
|
||||
self.assertEqual(tag.id, tag_name)
|
||||
# Check that the tag now exists
|
||||
self.assertIn(tag_name, self.client.tags.list())
|
||||
|
||||
# Delete the tag
|
||||
self.client.tag.delete(tag_name)
|
||||
# Check that the tag is now gone
|
||||
self.assertNotIn(tag_name, self.client.tags.list())
|
||||
|
||||
# Create and delete using the Tag object directly
|
||||
tag = self.client.tag.create(tag_name)
|
||||
tag.delete()
|
||||
# Check that the tag is now gone
|
||||
self.assertNotIn(tag_name, self.client.tags.list())
|
||||
|
||||
@unittest.expectedFailure # Tag update fails - Issue #927
|
||||
# NOTE: the below has not been tested/debugged, since it fails at the first step
|
||||
def test_update(self):
|
||||
""" Test that a tag can be updated """
|
||||
# Update the tag using the OpenPhoto class, passing in the tag object
|
||||
owner = "test1@openphoto.me"
|
||||
ret_val = self.client.tag.update(self.tags[0], owner=owner)
|
||||
|
||||
# Check that the tag is updated
|
||||
self.tags = self.client.tags.list()
|
||||
self.assertEqual(self.tags[0].owner, owner)
|
||||
self.assertEqual(ret_val.owner, owner)
|
||||
|
||||
# Update the tag using the OpenPhoto class, passing in the tag id
|
||||
owner = "test2@openphoto.me"
|
||||
ret_val = self.client.tag.update(self.TEST_TAG, owner=owner)
|
||||
|
||||
# Check that the tag is updated
|
||||
self.tags = self.client.tags.list()
|
||||
self.assertEqual(self.tags[0].owner, owner)
|
||||
self.assertEqual(ret_val.owner, owner)
|
||||
|
||||
# Update the tag using the Tag object directly
|
||||
owner = "test3@openphoto.me"
|
||||
ret_val = self.tags[0].update(owner=owner)
|
||||
|
||||
# Check that the tag is updated
|
||||
self.tags = self.client.tags.list()
|
||||
self.assertEqual(self.tags[0].owner, owner)
|
||||
self.assertEqual(ret_val.owner, owner)
|
||||
|
||||
@unittest.expectedFailure # Tag create fails - Issue #927
|
||||
# NOTE: the below has not been tested/debugged, since it fails at the first step
|
||||
def test_tag_with_spaces(self):
|
||||
""" Run test_create_delete using a tag containing spaces """
|
||||
self.test_create_delete("tag with spaces")
|
||||
|
||||
# We mustn't run this test until Issue #919 is resolved,
|
||||
# since it creates an undeletable tag
|
||||
@unittest.skip("Tags with double-slashes cannot be deleted - Issue #919")
|
||||
def test_tag_with_double_slashes(self):
|
||||
""" Run test_create_delete using a tag containing slashes """
|
||||
self.test_create_delete("tag/with//slashes")
|
Loading…
Add table
Add a link
Reference in a new issue