Merge branch 'unit_tests' into development
This commit is contained in:
commit
64d762d709
29 changed files with 1491 additions and 158 deletions
10
.travis.yml
10
.travis.yml
|
@ -4,15 +4,7 @@ install:
|
||||||
- pip install tox --use-mirrors
|
- pip install tox --use-mirrors
|
||||||
- .travis/install_pylint
|
- .travis/install_pylint
|
||||||
|
|
||||||
env:
|
script: tox
|
||||||
global:
|
|
||||||
- secure: "CQNSUBhVyedtCbioKkoueZsBFw0H+EnrPPNQIO+v3ieniPhFQbCoaCOA6rZG\n1MH1oIz5GSB1hv48jLCSSDppYOX1nKlLUFAepm9h7HSv2MaBzENKcp3ipBLP\nn8QEhVCkeWVnTkRB+IWrQHiW+8vHZ1iaERjlX9cMav7rBzzvK9g="
|
|
||||||
- secure: "e5xYBGGzn6x06hmofDJ+tuS8iAVPuFNGqizR8cA6+2W4rSQEbh7NcKKeAvB5\n8qlmBonupo0wttkewh2hpnxvaXV7uS4C0Qt/h087Bu4cPkJMENWq++CrDo6G\nwjkAu6x6YDkzuMuxa5BTWU9hAQVX1jq+cjYOmORhw/v5FFukN44="
|
|
||||||
- secure: "aU95NQmiY2ytyGRywEQvblN1YinIHpe/L9jnYlxazhfdHr+WXZd5aXC4Ze/U\nqlsHR+PGjycPHUCykJ/W5KU68tAX9r3PQgaQlfWd1cT89paY4givtoHiTz+f\nGu2I3BexskJ58NcUEDp6MEJqEuIXiQYUpoQ+6rNzvpe427xt6R0="
|
|
||||||
- secure: "ilNFM41mePkXMpvK/6T7s3vsQCN36XoiHnR7Fxrnpur9sXOfwB8A1Kw7CpbM\n5rxc2QNj7SPrT2K49QE8fUKHIl88a2MqCf+ujy9mG7WgKdxYazIxrhyHCNKO\nZ47r38kijW92GnSX4KTDeORfouZgR21BpDTfoCvspiWzWzG/fYE="
|
|
||||||
- secure: "YdUPDO7sTUTG2EwUlrxwOWKhlGXJiIK+RBWDspqvM8UQV4CQjzIsRX8urUIN\nSpSjJOfbIw25S+AsLpEBye8OJMncm/16Xp7PL5tlkNmRC12mPVG8f+wpOkrW\nt8v+2Cv/prYDn0tjoqnV1f5Nv5cEW6kAkG19UQ4QBgQzirtrs9Y="
|
|
||||||
|
|
||||||
script: .travis/run_travis
|
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
# Run Pylint
|
# Run Pylint
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Create a config file containing the test host's secrets
|
|
||||||
|
|
||||||
CONFIG_DIR=~/.config/openphoto
|
|
||||||
CONFIG_FILE=$CONFIG_DIR/test
|
|
||||||
|
|
||||||
mkdir ~/.config
|
|
||||||
mkdir $CONFIG_DIR
|
|
||||||
|
|
||||||
echo "host = $OP_HOST" >> $CONFIG_FILE
|
|
||||||
echo "consumerKey = $OP_CONSUMER_KEY" >> $CONFIG_FILE
|
|
||||||
echo "consumerSecret = $OP_CONSUMER_SECRET" >> $CONFIG_FILE
|
|
||||||
echo "token = $OP_TOKEN" >> $CONFIG_FILE
|
|
||||||
echo "tokenSecret = $OP_TOKEN_SECRET" >> $CONFIG_FILE
|
|
||||||
|
|
||||||
# Run the tests
|
|
||||||
|
|
||||||
tox -e py27
|
|
|
@ -81,6 +81,8 @@ def main(args=sys.argv[1:]):
|
||||||
params, files = extract_files(params)
|
params, files = extract_files(params)
|
||||||
result = client.post(options.endpoint, process_response=False,
|
result = client.post(options.endpoint, process_response=False,
|
||||||
files=files, **params)
|
files=files, **params)
|
||||||
|
for f in files:
|
||||||
|
files[f].close()
|
||||||
|
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print("==========\nMethod: %s\nHost: %s\nEndpoint: %s" %
|
print("==========\nMethod: %s\nHost: %s\nEndpoint: %s" %
|
||||||
|
|
|
@ -14,12 +14,8 @@ from openphoto.config import Config
|
||||||
|
|
||||||
if sys.version < '3':
|
if sys.version < '3':
|
||||||
TEXT_TYPE = unicode
|
TEXT_TYPE = unicode
|
||||||
# requests_oauth needs to decode to ascii for Python2
|
|
||||||
OAUTH_DECODING = "utf-8"
|
|
||||||
else:
|
else:
|
||||||
TEXT_TYPE = str
|
TEXT_TYPE = str
|
||||||
# requests_oauth needs to use (unicode) strings for Python3
|
|
||||||
OAUTH_DECODING = None
|
|
||||||
|
|
||||||
DUPLICATE_RESPONSE = {"code": 409,
|
DUPLICATE_RESPONSE = {"code": 409,
|
||||||
"message": "This photo already exists"}
|
"message": "This photo already exists"}
|
||||||
|
@ -76,8 +72,7 @@ class OpenPhotoHttp:
|
||||||
auth = requests_oauthlib.OAuth1(self.config.consumer_key,
|
auth = requests_oauthlib.OAuth1(self.config.consumer_key,
|
||||||
self.config.consumer_secret,
|
self.config.consumer_secret,
|
||||||
self.config.token,
|
self.config.token,
|
||||||
self.config.token_secret,
|
self.config.token_secret)
|
||||||
decoding=OAUTH_DECODING)
|
|
||||||
else:
|
else:
|
||||||
auth = None
|
auth = None
|
||||||
|
|
||||||
|
@ -122,8 +117,7 @@ class OpenPhotoHttp:
|
||||||
auth = requests_oauthlib.OAuth1(self.config.consumer_key,
|
auth = requests_oauthlib.OAuth1(self.config.consumer_key,
|
||||||
self.config.consumer_secret,
|
self.config.consumer_secret,
|
||||||
self.config.token,
|
self.config.token,
|
||||||
self.config.token_secret,
|
self.config.token_secret)
|
||||||
decoding=OAUTH_DECODING)
|
|
||||||
with requests.Session() as session:
|
with requests.Session() as session:
|
||||||
if files:
|
if files:
|
||||||
# Need to pass parameters as URL query, so they get OAuth signed
|
# Need to pass parameters as URL query, so they get OAuth signed
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Simple script to run all tests with multiple test servers
|
# Simple script to run all functional tests with multiple test servers
|
||||||
# across all supported Python versions
|
|
||||||
#
|
#
|
||||||
|
|
||||||
# Default test server running latest self-hosted site
|
# Test server running latest self-hosted site
|
||||||
|
|
||||||
tput setaf 3
|
tput setaf 3
|
||||||
echo
|
echo
|
||||||
|
@ -12,7 +11,7 @@ echo "Testing latest self-hosted site..."
|
||||||
tput sgr0
|
tput sgr0
|
||||||
export OPENPHOTO_TEST_CONFIG=test
|
export OPENPHOTO_TEST_CONFIG=test
|
||||||
unset OPENPHOTO_TEST_SERVER_API
|
unset OPENPHOTO_TEST_SERVER_API
|
||||||
tox $@
|
python -m unittest discover --catch tests/functional
|
||||||
|
|
||||||
# Test server running APIv1 OpenPhoto instance
|
# Test server running APIv1 OpenPhoto instance
|
||||||
tput setaf 3
|
tput setaf 3
|
||||||
|
@ -21,7 +20,7 @@ echo "Testing APIv1 self-hosted site..."
|
||||||
tput sgr0
|
tput sgr0
|
||||||
export OPENPHOTO_TEST_CONFIG=test-apiv1
|
export OPENPHOTO_TEST_CONFIG=test-apiv1
|
||||||
export OPENPHOTO_TEST_SERVER_API=1
|
export OPENPHOTO_TEST_SERVER_API=1
|
||||||
tox $@
|
python -m unittest discover --catch tests/functional
|
||||||
|
|
||||||
# Test account on hosted trovebox.com site
|
# Test account on hosted trovebox.com site
|
||||||
tput setaf 3
|
tput setaf 3
|
||||||
|
@ -30,5 +29,5 @@ echo "Testing latest hosted site..."
|
||||||
tput sgr0
|
tput sgr0
|
||||||
export OPENPHOTO_TEST_CONFIG=test-hosted
|
export OPENPHOTO_TEST_CONFIG=test-hosted
|
||||||
unset OPENPHOTO_TEST_SERVER_API
|
unset OPENPHOTO_TEST_SERVER_API
|
||||||
tox $@
|
python -m unittest discover --catch tests/functional
|
||||||
|
|
|
@ -1,101 +1,34 @@
|
||||||
Tests for the Open Photo API / Python Library
|
OpenPhoto/Trovebox Python Testing
|
||||||
=======================
|
=======================
|
||||||
#### OpenPhoto, a photo service for the masses
|
|
||||||
|
###Unit Tests
|
||||||
|
|
||||||
|
The unit tests mock out all HTTP requests, and verify that the various
|
||||||
|
components of the library are operating correctly.
|
||||||
|
|
||||||
|
They run very quickly and don't require any external test hosts.
|
||||||
|
|
||||||
|
<a name="requirements"></a>
|
||||||
|
#### Requirements
|
||||||
|
* mock >= 1.0.0
|
||||||
|
* httpretty >= 0.6.1
|
||||||
|
* tox (optional)
|
||||||
|
|
||||||
|
#### Running the Unit Tests
|
||||||
|
|
||||||
|
python -m unittest discover tests/unit
|
||||||
|
|
||||||
|
To run the unit tests against all supported Python versions, use ```tox```:
|
||||||
|
|
||||||
|
tox
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
<a name="requirements"></a>
|
|
||||||
### Requirements
|
|
||||||
A computer, Python and an empty OpenPhoto test host.
|
|
||||||
|
|
||||||
---------------------------------------
|
###Functional Tests
|
||||||
<a name="setup"></a>
|
|
||||||
### Setting up
|
|
||||||
|
|
||||||
Create a ``~/.config/openphoto/test`` config file containing the following:
|
The functional tests check that the openphoto-python library interoperates
|
||||||
|
correctly with a real OpenPhoto/Trovebox server.
|
||||||
|
|
||||||
# ~/.config/openphoto/test
|
They are slow to run and rely on a stable HTTP connection to a test server.
|
||||||
host = your.host.com
|
|
||||||
consumerKey = your_consumer_key
|
|
||||||
consumerSecret = your_consumer_secret
|
|
||||||
token = your_access_token
|
|
||||||
tokenSecret = your_access_token_secret
|
|
||||||
|
|
||||||
Make sure this is an empty test server, **not a production OpenPhoto server!!!**
|
For full details, see the [functional test README file](functional/README.markdown).
|
||||||
|
|
||||||
You can specify an alternate test config file with the following environment variable:
|
|
||||||
|
|
||||||
export OPENPHOTO_TEST_CONFIG=test2
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
<a name="running"></a>
|
|
||||||
### Running the tests
|
|
||||||
|
|
||||||
The following instructions are for Python 2.7. You can adapt them for earlier
|
|
||||||
Python versions using the ``unittest2`` package.
|
|
||||||
|
|
||||||
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 the ``nose`` package:
|
|
||||||
|
|
||||||
cd /path/to/openphoto-python
|
|
||||||
nosetests -v -s --nologcapture tests/test_albums.py:TestAlbums.test_view
|
|
||||||
|
|
||||||
All HTTP requests and responses are recorded in the file ``tests.log``.
|
|
||||||
|
|
||||||
You can enable more verbose output to stdout with the following environment variable:
|
|
||||||
|
|
||||||
export OPENPHOTO_TEST_DEBUG=1
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
<a name="test_details"></a>
|
|
||||||
### Test Details
|
|
||||||
|
|
||||||
These tests are intended to verify the openphoto-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
|
|
||||||
|
|
||||||
### Testing old servers
|
|
||||||
|
|
||||||
By default, all currently supported API versions will be tested.
|
|
||||||
It's useful to test servers that only support older API versions.
|
|
||||||
To restrict the testing to a specific maximum API version, use the
|
|
||||||
``OPENPHOTO_TEST_SERVER_API`` environment variable.
|
|
||||||
|
|
||||||
For example, to restrict testing to APIv1 and APIv2:
|
|
||||||
|
|
||||||
export OPENPHOTO_TEST_SERVER_API=2
|
|
||||||
|
|
||||||
<a name="full_regression"></a>
|
|
||||||
### Full Regression Test
|
|
||||||
|
|
||||||
The ``run_tests`` script uses the ``tox`` package to run a full regression across:
|
|
||||||
* Multiple Python versions
|
|
||||||
* All supported API versions
|
|
||||||
|
|
||||||
To use it, you must set up multiple OpenPhoto instances and create the following
|
|
||||||
config files containing your credentials:
|
|
||||||
|
|
||||||
test : Latest self-hosted site
|
|
||||||
test-apiv1 : APIv1 self-hosted site
|
|
||||||
test-hosted : Credentials for test account on trovebox.com
|
|
||||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 910 B After Width: | Height: | Size: 910 B |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 635 B |
104
tests/functional/README.markdown
Normal file
104
tests/functional/README.markdown
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
Functional Testing
|
||||||
|
=======================
|
||||||
|
|
||||||
|
These functional tests check that the openphoto-python library interoperates
|
||||||
|
correctly with a real OpenPhoto/Trovebox server.
|
||||||
|
|
||||||
|
They are slow to run, and require a stable HTTP connection to a test server.
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
<a name="requirements"></a>
|
||||||
|
### Requirements
|
||||||
|
A computer, Python and an empty OpenPhoto/Trovebox test host.
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
<a name="setup"></a>
|
||||||
|
### Setting up
|
||||||
|
|
||||||
|
Create a ``~/.config/openphoto/test`` config file containing the following:
|
||||||
|
|
||||||
|
# ~/.config/openphoto/test
|
||||||
|
host = your.host.com
|
||||||
|
consumerKey = your_consumer_key
|
||||||
|
consumerSecret = your_consumer_secret
|
||||||
|
token = your_access_token
|
||||||
|
tokenSecret = your_access_token_secret
|
||||||
|
|
||||||
|
Make sure this is an empty test server, **not a production OpenPhoto server!!!**
|
||||||
|
|
||||||
|
You can specify an alternate test config file with the following environment variable:
|
||||||
|
|
||||||
|
export OPENPHOTO_TEST_CONFIG=test2
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
<a name="running"></a>
|
||||||
|
### Running the tests
|
||||||
|
|
||||||
|
The following instructions are for Python 2.7. You can adapt them for earlier
|
||||||
|
Python versions using the ``unittest2`` package.
|
||||||
|
|
||||||
|
cd /path/to/openphoto-python
|
||||||
|
python -m unittest discover -c tests/functional
|
||||||
|
|
||||||
|
The "-c" lets you stop the tests gracefully with \[CTRL\]-c.
|
||||||
|
|
||||||
|
The easiest way to run a subset of the tests is with the ``nose`` package:
|
||||||
|
|
||||||
|
cd /path/to/openphoto-python
|
||||||
|
nosetests -v -s --nologcapture tests/functional/test_albums.py:TestAlbums.test_view
|
||||||
|
|
||||||
|
All HTTP requests and responses are recorded in the file ``tests.log``.
|
||||||
|
|
||||||
|
You can enable more verbose output to stdout with the following environment variable:
|
||||||
|
|
||||||
|
export OPENPHOTO_TEST_DEBUG=1
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
<a name="test_details"></a>
|
||||||
|
### Test Details
|
||||||
|
|
||||||
|
These tests are intended to verify the openphoto-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
|
||||||
|
|
||||||
|
### Testing old servers
|
||||||
|
|
||||||
|
By default, all currently supported API versions will be tested.
|
||||||
|
It's useful to test servers that only support older API versions.
|
||||||
|
To restrict the testing to a specific maximum API version, use the
|
||||||
|
``OPENPHOTO_TEST_SERVER_API`` environment variable.
|
||||||
|
|
||||||
|
For example, to restrict testing to APIv1 and APIv2:
|
||||||
|
|
||||||
|
export OPENPHOTO_TEST_SERVER_API=2
|
||||||
|
|
||||||
|
<a name="full_regression"></a>
|
||||||
|
### Full Regression Test
|
||||||
|
|
||||||
|
The ``run_functional_tests`` script runs all functional tests against
|
||||||
|
all supported API versions.
|
||||||
|
|
||||||
|
To use it, you must set up multiple OpenPhoto instances and create the following
|
||||||
|
config files containing your credentials:
|
||||||
|
|
||||||
|
test : Latest self-hosted site
|
||||||
|
test-apiv1 : APIv1 self-hosted site
|
||||||
|
test-hosted : Credentials for test account on trovebox.com
|
0
tests/functional/api_versions/__init__.py
Normal file
0
tests/functional/api_versions/__init__.py
Normal file
|
@ -1,4 +1,4 @@
|
||||||
from tests import test_albums, test_photos, test_tags
|
from tests.functional import test_albums, test_photos, test_tags
|
||||||
|
|
||||||
class TestAlbumsV1(test_albums.TestAlbums):
|
class TestAlbumsV1(test_albums.TestAlbums):
|
||||||
api_version = 1
|
api_version = 1
|
|
@ -2,7 +2,7 @@ try:
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import unittest
|
import unittest
|
||||||
from tests import test_base, test_albums, test_photos, test_tags
|
from tests.functional import test_base, test_albums, test_photos, test_tags
|
||||||
|
|
||||||
@unittest.skipIf(test_base.get_test_server_api() < 2,
|
@unittest.skipIf(test_base.get_test_server_api() < 2,
|
||||||
"Don't test future API versions")
|
"Don't test future API versions")
|
|
@ -1,6 +1,6 @@
|
||||||
import tests.test_base
|
from tests.functional import test_base
|
||||||
|
|
||||||
class TestAlbums(tests.test_base.TestBase):
|
class TestAlbums(test_base.TestBase):
|
||||||
testcase_name = "album API"
|
testcase_name = "album API"
|
||||||
|
|
||||||
def test_create_delete(self):
|
def test_create_delete(self):
|
|
@ -128,13 +128,13 @@ class TestBase(unittest.TestCase):
|
||||||
""" Upload three test photos """
|
""" Upload three test photos """
|
||||||
album = cls.client.album.create(cls.TEST_ALBUM)
|
album = cls.client.album.create(cls.TEST_ALBUM)
|
||||||
photos = [
|
photos = [
|
||||||
cls.client.photo.upload("tests/test_photo1.jpg",
|
cls.client.photo.upload("tests/data/test_photo1.jpg",
|
||||||
title=cls.TEST_TITLE,
|
title=cls.TEST_TITLE,
|
||||||
albums=album.id),
|
albums=album.id),
|
||||||
cls.client.photo.upload("tests/test_photo2.jpg",
|
cls.client.photo.upload("tests/data/test_photo2.jpg",
|
||||||
title=cls.TEST_TITLE,
|
title=cls.TEST_TITLE,
|
||||||
albums=album.id),
|
albums=album.id),
|
||||||
cls.client.photo.upload("tests/test_photo3.jpg",
|
cls.client.photo.upload("tests/data/test_photo3.jpg",
|
||||||
title=cls.TEST_TITLE,
|
title=cls.TEST_TITLE,
|
||||||
albums=album.id),
|
albums=album.id),
|
||||||
]
|
]
|
|
@ -1,9 +1,9 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import openphoto
|
import openphoto
|
||||||
import tests.test_base
|
from tests.functional import test_base
|
||||||
|
|
||||||
class TestFramework(tests.test_base.TestBase):
|
class TestFramework(test_base.TestBase):
|
||||||
testcase_name = "framework"
|
testcase_name = "framework"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -27,7 +27,7 @@ class TestFramework(tests.test_base.TestBase):
|
||||||
"""
|
"""
|
||||||
For all API versions >0, we get a generic hello world message
|
For all API versions >0, we get a generic hello world message
|
||||||
"""
|
"""
|
||||||
for api_version in range(1, tests.test_base.get_test_server_api() + 1):
|
for api_version in range(1, test_base.get_test_server_api() + 1):
|
||||||
client = openphoto.OpenPhoto(config_file=self.config_file,
|
client = openphoto.OpenPhoto(config_file=self.config_file,
|
||||||
api_version=api_version)
|
api_version=api_version)
|
||||||
result = client.get("hello.json")
|
result = client.get("hello.json")
|
|
@ -1,9 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import openphoto
|
import openphoto
|
||||||
import tests.test_base
|
from tests.functional import test_base
|
||||||
|
|
||||||
class TestPhotos(tests.test_base.TestBase):
|
class TestPhotos(test_base.TestBase):
|
||||||
testcase_name = "photo API"
|
testcase_name = "photo API"
|
||||||
|
|
||||||
def test_delete_upload(self):
|
def test_delete_upload(self):
|
||||||
|
@ -19,11 +19,11 @@ class TestPhotos(tests.test_base.TestBase):
|
||||||
self.assertEqual(self.client.photos.list(), [])
|
self.assertEqual(self.client.photos.list(), [])
|
||||||
|
|
||||||
# Re-upload the photos, one of them using Bas64 encoding
|
# Re-upload the photos, one of them using Bas64 encoding
|
||||||
ret_val = self.client.photo.upload("tests/test_photo1.jpg",
|
ret_val = self.client.photo.upload("tests/data/test_photo1.jpg",
|
||||||
title=self.TEST_TITLE)
|
title=self.TEST_TITLE)
|
||||||
self.client.photo.upload("tests/test_photo2.jpg",
|
self.client.photo.upload("tests/data/test_photo2.jpg",
|
||||||
title=self.TEST_TITLE)
|
title=self.TEST_TITLE)
|
||||||
self.client.photo.upload_encoded("tests/test_photo3.jpg",
|
self.client.photo.upload_encoded("tests/data/test_photo3.jpg",
|
||||||
title=self.TEST_TITLE)
|
title=self.TEST_TITLE)
|
||||||
|
|
||||||
# Check there are now three photos with the correct titles
|
# Check there are now three photos with the correct titles
|
||||||
|
@ -61,7 +61,7 @@ class TestPhotos(tests.test_base.TestBase):
|
||||||
""" Ensure that duplicate photos are rejected """
|
""" Ensure that duplicate photos are rejected """
|
||||||
# Attempt to upload a duplicate
|
# Attempt to upload a duplicate
|
||||||
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
|
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
|
||||||
self.client.photo.upload("tests/test_photo1.jpg",
|
self.client.photo.upload("tests/data/test_photo1.jpg",
|
||||||
title=self.TEST_TITLE)
|
title=self.TEST_TITLE)
|
||||||
|
|
||||||
# Check there are still three photos
|
# Check there are still three photos
|
|
@ -3,11 +3,11 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import tests.test_base
|
from tests.functional import test_base
|
||||||
|
|
||||||
@unittest.skipIf(tests.test_base.get_test_server_api() == 1,
|
@unittest.skipIf(test_base.get_test_server_api() == 1,
|
||||||
"The tag API didn't work at v1 - see frontend issue #927")
|
"The tag API didn't work at v1 - see frontend issue #927")
|
||||||
class TestTags(tests.test_base.TestBase):
|
class TestTags(test_base.TestBase):
|
||||||
testcase_name = "tag API"
|
testcase_name = "tag API"
|
||||||
|
|
||||||
def test_create_delete(self, tag_id="create_tag"):
|
def test_create_delete(self, tag_id="create_tag"):
|
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
1
tests/unit/data/test_file.txt
Normal file
1
tests/unit/data/test_file.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Test File
|
262
tests/unit/test_albums.py
Normal file
262
tests/unit/test_albums.py
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import mock
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
|
||||||
|
class TestAlbums(unittest.TestCase):
|
||||||
|
test_host = "test.example.com"
|
||||||
|
test_albums_dict = [{"cover": {"id": "1a", "tags": ["tag1", "tag2"]},
|
||||||
|
"id": "1",
|
||||||
|
"name": "Album 1",
|
||||||
|
"totalRows": 2},
|
||||||
|
{"cover": {"id": "2b", "tags": ["tag3", "tag4"]},
|
||||||
|
"id": "2",
|
||||||
|
"name": "Album 2",
|
||||||
|
"totalRows": 2}]
|
||||||
|
def setUp(self):
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host)
|
||||||
|
self.test_albums = [openphoto.objects.Album(self.client, album)
|
||||||
|
for album in self.test_albums_dict]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _return_value(result, message="", code=200):
|
||||||
|
return {"message": message, "code": code, "result": result}
|
||||||
|
|
||||||
|
class TestAlbumsList(TestAlbums):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_albums_list(self, mock_get):
|
||||||
|
"""Check that the album list is returned correctly"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_albums_dict)
|
||||||
|
result = self.client.albums.list()
|
||||||
|
mock_get.assert_called_with("/albums/list.json")
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
self.assertEqual(result[0].id, "1")
|
||||||
|
self.assertEqual(result[0].name, "Album 1")
|
||||||
|
self.assertEqual(result[1].id, "2")
|
||||||
|
self.assertEqual(result[1].name, "Album 2")
|
||||||
|
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_albums_list_returns_cover_photos(self, mock_get):
|
||||||
|
"""Check that the album list returns cover photo objects"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_albums_dict)
|
||||||
|
result = self.client.albums.list()
|
||||||
|
mock_get.assert_called_with("/albums/list.json")
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
self.assertEqual(result[0].id, "1")
|
||||||
|
self.assertEqual(result[0].name, "Album 1")
|
||||||
|
self.assertEqual(result[0].cover.id, "1a")
|
||||||
|
self.assertEqual(result[0].cover.tags, ["tag1", "tag2"])
|
||||||
|
self.assertEqual(result[1].id, "2")
|
||||||
|
self.assertEqual(result[0].name, "Album 2")
|
||||||
|
self.assertEqual(result[1].cover.id, "2b")
|
||||||
|
self.assertEqual(result[1].cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
class TestAlbumCreate(TestAlbums):
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_create(self, mock_post):
|
||||||
|
"""Check that an album can be created"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_albums_dict[0])
|
||||||
|
result = self.client.album.create(name="Test", foo="bar")
|
||||||
|
mock_post.assert_called_with("/album/create.json", name="Test",
|
||||||
|
foo="bar")
|
||||||
|
self.assertEqual(result.id, "1")
|
||||||
|
self.assertEqual(result.name, "Album 1")
|
||||||
|
# self.assertEqual(result.cover.id, "1a")
|
||||||
|
# self.assertEqual(result.cover.tags, ["tag1", "tag2"])
|
||||||
|
|
||||||
|
class TestAlbumDelete(TestAlbums):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_delete(self, mock_post):
|
||||||
|
"""Check that an album can be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.album.delete(self.test_albums[0])
|
||||||
|
mock_post.assert_called_with("/album/1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_delete_id(self, mock_post):
|
||||||
|
"""Check that an album can be deleted using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.album.delete("1")
|
||||||
|
mock_post.assert_called_with("/album/1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
# TODO: album.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_delete_failure(self, mock_post):
|
||||||
|
"""Check that an exception is raised if an album cannot be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.album.delete(self.test_albums[0])
|
||||||
|
|
||||||
|
# TODO: after deleting object fields, name and id should be set to None
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_delete(self, mock_post):
|
||||||
|
"""Check that an album can be deleted using the album object directly"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
album = self.test_albums[0]
|
||||||
|
result = album.delete()
|
||||||
|
mock_post.assert_called_with("/album/1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
self.assertEqual(album.get_fields(), {})
|
||||||
|
# self.assertEqual(album.id, None)
|
||||||
|
# self.assertEqual(album.name, None)
|
||||||
|
|
||||||
|
# TODO: album.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_delete_failure(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if an album cannot be deleted
|
||||||
|
when using the album object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.test_albums[0].delete()
|
||||||
|
|
||||||
|
class TestAlbumForm(TestAlbums):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_form(self, _):
|
||||||
|
""" If album.form gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.form(self.test_albums[0])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_form_id(self, _):
|
||||||
|
""" If album.form gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.form("1")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_form(self, _):
|
||||||
|
""" If album.form gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.test_albums[0].form()
|
||||||
|
|
||||||
|
class TestAlbumAddPhotos(TestAlbums):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_add_photos(self, _):
|
||||||
|
""" If album.add_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.add_photos(self.test_albums[0], ["Photo Objects"])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_add_photos_id(self, _):
|
||||||
|
""" If album.add_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.add_photos("1", ["Photo Objects"])
|
||||||
|
|
||||||
|
# TODO: object.add_photos should accept photos list as first parameter
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_add_photos(self, _):
|
||||||
|
""" If album.add_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.test_albums[0].add_photos(["Photo Objects"])
|
||||||
|
|
||||||
|
class TestAlbumRemovePhotos(TestAlbums):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_remove_photos(self, _):
|
||||||
|
""" If album.remove_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.remove_photos(self.test_albums[0],
|
||||||
|
["Photo Objects"])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_remove_photos_id(self, _):
|
||||||
|
""" If album.remove_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.album.remove_photos("1", ["Photo Objects"])
|
||||||
|
|
||||||
|
# TODO: object.remove_photos should accept photos list as first parameter
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_remove_photos(self, _):
|
||||||
|
""" If album.remove_photos gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.test_albums[0].remove_photos(["Photo Objects"])
|
||||||
|
|
||||||
|
class TestAlbumUpdate(TestAlbums):
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_update(self, mock_post):
|
||||||
|
"""Check that an album can be updated"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
result = self.client.album.update(self.test_albums[0], name="Test")
|
||||||
|
mock_post.assert_called_with("/album/1/update.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "2")
|
||||||
|
self.assertEqual(result.name, "Album 2")
|
||||||
|
# self.assertEqual(result.cover.id, "2b")
|
||||||
|
# self.assertEqual(result.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_update_id(self, mock_post):
|
||||||
|
"""Check that an album can be updated using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
result = self.client.album.update("1", name="Test")
|
||||||
|
mock_post.assert_called_with("/album/1/update.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "2")
|
||||||
|
self.assertEqual(result.name, "Album 2")
|
||||||
|
# self.assertEqual(result.cover.id, "2b")
|
||||||
|
# self.assertEqual(result.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_album_object_update(self, mock_post):
|
||||||
|
"""Check that an album can be updated using the album object directly"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
album = self.test_albums[0]
|
||||||
|
album.update(name="Test")
|
||||||
|
mock_post.assert_called_with("/album/1/update.json", name="Test")
|
||||||
|
self.assertEqual(album.id, "2")
|
||||||
|
self.assertEqual(album.name, "Album 2")
|
||||||
|
# self.assertEqual(album.cover.id, "2b")
|
||||||
|
# self.assertEqual(album.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
class TestAlbumView(TestAlbums):
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_album_view(self, mock_get):
|
||||||
|
"""Check that an album can be viewed"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
result = self.client.album.view(self.test_albums[0], name="Test")
|
||||||
|
mock_get.assert_called_with("/album/1/view.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "2")
|
||||||
|
self.assertEqual(result.name, "Album 2")
|
||||||
|
# self.assertEqual(result.cover.id, "2b")
|
||||||
|
# self.assertEqual(result.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_album_view_id(self, mock_get):
|
||||||
|
"""Check that an album can be viewed using its ID"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
result = self.client.album.view("1", name="Test")
|
||||||
|
mock_get.assert_called_with("/album/1/view.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "2")
|
||||||
|
self.assertEqual(result.name, "Album 2")
|
||||||
|
# self.assertEqual(result.cover.id, "2b")
|
||||||
|
# self.assertEqual(result.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
# TODO: cover should be updated to Photo object
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_album_object_view(self, mock_get):
|
||||||
|
"""Check that an album can be viewed using the album object directly"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_albums_dict[1])
|
||||||
|
album = self.test_albums[0]
|
||||||
|
album.view(name="Test")
|
||||||
|
mock_get.assert_called_with("/album/1/view.json", name="Test")
|
||||||
|
self.assertEqual(album.id, "2")
|
||||||
|
self.assertEqual(album.name, "Album 2")
|
||||||
|
# self.assertEqual(album.cover.id, "2b")
|
||||||
|
# self.assertEqual(album.cover.tags, ["tag3", "tag4"])
|
||||||
|
|
122
tests/unit/test_cli.py
Normal file
122
tests/unit/test_cli.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import mock
|
||||||
|
try:
|
||||||
|
import StringIO as io # Python2
|
||||||
|
except ImportError:
|
||||||
|
import io # Python3
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
from openphoto.main import main
|
||||||
|
|
||||||
|
class TestException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def raise_exception(_):
|
||||||
|
raise TestException()
|
||||||
|
|
||||||
|
class TestCli(unittest.TestCase):
|
||||||
|
test_file = os.path.join("tests", "unit", "data", "test_file.txt")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_defaults(self, _, mock_openphoto):
|
||||||
|
"""Check that the default behaviour is correct"""
|
||||||
|
get = mock_openphoto.return_value.get
|
||||||
|
main([])
|
||||||
|
mock_openphoto.assert_called_with(config_file=None)
|
||||||
|
get.assert_called_with("/photos/list.json", process_response=False)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_config(self, _, mock_openphoto):
|
||||||
|
"""Check that a config file can be specified"""
|
||||||
|
main(["--config=test"])
|
||||||
|
mock_openphoto.assert_called_with(config_file="test")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_get(self, mock_stdout, mock_openphoto):
|
||||||
|
"""Check that the get operation is working"""
|
||||||
|
get = mock_openphoto.return_value.get
|
||||||
|
get.return_value = "Result"
|
||||||
|
main(["-X", "GET", "-h", "test_host", "-e", "test_endpoint", "-F",
|
||||||
|
"field1=1", "-F", "field2=2"])
|
||||||
|
mock_openphoto.assert_called_with(host="test_host")
|
||||||
|
get.assert_called_with("test_endpoint", field1="1", field2="2",
|
||||||
|
process_response=False)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(), "Result\n")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_post(self, mock_stdout, mock_openphoto):
|
||||||
|
"""Check that the post operation is working"""
|
||||||
|
post = mock_openphoto.return_value.post
|
||||||
|
post.return_value = "Result"
|
||||||
|
main(["-X", "POST", "-h", "test_host", "-e", "test_endpoint", "-F",
|
||||||
|
"field1=1", "-F", "field2=2"])
|
||||||
|
mock_openphoto.assert_called_with(host="test_host")
|
||||||
|
post.assert_called_with("test_endpoint", field1="1", field2="2",
|
||||||
|
files={}, process_response=False)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(), "Result\n")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_post_files(self, _, mock_openphoto):
|
||||||
|
"""Check that files are posted correctly"""
|
||||||
|
post = mock_openphoto.return_value.post
|
||||||
|
main(["-X", "POST", "-F", "photo=@%s" % self.test_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_file)
|
||||||
|
|
||||||
|
@mock.patch.object(sys, "exit", raise_exception)
|
||||||
|
@mock.patch('sys.stderr', new_callable=io.StringIO)
|
||||||
|
def test_unknown_arg(self, mock_stderr):
|
||||||
|
"""Check that an unknown argument produces an error"""
|
||||||
|
with self.assertRaises(TestException):
|
||||||
|
main(["hello"])
|
||||||
|
self.assertIn("error: Unknown argument", mock_stderr.getvalue())
|
||||||
|
|
||||||
|
@mock.patch.object(sys, "exit", raise_exception)
|
||||||
|
@mock.patch('sys.stderr', new_callable=io.StringIO)
|
||||||
|
def test_unknown_option(self, mock_stderr):
|
||||||
|
"""Check that an unknown option produces an error"""
|
||||||
|
with self.assertRaises(TestException):
|
||||||
|
main(["--hello"])
|
||||||
|
self.assertIn("error: no such option", mock_stderr.getvalue())
|
||||||
|
|
||||||
|
@mock.patch.object(sys, "exit", raise_exception)
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_unknown_config(self, mock_stdout):
|
||||||
|
"""Check that an unknown config file produces an error"""
|
||||||
|
with self.assertRaises(TestException):
|
||||||
|
main(["--config=this_config_doesnt_exist"])
|
||||||
|
self.assertIn("No such file or directory", mock_stdout.getvalue())
|
||||||
|
self.assertIn("You must create a configuration file",
|
||||||
|
mock_stdout.getvalue())
|
||||||
|
self.assertIn("To get your credentials", mock_stdout.getvalue())
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_verbose(self, mock_stdout, _):
|
||||||
|
"""Check that the verbose option is working"""
|
||||||
|
main(["-v"])
|
||||||
|
self.assertIn("Method: GET", mock_stdout.getvalue())
|
||||||
|
self.assertIn("Endpoint: /photos/list.json", mock_stdout.getvalue())
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.main, "OpenPhoto")
|
||||||
|
@mock.patch('sys.stdout', new_callable=io.StringIO)
|
||||||
|
def test_pretty_print(self, mock_stdout, mock_openphoto):
|
||||||
|
"""Check that the pretty-print option is working"""
|
||||||
|
get = mock_openphoto.return_value.get
|
||||||
|
get.return_value = '{"test":1}'
|
||||||
|
main(["-p"])
|
||||||
|
self.assertEqual(mock_stdout.getvalue(), '{\n "test":1\n}\n')
|
|
@ -29,11 +29,12 @@ class TestConfig(unittest.TestCase):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_config(config_file, host):
|
def create_config(config_file, host):
|
||||||
|
"""Create a dummy config file"""
|
||||||
with open(os.path.join(CONFIG_PATH, config_file), "w") as conf:
|
with open(os.path.join(CONFIG_PATH, config_file), "w") as conf:
|
||||||
conf.write("host = %s\n" % host)
|
conf.write("host = %s\n" % host)
|
||||||
conf.write("# Comment\n\n")
|
conf.write("# Comment\n\n")
|
||||||
conf.write("consumerKey = \"%s_consumer_key\"\n" % config_file)
|
conf.write("consumerKey = \"%s_consumer_key\"\n" % config_file)
|
||||||
conf.write("\"consumerSecret\" = %s_consumer_secret\n" % config_file)
|
conf.write("\"consumerSecret\"= %s_consumer_secret\n" % config_file)
|
||||||
conf.write("'token'=%s_token\n" % config_file)
|
conf.write("'token'=%s_token\n" % config_file)
|
||||||
conf.write("tokenSecret = '%s_token_secret'\n" % config_file)
|
conf.write("tokenSecret = '%s_token_secret'\n" % config_file)
|
||||||
|
|
173
tests/unit/test_http.py
Normal file
173
tests/unit/test_http.py
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import httpretty
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
|
||||||
|
class TestHttp(unittest.TestCase):
|
||||||
|
test_host = "test.example.com"
|
||||||
|
test_endpoint = "test.json"
|
||||||
|
test_uri = "http://%s/%s" % (test_host, test_endpoint)
|
||||||
|
test_data = {"message": "Test Message",
|
||||||
|
"code": 200,
|
||||||
|
"result": "Test Result"}
|
||||||
|
test_oauth = {"consumer_key": "dummy",
|
||||||
|
"consumer_secret": "dummy",
|
||||||
|
"token": "dummy",
|
||||||
|
"token_secret": "dummy"}
|
||||||
|
test_file = os.path.join("tests", "unit", "data", "test_file.txt")
|
||||||
|
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host,
|
||||||
|
**self.test_oauth)
|
||||||
|
|
||||||
|
def _register_uri(self, method, uri=test_uri, data=None, body=None,
|
||||||
|
**kwds):
|
||||||
|
"""Convenience wrapper around httpretty.register_uri"""
|
||||||
|
if data is None:
|
||||||
|
data = self.test_data
|
||||||
|
if body is None:
|
||||||
|
body = json.dumps(data)
|
||||||
|
httpretty.register_uri(method, uri=uri, body=body, **kwds)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _last_request():
|
||||||
|
"""This is a temporary measure until httpretty PR#59 is merged"""
|
||||||
|
return httpretty.httpretty.last_request
|
||||||
|
|
||||||
|
def test_attributes(self):
|
||||||
|
"""Check that the host attribute has been set correctly"""
|
||||||
|
self.assertEqual(self.client.host, self.test_host)
|
||||||
|
self.assertEqual(self.client.config.host, self.test_host)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_parameters(self):
|
||||||
|
"""Check that the get method accepts parameters correctly"""
|
||||||
|
self._register_uri(httpretty.GET)
|
||||||
|
response = self.client.get(self.test_endpoint,
|
||||||
|
foo="bar", spam="eggs")
|
||||||
|
self.assertIn("OAuth", self._last_request().headers["authorization"])
|
||||||
|
self.assertEqual(self._last_request().querystring["foo"], ["bar"])
|
||||||
|
self.assertEqual(self._last_request().querystring["spam"], ["eggs"])
|
||||||
|
self.assertEqual(response, self.test_data)
|
||||||
|
self.assertEqual(self.client.last_url, self.test_uri)
|
||||||
|
self.assertEqual(self.client.last_params, {"foo": b"bar",
|
||||||
|
"spam": b"eggs"})
|
||||||
|
self.assertEqual(self.client.last_response.json(), self.test_data)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_parameters(self):
|
||||||
|
"""Check that the post method accepts parameters correctly"""
|
||||||
|
self._register_uri(httpretty.POST)
|
||||||
|
response = self.client.post(self.test_endpoint,
|
||||||
|
foo="bar", spam="eggs")
|
||||||
|
self.assertIn(b"spam=eggs", self._last_request().body)
|
||||||
|
self.assertIn(b"foo=bar", self._last_request().body)
|
||||||
|
self.assertEqual(response, self.test_data)
|
||||||
|
self.assertEqual(self.client.last_url, self.test_uri)
|
||||||
|
self.assertEqual(self.client.last_params, {"foo": b"bar",
|
||||||
|
"spam": b"eggs"})
|
||||||
|
self.assertEqual(self.client.last_response.json(), self.test_data)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_without_oauth(self):
|
||||||
|
"""Check that the get method works without OAuth parameters"""
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host)
|
||||||
|
self._register_uri(httpretty.GET)
|
||||||
|
response = self.client.get(self.test_endpoint)
|
||||||
|
self.assertNotIn("authorization", self._last_request().headers)
|
||||||
|
self.assertEqual(response, self.test_data)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_without_oauth(self):
|
||||||
|
"""Check that the post method fails without OAuth parameters"""
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host)
|
||||||
|
self._register_uri(httpretty.POST)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_without_response_processing(self):
|
||||||
|
"""Check that the get method works with response processing disabled"""
|
||||||
|
self._register_uri(httpretty.GET)
|
||||||
|
response = self.client.get(self.test_endpoint, process_response=False)
|
||||||
|
self.assertEqual(response, json.dumps(self.test_data))
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_without_response_processing(self):
|
||||||
|
"""Check that the post method works with response processing disabled"""
|
||||||
|
self._register_uri(httpretty.POST)
|
||||||
|
response = self.client.post(self.test_endpoint, process_response=False)
|
||||||
|
self.assertEqual(response, json.dumps(self.test_data))
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_parameter_processing(self):
|
||||||
|
"""Check that the parameter processing function is working"""
|
||||||
|
self._register_uri(httpretty.GET)
|
||||||
|
photo = openphoto.objects.Photo(None, {"id": "photo_id"})
|
||||||
|
album = openphoto.objects.Album(None, {"id": "album_id"})
|
||||||
|
tag = openphoto.objects.Tag(None, {"id": "tag_id"})
|
||||||
|
self.client.get(self.test_endpoint,
|
||||||
|
photo=photo, album=album, tag=tag,
|
||||||
|
list_=[photo, album, tag],
|
||||||
|
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["boolean"], ["1"])
|
||||||
|
self.assertIn(params["unicode_"], [["\xc3\xbcmlaut"], ["\xfcmlaut"]])
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_api_version(self):
|
||||||
|
"""Check that an API version can be specified for the get method"""
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host, api_version=1)
|
||||||
|
self._register_uri(httpretty.GET,
|
||||||
|
uri="http://%s/v1/%s" % (self.test_host,
|
||||||
|
self.test_endpoint))
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_api_version(self):
|
||||||
|
"""Check that an API version can be specified for the post method"""
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host, api_version=1,
|
||||||
|
**self.test_oauth)
|
||||||
|
self._register_uri(httpretty.POST,
|
||||||
|
uri="http://%s/v1/%s" % (self.test_host,
|
||||||
|
self.test_endpoint))
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_file(self):
|
||||||
|
"""Check that a file can be posted"""
|
||||||
|
self._register_uri(httpretty.POST)
|
||||||
|
with open(self.test_file, 'rb') as in_file:
|
||||||
|
response = self.client.post(self.test_endpoint,
|
||||||
|
files={"file": in_file})
|
||||||
|
self.assertEqual(response, self.test_data)
|
||||||
|
body = str(self._last_request().body)
|
||||||
|
self.assertIn("Content-Disposition: form-data; "+
|
||||||
|
"name=\"file\"; filename=\"test_file.txt\"", body)
|
||||||
|
self.assertIn("Test File", str(body))
|
||||||
|
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_file_parameters_are_sent_as_querystring(self):
|
||||||
|
"""
|
||||||
|
Check that parameters are send as a query string
|
||||||
|
when a file is posted
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST)
|
||||||
|
with open(self.test_file, 'rb') as in_file:
|
||||||
|
response = self.client.post(self.test_endpoint, foo="bar",
|
||||||
|
files={"file": in_file})
|
||||||
|
self.assertEqual(response, self.test_data)
|
||||||
|
self.assertEqual(self._last_request().querystring["foo"], ["bar"])
|
195
tests/unit/test_http_errors.py
Normal file
195
tests/unit/test_http_errors.py
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import json
|
||||||
|
import httpretty
|
||||||
|
|
||||||
|
# TEMP: Temporary hack until httpretty string checking is fixed
|
||||||
|
if httpretty.compat.PY3:
|
||||||
|
httpretty.core.basestring = (bytes, str)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
|
||||||
|
class TestHttpErrors(unittest.TestCase):
|
||||||
|
test_host = "test.example.com"
|
||||||
|
test_endpoint = "test.json"
|
||||||
|
test_uri = "http://%s/%s" % (test_host, test_endpoint)
|
||||||
|
test_data = {"message": "Test Message",
|
||||||
|
"code": 200,
|
||||||
|
"result": "Test Result"}
|
||||||
|
test_oauth = {"consumer_key": "dummy",
|
||||||
|
"consumer_secret": "dummy",
|
||||||
|
"token": "dummy",
|
||||||
|
"token_secret": "dummy"}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host,
|
||||||
|
**self.test_oauth)
|
||||||
|
|
||||||
|
def _register_uri(self, method, uri=test_uri,
|
||||||
|
data=None, body=None, status=200, **kwds):
|
||||||
|
"""Convenience wrapper around httpretty.register_uri"""
|
||||||
|
if data is None:
|
||||||
|
data = self.test_data
|
||||||
|
# Set the JSON return code to match the HTTP status
|
||||||
|
data["code"] = status
|
||||||
|
if body is None:
|
||||||
|
body = json.dumps(data)
|
||||||
|
httpretty.register_uri(method, uri=uri, body=body, status=status,
|
||||||
|
**kwds)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_error_status(self):
|
||||||
|
"""
|
||||||
|
Check that an error status causes the get method
|
||||||
|
to raise an exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.GET, status=500)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_error_status(self):
|
||||||
|
"""
|
||||||
|
Check that an error status causes the post method
|
||||||
|
to raise an exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST, status=500)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
# TODO: 404 status should raise 404 error, even if JSON is valid
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_404_status(self):
|
||||||
|
"""
|
||||||
|
Check that a 404 status causes the get method
|
||||||
|
to raise a 404 exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.GET, status=404)
|
||||||
|
with self.assertRaises(openphoto.OpenPhoto404Error):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
# TODO: 404 status should raise 404 error, even if JSON is valid
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_404_status(self):
|
||||||
|
"""
|
||||||
|
Check that a 404 status causes the post method
|
||||||
|
to raise a 404 exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST, status=404)
|
||||||
|
with self.assertRaises(openphoto.OpenPhoto404Error):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the get method to
|
||||||
|
raise an exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.GET, body="Invalid JSON")
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the post method to
|
||||||
|
raise an exception
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST, body="Invalid JSON")
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_error_status_and_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the get method to raise an exception,
|
||||||
|
even with an error status is returned
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.GET, body="Invalid JSON", status=500)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_error_status_and_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the post method to raise an exception,
|
||||||
|
even with an error status is returned
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST, body="Invalid JSON", status=500)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_404_status_and_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the get method to raise an exception,
|
||||||
|
even with a 404 status is returned
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.GET, body="Invalid JSON", status=404)
|
||||||
|
with self.assertRaises(openphoto.OpenPhoto404Error):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_404_status_and_invalid_json(self):
|
||||||
|
"""
|
||||||
|
Check that invalid JSON causes the post method to raise an exception,
|
||||||
|
even with a 404 status is returned
|
||||||
|
"""
|
||||||
|
self._register_uri(httpretty.POST, body="Invalid JSON", status=404)
|
||||||
|
with self.assertRaises(openphoto.OpenPhoto404Error):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_duplicate_status(self):
|
||||||
|
"""
|
||||||
|
Check that a get with a duplicate status
|
||||||
|
raises a duplicate exception
|
||||||
|
"""
|
||||||
|
data = {"message": "This photo already exists", "code": 409}
|
||||||
|
self._register_uri(httpretty.GET, data=data, status=409)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_duplicate_status(self):
|
||||||
|
"""
|
||||||
|
Check that a post with a duplicate status
|
||||||
|
raises a duplicate exception
|
||||||
|
"""
|
||||||
|
data = {"message": "This photo already exists", "code": 409}
|
||||||
|
self._register_uri(httpretty.POST, data=data, status=409)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoDuplicateError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
||||||
|
# TODO: Status code mismatch should raise an exception
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@httpretty.activate
|
||||||
|
def test_get_with_status_code_mismatch(self):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if a get returns a
|
||||||
|
status code that doesn't match the JSON code
|
||||||
|
"""
|
||||||
|
data = {"message": "Test Message", "code": 200}
|
||||||
|
self._register_uri(httpretty.GET, data=data, status=202)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.get(self.test_endpoint)
|
||||||
|
|
||||||
|
# TODO: Status code mismatch should raise an exception
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@httpretty.activate
|
||||||
|
def test_post_with_status_code_mismatch(self):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if a post returns a
|
||||||
|
status code that doesn't match the JSON code
|
||||||
|
"""
|
||||||
|
data = {"message": "Test Message", "code": 200}
|
||||||
|
self._register_uri(httpretty.POST, data=data, status=202)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.post(self.test_endpoint)
|
||||||
|
|
441
tests/unit/test_photos.py
Normal file
441
tests/unit/test_photos.py
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
import mock
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
|
||||||
|
class TestPhotos(unittest.TestCase):
|
||||||
|
test_host = "test.example.com"
|
||||||
|
test_file = os.path.join("tests", "unit", "data", "test_file.txt")
|
||||||
|
test_photos_dict = [{"id": "1a", "tags": ["tag1", "tag2"],
|
||||||
|
"totalPages": 1, "totalRows": 2},
|
||||||
|
{"id": "2b", "tags": ["tag3", "tag4"],
|
||||||
|
"totalPages": 1, "totalRows": 2}]
|
||||||
|
def setUp(self):
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host)
|
||||||
|
self.test_photos = [openphoto.objects.Photo(self.client, photo)
|
||||||
|
for photo in self.test_photos_dict]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _return_value(result, message="", code=200):
|
||||||
|
return {"message": message, "code": code, "result": result}
|
||||||
|
|
||||||
|
class TestPhotosList(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photos_list(self, mock_get):
|
||||||
|
"""Check that the photo list is returned correctly"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_photos_dict)
|
||||||
|
|
||||||
|
result = self.client.photos.list()
|
||||||
|
mock_get.assert_called_with("/photos/list.json")
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
self.assertEqual(result[0].id, "1a")
|
||||||
|
self.assertEqual(result[0].tags, ["tag1", "tag2"])
|
||||||
|
self.assertEqual(result[1].id, "2b")
|
||||||
|
self.assertEqual(result[1].tags, ["tag3", "tag4"])
|
||||||
|
|
||||||
|
class TestPhotosUpdate(TestPhotos):
|
||||||
|
# TODO: photos.update should accept a list of Photo objects
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_update(self, mock_post):
|
||||||
|
"""Check that multiple photos can be updated"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photos.update(self.test_photos, title="Test")
|
||||||
|
mock_post.assert_called_with("/photos/update.json",
|
||||||
|
ids=["1a", "2b"], title="Test")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_update_ids(self, mock_post):
|
||||||
|
"""Check that multiple photos can be updated using their IDs"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photos.update(["1a", "2b"], title="Test")
|
||||||
|
mock_post.assert_called_with("/photos/update.json",
|
||||||
|
ids=["1a", "2b"], title="Test")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_update_failure(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if multiple photos
|
||||||
|
cannot be updated
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.photos.update(self.test_photos, title="Test")
|
||||||
|
|
||||||
|
class TestPhotosDelete(TestPhotos):
|
||||||
|
# TODO: photos.delete should accept a list of Photo objects
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_delete(self, mock_post):
|
||||||
|
"""Check that multiple photos can be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photos.delete(self.test_photos)
|
||||||
|
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"])
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_delete_ids(self, mock_post):
|
||||||
|
"""Check that multiple photos can be deleted using their IDs"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photos.delete(["1a", "2b"])
|
||||||
|
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"])
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photos_delete_failure(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if multiple photos
|
||||||
|
cannot be deleted
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.photos.delete(self.test_photos)
|
||||||
|
|
||||||
|
class TestPhotoDelete(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_delete(self, mock_post):
|
||||||
|
"""Check that a photo can be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photo.delete(self.test_photos[0])
|
||||||
|
mock_post.assert_called_with("/photo/1a/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_delete_id(self, mock_post):
|
||||||
|
"""Check that a photo can be deleted using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.photo.delete("1a")
|
||||||
|
mock_post.assert_called_with("/photo/1a/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
# TODO: photo.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_delete_failure(self, mock_post):
|
||||||
|
"""Check that an exception is raised if a photo cannot be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.photo.delete(self.test_photos[0])
|
||||||
|
|
||||||
|
# TODO: after deleting object fields, name and id should be set to None
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_object_delete(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that a photo can be deleted when using
|
||||||
|
the photo object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
photo = self.test_photos[0]
|
||||||
|
result = photo.delete()
|
||||||
|
mock_post.assert_called_with("/photo/1a/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
self.assertEqual(photo.get_fields(), {})
|
||||||
|
# self.assertEqual(photo.id, None)
|
||||||
|
|
||||||
|
# TODO: photo.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_object_delete_failure(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if a photo cannot be deleted
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.test_photos[0].delete()
|
||||||
|
|
||||||
|
class TestPhotoEdit(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_edit(self, mock_get):
|
||||||
|
"""Check that a the photo edit endpoint is working"""
|
||||||
|
mock_get.return_value = self._return_value({"markup": "<form/>"})
|
||||||
|
result = self.client.photo.edit(self.test_photos[0])
|
||||||
|
mock_get.assert_called_with("/photo/1a/edit.json")
|
||||||
|
self.assertEqual(result, "<form/>")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_edit_id(self, mock_get):
|
||||||
|
"""Check that a the photo edit endpoint is working when using an ID"""
|
||||||
|
mock_get.return_value = self._return_value({"markup": "<form/>"})
|
||||||
|
result = self.client.photo.edit("1a")
|
||||||
|
mock_get.assert_called_with("/photo/1a/edit.json")
|
||||||
|
self.assertEqual(result, "<form/>")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_object_edit(self, mock_get):
|
||||||
|
"""
|
||||||
|
Check that a the photo edit endpoint is working
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_get.return_value = self._return_value({"markup": "<form/>"})
|
||||||
|
result = self.test_photos[0].edit()
|
||||||
|
mock_get.assert_called_with("/photo/1a/edit.json")
|
||||||
|
self.assertEqual(result, "<form/>")
|
||||||
|
|
||||||
|
class TestPhotoReplace(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
# TODO: replace_encoded parameter should be called photo_file,
|
||||||
|
# not encoded_photo
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, '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)
|
||||||
|
|
||||||
|
class TestPhotoUpdate(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_update(self, mock_post):
|
||||||
|
"""Check that a photo can be updated"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.update(self.test_photos[0], title="Test")
|
||||||
|
mock_post.assert_called_with("/photo/1a/update.json", title="Test")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_update_id(self, mock_post):
|
||||||
|
"""Check that a photo can be updated using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.update("1a", title="Test")
|
||||||
|
mock_post.assert_called_with("/photo/1a/update.json", title="Test")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_object_update(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that a photo can be updated
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
photo = self.test_photos[0]
|
||||||
|
photo.update(title="Test")
|
||||||
|
mock_post.assert_called_with("/photo/1a/update.json", title="Test")
|
||||||
|
self.assertEqual(photo.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
class TestPhotoView(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_view(self, mock_get):
|
||||||
|
"""Check that a photo can be viewed"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.view(self.test_photos[0],
|
||||||
|
returnSizes="20x20")
|
||||||
|
mock_get.assert_called_with("/photo/1a/view.json", returnSizes="20x20")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_view_id(self, mock_get):
|
||||||
|
"""Check that a photo can be viewed using its ID"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.view("1a", returnSizes="20x20")
|
||||||
|
mock_get.assert_called_with("/photo/1a/view.json", returnSizes="20x20")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_object_view(self, mock_get):
|
||||||
|
"""
|
||||||
|
Check that a photo can be viewed
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
photo = self.test_photos[0]
|
||||||
|
photo.view(returnSizes="20x20")
|
||||||
|
mock_get.assert_called_with("/photo/1a/view.json", returnSizes="20x20")
|
||||||
|
self.assertEqual(photo.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
class TestPhotoUpload(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_upload(self, mock_post):
|
||||||
|
"""Check that a photo can be uploaded"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[0])
|
||||||
|
result = self.client.photo.upload(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/upload.json",))
|
||||||
|
self.assertEqual(title, "Test")
|
||||||
|
self.assertIn("photo", files)
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[0])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_upload_encoded(self, mock_post):
|
||||||
|
"""Check that a photo can be uploaded using Base64 encoding"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[0])
|
||||||
|
result = self.client.photo.upload_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/upload.json",
|
||||||
|
photo=encoded_file, title="Test")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[0])
|
||||||
|
|
||||||
|
class TestPhotoDynamicUrl(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_dynamic_url(self, _):
|
||||||
|
""" If photo.dynamic_url gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.photo.dynamic_url(self.test_photos[0])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_dynamic_url_id(self, _):
|
||||||
|
""" If photo.dynamic_url gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.client.photo.dynamic_url("1a")
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_object_dynamic_url(self, _):
|
||||||
|
""" If photo.dynamic_url gets implemented, write a test! """
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.test_photos[0].dynamic_url()
|
||||||
|
|
||||||
|
class TestPhotoNextPrevious(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_next_previous(self, mock_get):
|
||||||
|
"""Check that the next/previous photos are returned"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"next": [self.test_photos_dict[0]],
|
||||||
|
"previous": [self.test_photos_dict[1]]})
|
||||||
|
result = self.client.photo.next_previous(self.test_photos[0])
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["next"][0].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertEqual(result["previous"][0].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_next_previous_id(self, mock_get):
|
||||||
|
"""
|
||||||
|
Check that the next/previous photos are returned
|
||||||
|
when using the photo ID
|
||||||
|
"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"next": [self.test_photos_dict[0]],
|
||||||
|
"previous": [self.test_photos_dict[1]]})
|
||||||
|
result = self.client.photo.next_previous("1a")
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["next"][0].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertEqual(result["previous"][0].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_object_next_previous(self, mock_get):
|
||||||
|
"""
|
||||||
|
Check that the next/previous photos are returned
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"next": [self.test_photos_dict[0]],
|
||||||
|
"previous": [self.test_photos_dict[1]]})
|
||||||
|
result = self.test_photos[0].next_previous()
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["next"][0].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertEqual(result["previous"][0].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_next(self, mock_get):
|
||||||
|
"""Check that the next photos are returned"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"next": [self.test_photos_dict[0]]})
|
||||||
|
result = self.client.photo.next_previous(self.test_photos[0])
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["next"][0].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertNotIn("previous", result)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_previous(self, mock_get):
|
||||||
|
"""Check that the previous photos are returned"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"previous": [self.test_photos_dict[1]]})
|
||||||
|
result = self.client.photo.next_previous(self.test_photos[0])
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["previous"][0].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
self.assertNotIn("next", result)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_photo_multiple_next_previous(self, mock_get):
|
||||||
|
"""Check that multiple next/previous photos are returned"""
|
||||||
|
mock_get.return_value = self._return_value(
|
||||||
|
{"next": [self.test_photos_dict[0], self.test_photos_dict[0]],
|
||||||
|
"previous": [self.test_photos_dict[1], self.test_photos_dict[1]]})
|
||||||
|
result = self.client.photo.next_previous(self.test_photos[0])
|
||||||
|
mock_get.assert_called_with("/photo/1a/nextprevious.json")
|
||||||
|
self.assertEqual(result["next"][0].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertEqual(result["next"][1].get_fields(),
|
||||||
|
self.test_photos_dict[0])
|
||||||
|
self.assertEqual(result["previous"][0].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
self.assertEqual(result["previous"][1].get_fields(),
|
||||||
|
self.test_photos_dict[1])
|
||||||
|
|
||||||
|
class TestPhotoTransform(TestPhotos):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_transform(self, mock_post):
|
||||||
|
"""Check that a photo can be transformed"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.transform(self.test_photos[0], rotate="90")
|
||||||
|
mock_post.assert_called_with("/photo/1a/transform.json", rotate="90")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_transform_id(self, mock_post):
|
||||||
|
"""Check that a photo can be transformed using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
result = self.client.photo.transform("1a", rotate="90")
|
||||||
|
mock_post.assert_called_with("/photo/1a/transform.json", rotate="90")
|
||||||
|
self.assertEqual(result.get_fields(), self.test_photos_dict[1])
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_photo_object_transform(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that a photo can be transformed
|
||||||
|
when using the photo object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_photos_dict[1])
|
||||||
|
photo = self.test_photos[0]
|
||||||
|
photo.transform(rotate="90")
|
||||||
|
mock_post.assert_called_with("/photo/1a/transform.json", rotate="90")
|
||||||
|
self.assertEqual(photo.get_fields(), self.test_photos_dict[1])
|
128
tests/unit/test_tags.py
Normal file
128
tests/unit/test_tags.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import mock
|
||||||
|
try:
|
||||||
|
import unittest2 as unittest # Python2.6
|
||||||
|
except ImportError:
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import openphoto
|
||||||
|
|
||||||
|
class TestTags(unittest.TestCase):
|
||||||
|
test_host = "test.example.com"
|
||||||
|
test_tags = None
|
||||||
|
test_tags_dict = [{"count": 11, "id":"tag1"},
|
||||||
|
{"count": 5, "id":"tag2"}]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = openphoto.OpenPhoto(host=self.test_host)
|
||||||
|
self.test_tags = [openphoto.objects.Tag(self.client, tag)
|
||||||
|
for tag in self.test_tags_dict]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _return_value(result, message="", code=200):
|
||||||
|
return {"message": message, "code": code, "result": result}
|
||||||
|
|
||||||
|
class TestTagsList(TestTags):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'get')
|
||||||
|
def test_tags_list(self, mock_get):
|
||||||
|
"""Check that the the tag list is returned correctly"""
|
||||||
|
mock_get.return_value = self._return_value(self.test_tags_dict)
|
||||||
|
result = self.client.tags.list()
|
||||||
|
mock_get.assert_called_with("/tags/list.json")
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
self.assertEqual(result[0].id, "tag1")
|
||||||
|
self.assertEqual(result[0].count, 11)
|
||||||
|
self.assertEqual(result[1].id, "tag2")
|
||||||
|
self.assertEqual(result[1].count, 5)
|
||||||
|
|
||||||
|
class TestTagCreate(TestTags):
|
||||||
|
# TODO: should return a tag object, not a result dict
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_create(self, mock_post):
|
||||||
|
"""Check that a tag can be created"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_tags_dict[0])
|
||||||
|
result = self.client.tag.create(tag="Test", foo="bar")
|
||||||
|
mock_post.assert_called_with("/tag/create.json", tag="Test", foo="bar")
|
||||||
|
self.assertEqual(result.id, "tag1")
|
||||||
|
self.assertEqual(result.count, 11)
|
||||||
|
|
||||||
|
class TestTagDelete(TestTags):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_delete(self, mock_post):
|
||||||
|
"""Check that a tag can be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.tag.delete(self.test_tags[0])
|
||||||
|
mock_post.assert_called_with("/tag/tag1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_delete_id(self, mock_post):
|
||||||
|
"""Check that a tag can be deleted using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
result = self.client.tag.delete("tag1")
|
||||||
|
mock_post.assert_called_with("/tag/tag1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
|
||||||
|
# TODO: tag.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_delete_failure(self, mock_post):
|
||||||
|
"""Check that an exception is raised if a tag cannot be deleted"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.client.tag.delete(self.test_tags[0])
|
||||||
|
|
||||||
|
# TODO: after deleting object fields, id should be set to None
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_object_delete(self, mock_post):
|
||||||
|
"""Check that a tag can be deleted when using the tag object directly"""
|
||||||
|
mock_post.return_value = self._return_value(True)
|
||||||
|
tag = self.test_tags[0]
|
||||||
|
result = tag.delete()
|
||||||
|
mock_post.assert_called_with("/tag/tag1/delete.json")
|
||||||
|
self.assertEqual(result, True)
|
||||||
|
self.assertEqual(tag.get_fields(), {})
|
||||||
|
# self.assertEqual(tag.id, None)
|
||||||
|
|
||||||
|
# TODO: tag.delete should raise exception on failure
|
||||||
|
@unittest.expectedFailure
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_object_delete_failure(self, mock_post):
|
||||||
|
"""
|
||||||
|
Check that an exception is raised if a tag cannot be deleted
|
||||||
|
when using the tag object directly
|
||||||
|
"""
|
||||||
|
mock_post.return_value = self._return_value(False)
|
||||||
|
with self.assertRaises(openphoto.OpenPhotoError):
|
||||||
|
self.test_tags[0].delete()
|
||||||
|
|
||||||
|
class TestTagUpdate(TestTags):
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_update(self, mock_post):
|
||||||
|
"""Check that a tag can be updated"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_tags_dict[1])
|
||||||
|
result = self.client.tag.update(self.test_tags[0], name="Test")
|
||||||
|
mock_post.assert_called_with("/tag/tag1/update.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "tag2")
|
||||||
|
self.assertEqual(result.count, 5)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_update_id(self, mock_post):
|
||||||
|
"""Check that a tag can be updated using its ID"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_tags_dict[1])
|
||||||
|
result = self.client.tag.update("tag1", name="Test")
|
||||||
|
mock_post.assert_called_with("/tag/tag1/update.json", name="Test")
|
||||||
|
self.assertEqual(result.id, "tag2")
|
||||||
|
self.assertEqual(result.count, 5)
|
||||||
|
|
||||||
|
@mock.patch.object(openphoto.OpenPhoto, 'post')
|
||||||
|
def test_tag_object_update(self, mock_post):
|
||||||
|
"""Check that a tag can be updated when using the tag object directly"""
|
||||||
|
mock_post.return_value = self._return_value(self.test_tags_dict[1])
|
||||||
|
tag = self.test_tags[0]
|
||||||
|
tag.update(name="Test")
|
||||||
|
mock_post.assert_called_with("/tag/tag1/update.json", name="Test")
|
||||||
|
self.assertEqual(tag.id, "tag2")
|
||||||
|
self.assertEqual(tag.count, 5)
|
||||||
|
|
9
tox.ini
9
tox.ini
|
@ -2,10 +2,15 @@
|
||||||
envlist = py26, py27, py33
|
envlist = py26, py27, py33
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands = python -m unittest discover --catch
|
commands = python -m unittest discover --catch tests/unit
|
||||||
|
deps =
|
||||||
|
mock >= 1.0.0
|
||||||
|
httpretty >= 0.6.1
|
||||||
|
|
||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
commands = unit2 discover --catch
|
commands = unit2 discover --catch tests/unit
|
||||||
deps =
|
deps =
|
||||||
|
mock >= 1.0.0
|
||||||
|
httpretty >= 0.6.1
|
||||||
unittest2
|
unittest2
|
||||||
discover
|
discover
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue