Skip to content

Commit

Permalink
Merge pull request #52 from zimeon/url-return-and-tests
Browse files Browse the repository at this point in the history
URL return and tests
  • Loading branch information
azaroth42 authored Mar 21, 2018
2 parents a9865d0 + 23fb476 commit e620780
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ python:
- "3.3"
- "3.4"
- "3.5"
- "3.6"
install:
- python setup.py install
- pip install coveralls
script:
- python setup.py test
- coverage run --omit=tests/*,setup.py,*.egg --include=iiif-presentation-validator.py setup.py test
- coverage run --include=iiif-presentation-validator.py setup.py test
after_success:
- coveralls
33 changes: 22 additions & 11 deletions iiif-presentation-validator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# encoding: utf-8
"""IIIF Presentation Validation Service"""
"""IIIF Presentation Validation Service."""

import argparse
import codecs
Expand All @@ -27,11 +27,14 @@


class Validator(object):
"""Validator class that runs with Bottle."""

def __init__(self):
"""Initialize Validator with default_version."""
self.default_version = "2.1"

def fetch(self, url):
"""Fetch manifest from url."""
req = Request(url)
req.add_header('Accept-Encoding', 'gzip')

Expand All @@ -50,11 +53,10 @@ def fetch(self, url):
data = data.decode('utf-8')
except:
pass
return(data, wh)

return (data, wh)

def check_manifest(self, data, version, warnings=[]):
# Now check data
def check_manifest(self, data, version, url=None, warnings=[]):
"""Check manifest data at version, return JSON."""
reader = ManifestReader(data, version=version)
err = None
try:
Expand All @@ -72,15 +74,18 @@ def check_manifest(self, data, version, warnings=[]):
'received': data,
'okay': okay,
'warnings': warnings,
'error': str(err)
'error': str(err),
'url': url
}
return self.return_json(infojson)

def return_json(self, js):
"""Set header and return JSON response."""
response.content_type = "application/json"
return json.dumps(js)

def do_POST_test(self):
"""Implement POST request to test posted data at default version."""
data = request.json
if not data:
b = request._get_body_string()
Expand All @@ -92,12 +97,12 @@ def do_POST_test(self):
return self.check_manifest(data, self.default_version)

def do_GET_test(self):
"""Implement GET request to test url at version."""
url = request.query.get('url', '')
version = request.query.get('version', self.default_version)
url = url.strip()

parsed_url = urlparse(url)
if not parsed_url.scheme.startswith('http'):
if (parsed_url.scheme != 'http' and parsed_url.scheme != 'https'):
return self.return_json({'okay': 0, 'error': 'URLs must use HTTP or HTTPS', 'url': url})

try:
Expand Down Expand Up @@ -127,20 +132,23 @@ def do_GET_test(self):
' header does not include Accept-Encoding, which'
' can cause compatibility issues')

return self.check_manifest(data, version, warnings)
return self.check_manifest(data, version, url, warnings)

def index_route(self):
"""Read and return index page."""
with codecs.open(os.path.join(os.path.dirname(__file__), 'index.html'), 'r', 'utf-8') as fh:
data = fh.read()
return data

def dispatch_views(self):
"""Set up path mappings."""
self.app.route("/", "GET", self.index_route)
self.app.route("/validate", "OPTIONS", self.empty_response)
self.app.route("/validate", "GET", self.do_GET_test)
self.app.route("/validate", "POST", self.do_POST_test)

def after_request(self):
"""Used with after_request hook to set response headers."""
methods = 'GET,POST,OPTIONS'
headers = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
response.headers['Access-Control-Allow-Origin'] = '*'
Expand All @@ -149,22 +157,24 @@ def after_request(self):
response.headers['Allow'] = methods

def empty_response(self, *args, **kwargs):
"""Empty response"""
"""Empty response."""

def get_bottle_app(self):
"""Returns bottle instance"""
"""Return bottle instance."""
self.app = Bottle()
self.dispatch_views()
self.app.hook('after_request')(self.after_request)
return self.app


def apache():
"""Run as WSGI application."""
v = Validator()
return v.get_bottle_app()


def main():
"""Parse argument and run server when run from command line."""
parser = argparse.ArgumentParser(description=__doc__.strip(),
formatter_class=argparse.ArgumentDefaultsHelpFormatter)

Expand All @@ -178,6 +188,7 @@ def main():
v = Validator()
run(host=args.hostname, port=args.port, app=v.get_bottle_app())


if __name__ == "__main__":
main()
else:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Setup for iiif/presentation-validator"""
"""Setup for iiif/presentation-validator."""
from setuptools import setup, Command
import os
import re
Expand Down
45 changes: 33 additions & 12 deletions tests/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import unittest
from mock import Mock
import imp
from bottle import Response, request
from bottle import Response, request, LocalRequest
try:
# python3
from urllib.request import URLError
Expand All @@ -17,29 +17,36 @@
fh = open('iiif-presentation-validator.py', 'r')
try:
val_mod = imp.load_module('ipv', fh, 'iiif-presentation-validator.py',
('py','r',imp.PY_SOURCE))
('py', 'r', imp.PY_SOURCE))
finally:
fh.close()


def read_fixture(fixture):
"""Read data from text fixture."""
with open(fixture, 'r') as fh:
data = fh.read()
return(data)


class MockWSGI(object):
"""Mock WSGI object with data read from fixture."""

def __init__(self, fixture):
"""Initialize mock object with fixture filename."""
self.fixture = fixture

def read(self, clen):
"""Read from fixture."""
return read_fixture(self.fixture)


class MockWebHandle(object):
"""Mock WebHandle object with empty headers."""

def __init__(self):
self.headers={}
"""Initialize mock object with empty headers."""
self.headers = {}


class TestAll(unittest.TestCase):
Expand Down Expand Up @@ -69,6 +76,7 @@ def test03_check_manifest(self):
self.assertEqual(j['okay'], 0)

def test04_do_POST_test(self):
"""Test POST requests -- machine interaction with validator service."""
v = val_mod.Validator()
# FIXME - nasty hack to mock data for bottle.request
m = MockWSGI('fixtures/1/manifest.json')
Expand All @@ -78,26 +86,39 @@ def test04_do_POST_test(self):
self.assertEqual(j['okay'], 1)

def test05_do_GET_test(self):
# FIXME - hack to mock data for bottle.request
"""Test GET requests -- typical user interaction with web form."""
# Note that attempting to set request.environ['QUERY_STRING'] to mock
# input data works only the first time. Instead create a new request
# object to similate each web request, with data that sets request.environ
v = val_mod.Validator()
request.environ['QUERY_STRING'] = 'url=http://example.org/a'
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'),MockWebHandle()))
request = LocalRequest({'QUERY_STRING': 'url=https://example.org/a'})
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 1)
self.assertEqual(j['url'], 'https://example.org/a')
# fetch failure
v = val_mod.Validator()
request = LocalRequest({'QUERY_STRING': 'url=http://example.org/a'})
v.fetch = Mock()
v.fetch.side_effect = Exception('Fetch failed')
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 0)
self.assertEqual(j['error'], 'Cannot fetch url')
# bogus URL
v = val_mod.Validator()
request.environ['QUERY_STRING'] = 'url=not_http://a.b.c/'
v.fetch = Mock(return_value=('',MockWebHandle()))
request = LocalRequest({'QUERY_STRING': 'url=not_http://a.b.c/'})
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 0)
# bogus URL but not caught
# another bogus URL
v = val_mod.Validator()
request.environ['QUERY_STRING'] = 'url=httpX://a.b/'
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'),MockWebHandle()))
request = LocalRequest({'QUERY_STRING': 'url=httpX://a.b/'})
v.fetch = Mock(return_value=(read_fixture('fixtures/1/manifest.json'), MockWebHandle()))
j = json.loads(v.do_GET_test())
self.assertEqual(j['okay'], 1) #FIXME!
self.assertEqual(j['okay'], 0)

def test06_index_route(self):
"""Test index page."""
v = val_mod.Validator()
html = v.index_route()
self.assertTrue(html.startswith('<!DOCTYPE html>'))

0 comments on commit e620780

Please sign in to comment.