Skip to content

Commit

Permalink
[WIP] CSW tests (#215)
Browse files Browse the repository at this point in the history
* add CSW tests

* ensure PostGIS support

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* test force closing PostgreSQL instance

* fix db teardown
  • Loading branch information
tomkralidis authored and ingenieroariel committed Sep 26, 2016
1 parent 1361985 commit f9365cb
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 19 deletions.
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pre-up:
$(DOCKER_COMPOSE) up -d postgres rabbitmq

wait:
sleep 2
sleep 60


up:
Expand Down Expand Up @@ -70,12 +70,17 @@ test-endtoend-selenium-firefox:
# Want to see whats happening? connect to VNC server localhost:5900 password: secret
$(DOCKER_COMPOSE) run $(END_TO_END_TEST_FLAGS) django python manage.py test hypermap.tests.test_end_to_end_selenium_firefox --failfast

test-csw: DOCKER_FILES=$(DEV_DOCKER_FILES)
test-csw:
# Run tests CSW requests <--> Nginx
$(DOCKER_COMPOSE) run $(TEST_CSW_TRANSACTIONS_FLAGS) django python manage.py test hypermap.tests.test_csw --failfast

test-csw-transactions: DOCKER_FILES=$(DEV_DOCKER_FILES)
test-csw-transactions:
# Run tests CSW requests <--> Nginx
$(DOCKER_COMPOSE) run $(TEST_CSW_TRANSACTIONS_FLAGS) django python manage.py test hypermap.tests.test_csw_transactions --failfast

test: down start test-unit test-solr test-elastic test-csw-transactions test-endtoend-selenium-firefox
test: down start test-unit test-csw test-csw-transactions test-solr test-elastic test-endtoend-selenium-firefox

shell: $(DOCKER_COMPOSE) run django python manage.py shell_plus

Expand Down
4 changes: 2 additions & 2 deletions _docs/admins.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ Add the following django applications into the main project INSTALLED_APPS setti
## Test CSW transactions

```
curl -v "http://(admin_username):(admin_password)@(host)/registry/(catalog)/csw/?service=CSW&request=Transaction&version=2.0.2" -d "@(document_body_path)"
curl -v "http://(admin_username):(admin_password)@(host)/registry/(catalog)/csw?service=CSW&request=Transaction&version=2.0.2" -d "@(document_body_path)"
```

As an example, using the template found in the data folder,

```
curl -v "http://admin:admin@localhost/registry/hypermap/csw/?service=CSW&request=Transaction&version=2.0.2" -d "@data/cswt_insert.xml"
curl -v "http://admin:admin@localhost/registry/hypermap/csw?service=CSW&request=Transaction&version=2.0.2" -d "@data/cswt_insert.xml"
```

Verify that layers have been added into the database.
Expand Down
8 changes: 4 additions & 4 deletions _docs/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Insert layers from a XML file located in `data/cswt_insert.xml` with `request=Tr

```
curl -v -X "POST" \
"http://admin:admin@localhost/registry/hypermap/csw/?service=CSW&request=Transaction" \
"http://admin:admin@localhost/registry/hypermap/csw?service=CSW&request=Transaction" \
-H "Content-Type: application/xml" \
-f "data:@data/cswt_insert.xml"
```
Expand All @@ -185,19 +185,19 @@ Return the 10 layers added before with `request=GetRecords`

```
curl -X "GET" \
"http://admin:admin@localhost/registry/hypermap/csw/?service=CSW&version=2.0.2&request=GetRecords&typenames=csw:Record&elementsetname=full&resulttype=results
"http://admin:admin@localhost/registry/hypermap/csw?service=CSW&version=2.0.2&request=GetRecords&typenames=csw:Record&elementsetname=full&resulttype=results
```

#### 3. Filter layers
Query records with `mode=opensearch` and `q=` parameter, in this example one layer is named "Airport"

```
curl -X "GET" \
"http://admin:admin@localhost/registry/hypermap/csw/?mode=opensearch&service=CSW&version=2.0.2&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q=Airport"
"http://admin:admin@localhost/registry/hypermap/csw?mode=opensearch&service=CSW&version=2.0.2&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q=Airport"
```

#### 4. Ensure layers are also indexed in Search backend:
```
curl -X "GET" \
"http://localhost/_elastic/hypermap/_search"
```
```
8 changes: 5 additions & 3 deletions django.env
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
DATABASE_URL=postgres://postgres:postgres@postgres:5432/postgres
DATABASE_URL=postgis://postgres:postgres@postgres:5432/postgres
BROKER_URL=amqp://guest:guest@rabbitmq:5672/
CACHE_URL=pymemcached://memcached:11211/
BASE_URL=django
ALLOWED_HOSTS=['django',]
BASE_URL=localhost
BASE_PORT=8081
SITE_URL=http://localhost:8081
ALLOWED_HOSTS=('django', 'localhost')
REGISTRY_SEARCH_URL=elasticsearch+http://elasticsearch:9200/
REGISTRY_MAPPING_PRECISION=500m
REGISTRY_CHECK_PERIOD=120
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '2'
services:

postgres:
image: postgres
image: mdillon/postgis

elasticsearch:
image: elasticsearch:1.7
Expand Down
2 changes: 1 addition & 1 deletion hypermap/search/pycsw_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def __init__(self, context, repo_filter=None):
self.dbtype = settings.DATABASES['default']['ENGINE'].split('.')[-1]

# HHypermap PostgreSQL installs are PostGIS enabled
if self.dbtype == 'postgresql_psycopg2':
if self.dbtype == 'postgis':
self.dbtype = 'postgresql+postgis+wkt'

if self.dbtype in ['sqlite', 'sqlite3']: # load SQLite query bindings
Expand Down
2 changes: 1 addition & 1 deletion hypermap/search/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from . import views

urlpatterns = [
url(r'^(?P<catalog_slug>[\w-]+)/csw/$', views.csw_global_dispatch_by_catalog,
url(r'^(?P<catalog_slug>[\w-]+)/csw$', views.csw_global_dispatch_by_catalog,
name='csw_global_dispatch_by_catalog'),
url(r'^opensearch$', views.opensearch_dispatch, name='opensearch_dispatch')
]
Expand Down
182 changes: 182 additions & 0 deletions hypermap/tests/test_csw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# -*- coding: utf-8 -*-
import json
import os

from django.conf import settings
from django.contrib.auth.models import User
from django.db import connections
from django.db.utils import OperationalError
from django.test import Client, LiveServerTestCase
from lxml.etree import XMLSyntaxError
from owslib.csw import CatalogueServiceWeb
from owslib.fes import BBox, PropertyIsLike
from hypermap.aggregator.models import Catalog, Layer, Service

catalog_test_slug = 'hypermap'


class TestCSW(LiveServerTestCase):
def setUp(self):
self.script_name = '/registry/{}/csw'.format(catalog_test_slug)
self.url = '{}{}'.format(self.live_server_url, self.script_name)
self.username = 'admin'
self.password = 'admin'
self.client = Client()
user = User.objects.create(username=self.username)
user.set_password(self.password)
user.save()
self.client.login(username=self.username, password=self.password)

settings.REGISTRY_PYCSW['server']['url'] = self.url

Catalog.objects.get_or_create(
name=catalog_test_slug
)

Layer.objects.all().delete()
Service.objects.all().delete()

print ""
print ">>> with env:"
print "REGISTRY_SKIP_CELERY: %s" % settings.REGISTRY_SKIP_CELERY
print "REGISTRY_LIMIT_LAYERS: %s" % settings.REGISTRY_LIMIT_LAYERS
print "REGISTRY_CHECK_PERIOD: %s" % settings.REGISTRY_CHECK_PERIOD
print "REGISTRY_SEARCH_URL: %s" % settings.REGISTRY_SEARCH_URL
print "REGISTRY_HARVEST_SERVICES: %s" % settings.REGISTRY_HARVEST_SERVICES
print ""

# Post the 10 Layers contained in this file: data/cswt_insert.xml
path = os.path.join(settings.PROJECT_DIR, "..",
"data", "cswt_insert.xml")
with open(path, 'rb') as ff:
payload = ff.read()
content_type = "application/xml"

res = self.client.post(self.url, data=payload, content_type=content_type)
self.assertEqual(res.status_code, 200)
self.assertEqual(Layer.objects.all().count(), 10)

def test_csw(self):
# test 2.0.2 Basic Service Profile

self.csw = CatalogueServiceWeb(self.url, version='2.0.2', username=self.username, password=self.password)
self.assertEqual(self.csw.version, '2.0.2')

self.assertIn('2.0.2', self.csw.parameters['version'].values)
self.assertIn('3.0.0', self.csw.parameters['version'].values)

for op in self.csw.operations:
for method in op.methods:
self.assertEqual(self.csw.url, method['url'])

self.assertTrue('Transaction' in [o.name for o in self.csw.operations])
self.assertTrue('Harvest' in [o.name for o in self.csw.operations])

get_records_op = self.csw.get_operation_by_name('GetRecords')
self.assertIn('application/json', get_records_op.parameters['outputFormat']['values'])

# test basic search, no predicates
self.csw.getrecords2()
self.assertEqual(Layer.objects.all().count(), self.csw.results['matches'])

# test csw:AnyText
anytext = PropertyIsLike('csw:AnyText', 'Brasilia')
self.csw.getrecords2(constraints=[anytext])
self.assertEqual(self.csw.results['matches'], 1)

anytext = PropertyIsLike('csw:AnyText', 'roads')
self.csw.getrecords2(constraints=[anytext])
self.assertEqual(self.csw.results['matches'], 4)

# test ogc:BBOX
bbox = BBox(['-13', '-80', '15', '-30'])
self.csw.getrecords2(constraints=[bbox])
self.assertEqual(self.csw.results['matches'], 2)

# test csw:AnyText OR ogc:BBOX
self.csw.getrecords2(constraints=[anytext, bbox])
self.assertEqual(self.csw.results['matches'], 5)

# test csw:AnyText && ogc:BBOX
self.csw.getrecords2(constraints=[[anytext, bbox]])
self.assertEqual(self.csw.results['matches'], 1)

# test that ElementSetName=full stores full metadata record as inserted
self.csw.getrecords2(esn='full')
self.assertIn('xmlns:registry="http://gis.harvard.edu/HHypermap/registry/0.1"', self.csw.response)

# test JSON output
# TODO: fix owslib.csw.CatalogueServiceWeb.getrecords2 to handle non-XML request/response
with self.assertRaises(XMLSyntaxError):
self.csw.getrecords2(constraints=[anytext, bbox], format='application/json')

records_json = json.loads(self.csw.response)
self.assertEqual(records_json['csw:GetRecordsResponse']['csw:SearchResults']['@numberOfRecordsMatched'], '5')

# test 3.0.0 OpenSearch
bsp = {
'mode': 'opensearch',
'service': 'CSW',
'version': '3.0.0',
'request': 'GetRecords',
'typenames': 'csw:Record',
'elementsetname': 'full',
'outputformat': 'application/json'
}

# test basic search, no predicates
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '10')

# test q
bsp['q'] = 'Brasilia'
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '1')
bsp.pop('q')

# test bbox
bsp['bbox'] = '-80,-13,-30,15'
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '2')
bsp.pop('bbox')

# test time
bsp['time'] = '2014-09-23T12:04:31.102243+00:00/'
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '10')
bsp.pop('time')

# test q and bbox
bsp['q'] = 'roads'
bsp['bbox'] = '-80,-13,-30,15'
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '1')

# test q and bbox and time
bsp['time'] = '2014-09-23T12:04:31.102243+00:00/'
res = json.loads(self.client.get(self.script_name, bsp).content)
self.assertEqual(res['atom:feed']['os:totalResults'], '1')

@classmethod
def tearDownClass(cls):
# Workaround for https://code.djangoproject.com/ticket/22414
# Persistent connections not closed by LiveServerTestCase, preventing dropping test databases
# https://github.com/cjerdonek/django/commit/b07fbca02688a0f8eb159f0dde132e7498aa40cc
def close_sessions(conn):
close_sessions_query = """
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE
datname = current_database() AND
pid <> pg_backend_pid();
"""
with conn.cursor() as cursor:
try:
cursor.execute(close_sessions_query)
except OperationalError:
# We get kicked out after closing.
pass

for alias in connections:
connections[alias].close()
close_sessions(connections[alias])

print "Forcefully closed database connections."
8 changes: 3 additions & 5 deletions hypermap/tests/test_csw_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,14 @@ def test_post(self):
payload = open(path, 'rb').read()
content_type = "application/xml"

url = "/registry/{0}/csw/?service=CSW&request=Transaction".format(
catalog_test_slug
)
url = "/registry/{0}/csw".format(catalog_test_slug)

res = self.client.post(url, data=payload, content_type=content_type)
self.assertEqual(res.status_code, 200)
self.assertEqual(Layer.objects.all().count(), 10)

# List the Layers posted above
url = "/registry/{0}/csw/?service=CSW&version=2.0.2&request=" \
url = "/registry/{0}/csw?service=CSW&version=2.0.2&request=" \
"GetRecords&typenames=csw:Record&elementsetname=full&" \
"resulttype=results".format(catalog_test_slug)
res = self.client.get(url)
Expand All @@ -87,7 +85,7 @@ def test_post(self):
self.assertEqual(res.content.count("Manaus Roads (OSM May 2016)"), 2)

# Search one Layer posted above
url = "/registry/{0}/csw/?mode=opensearch&service=CSW&version" \
url = "/registry/{0}/csw?mode=opensearch&service=CSW&version" \
"=2.0.2&request=GetRecords&elementsetname=full&typenames=" \
"csw:Record&resulttype=results" \
"&q=Airport".format(catalog_test_slug)
Expand Down

0 comments on commit f9365cb

Please sign in to comment.