Skip to content

Commit 569db35

Browse files
committed
Move api test-helper functions to conftest.py
Preparing to write some api tests for the `shelflist` app, but I need to access the test helper functions originally defined in `api.tests.test_api`. I changed them to pytest fixtures and moved them into the top-level conftest.py file.
1 parent 51ef9c7 commit 569db35

2 files changed

Lines changed: 155 additions & 118 deletions

File tree

django/sierra/api/tests/test_api.py

Lines changed: 36 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
Contains integration tests for the `api` app.
33
"""
44

5-
import urllib
6-
import random
75
from datetime import datetime
86
from pytz import utc
97

@@ -20,6 +18,12 @@
2018
# api_solr_env
2119
# basic_solr_assembler
2220
# api_client
21+
# pick_reference_object_having_link
22+
# assert_obj_fields_match_serializer
23+
# get_linked_view_and_objects
24+
# assemble_test_records
25+
# do_filter_search
26+
# get_found_ids
2327

2428

2529
# API_ROOT: Base URL for the API we're testing.
@@ -1898,100 +1902,6 @@ def compile_ids(parameters):
18981902
return tuple(p.keys()[0] for p in parameters[1:])
18991903

19001904

1901-
# HELPER FUNCTIONS used in tests
1902-
1903-
def pick_reference_object_having_link(objects, link_field):
1904-
"""
1905-
Test helper function that picks an object (in JSON terms) from the
1906-
list of `objects` -- e.g., from an API list view -- and randomly
1907-
chooses one that has the given `link_field` populated.
1908-
"""
1909-
choices = [o for o in objects if o['_links'].get(link_field, None)]
1910-
return random.choice(choices)
1911-
1912-
1913-
def assert_obj_fields_match_serializer(obj, serializer):
1914-
"""
1915-
Test helper function that asserts that the given `obj` conforms to
1916-
the given `serializer` -- all fields on the serializer are
1917-
represented on the object.
1918-
"""
1919-
for field_name in serializer.fields:
1920-
assert serializer.render_field_name(field_name) in obj
1921-
1922-
1923-
def get_linked_view_and_objects(client, ref_obj, link_field):
1924-
"""
1925-
Test helper function: given a `client` object fixture and
1926-
`ref_obj` (i.e. reference object, from the API) -- grab objects
1927-
from the given `link_field` and return them. Returns a tuple:
1928-
(view_obj, linked_objs). For the sake of normalization, the
1929-
returned linked_objs is ALWAYS a list, even if the link references
1930-
a single object.
1931-
"""
1932-
linked_objs = []
1933-
try:
1934-
resp = client.get(ref_obj['_links'][link_field]['href'])
1935-
except TypeError:
1936-
for link in ref_obj['_links'][link_field]:
1937-
resp = client.get(link['href'])
1938-
linked_objs.append(resp.data)
1939-
else:
1940-
try:
1941-
linked_objs = resp.data['_embedded'].values()[0]
1942-
except KeyError:
1943-
linked_objs = [resp.data]
1944-
return resp.renderer_context['view'], linked_objs
1945-
1946-
1947-
def assemble_test_records(profile, id_field, test_data, solr_env, assembler):
1948-
"""
1949-
Test helper function that assembles & loads a set of test records
1950-
given a `profile` string, a set of static `test_data` records, the
1951-
name of the `id_field` for each record (for test_data purposes), a
1952-
module-level `solr_env` assembler fixture, and a function-level
1953-
`assembler` fixture. Returns a tuple of default solr_env records
1954-
(the ones loaded by the module-level fixture) and the new test
1955-
records that were loaded from the provided test data.
1956-
len(env_recs) + len(test_recs) should == the total number of Solr
1957-
records for that profile.
1958-
"""
1959-
gens = assembler.gen_factory
1960-
env_recs = solr_env.records[profile]
1961-
test_recs = assembler.load_static_test_data(profile, test_data, id_field,
1962-
env_recs)
1963-
return (env_recs, test_recs)
1964-
1965-
1966-
def do_filter_search(resource, search, client):
1967-
"""
1968-
Test helper function that performs the given `search` (e.g. search
1969-
query string) on the given API `resource` via the given
1970-
`api_client` fixture. Returns the response.
1971-
"""
1972-
qs = '&'.join(['='.join([urllib.quote_plus(v) for v in pair.split('=')])
1973-
for pair in search.split('&')])
1974-
return client.get('{}{}/?{}'.format(API_ROOT, resource, qs))
1975-
1976-
1977-
def get_found_ids(solr_id_field, response):
1978-
"""
1979-
Returns a list of values for identifying test records, in order,
1980-
from the given `response` object. (Usually the response will come
1981-
from calling `do_filter_search`.) `solr_id_field` is the name of
1982-
that ID field as it exists in Solr.
1983-
"""
1984-
serializer = response.renderer_context['view'].get_serializer()
1985-
api_id_field = serializer.render_field_name(solr_id_field)
1986-
total_found = response.data['totalCount']
1987-
data = response.data.get('_embedded', {'data': []}).values()[0]
1988-
# reality check: FAIL if there's any data returned on a different
1989-
# page of results. If we don't return ALL available data, further
1990-
# assertions will be invalid.
1991-
assert len(data) == total_found
1992-
return [r[api_id_field] for r in data]
1993-
1994-
19951905
# PYTEST FIXTURES
19961906

19971907
@pytest.fixture
@@ -2018,7 +1928,8 @@ def api_settings(settings):
20181928

20191929
@pytest.mark.django_db
20201930
def test_apiusers_authenticated_requests(api_client,
2021-
simple_sig_auth_credentials):
1931+
simple_sig_auth_credentials,
1932+
assert_obj_fields_match_serializer):
20221933
"""
20231934
The apiusers resource requires authentication to access; users that
20241935
can authenticate can view the apiusers list and details of a single
@@ -2109,7 +2020,9 @@ def test_apiusers_repeated_requests_fail(api_client,
21092020

21102021

21112022
@pytest.mark.parametrize('resource', RESOURCE_METADATA.keys())
2112-
def test_standard_resource(resource, api_settings, api_solr_env, api_client):
2023+
def test_standard_resource(resource, api_settings, api_solr_env, api_client,
2024+
pick_reference_object_having_link,
2025+
assert_obj_fields_match_serializer):
21132026
"""
21142027
Standard resources (each with a "list" and "detail" view) should
21152028
have objects available in an "_embedded" object in the list view,
@@ -2131,7 +2044,10 @@ def test_standard_resource(resource, api_settings, api_solr_env, api_client):
21312044
@pytest.mark.parametrize('resource, links',
21322045
compile_resource_links(RESOURCE_METADATA))
21332046
def test_standard_resource_links(resource, links, api_settings, api_solr_env,
2134-
api_client):
2047+
api_client,
2048+
pick_reference_object_having_link,
2049+
assert_obj_fields_match_serializer,
2050+
get_linked_view_and_objects):
21352051
"""
21362052
Accessing linked resources from standard resources (via _links)
21372053
should return the expected resource(s).
@@ -2263,7 +2179,8 @@ def test_list_view_pagination(resource, default_limit, max_limit, limit,
22632179
ids=compile_ids(PARAMETERS__FILTER_TESTS__INTENDED) +
22642180
compile_ids(PARAMETERS__FILTER_TESTS__STRANGE))
22652181
def test_list_view_filters(resource, test_data, search, expected, api_settings,
2266-
api_solr_env, basic_solr_assembler, api_client):
2182+
assemble_test_records, api_client, get_found_ids,
2183+
do_filter_search):
22672184
"""
22682185
Given the provided `test_data` records: requesting the given
22692186
`resource` using the provided search filter parameters (`search`)
@@ -2276,15 +2193,15 @@ def test_list_view_filters(resource, test_data, search, expected, api_settings,
22762193

22772194
profile = RESOURCE_METADATA[resource]['profile']
22782195
id_field = RESOURCE_METADATA[resource]['id_field']
2279-
erecs, trecs = assemble_test_records(profile, id_field, test_data,
2280-
api_solr_env, basic_solr_assembler)
2196+
erecs, trecs = assemble_test_records(profile, id_field, test_data)
22812197

22822198
# First let's do a quick sanity check to make sure the resource
22832199
# returns the correct num of records before the filter is applied.
2284-
check_response = api_client.get('{}{}/'.format(API_ROOT, resource))
2200+
full_resource_path = '{}{}'.format(API_ROOT, resource)
2201+
check_response = api_client.get('{}/'.format(full_resource_path))
22852202
assert check_response.data['totalCount'] == len(erecs) + len(trecs)
22862203

2287-
response = do_filter_search(resource, search, api_client)
2204+
response = do_filter_search(full_resource_path, search, api_client)
22882205
found_ids = set(get_found_ids(id_field, response))
22892206
assert all([i in found_ids for i in expected_ids])
22902207
assert all([i not in found_ids for i in not_expected_ids])
@@ -2296,7 +2213,8 @@ def test_list_view_filters(resource, test_data, search, expected, api_settings,
22962213
ids=compile_ids(PARAMETERS__ORDERBY_TESTS__INTENDED) +
22972214
compile_ids(PARAMETERS__ORDERBY_TESTS__STRANGE))
22982215
def test_list_view_orderby(resource, test_data, search, expected, api_settings,
2299-
api_solr_env, basic_solr_assembler, api_client):
2216+
assemble_test_records, api_client, get_found_ids,
2217+
do_filter_search):
23002218
"""
23012219
Given the provided `test_data` records: requesting the given
23022220
`resource` using the provided search filter parameters (`search`)
@@ -2305,10 +2223,10 @@ def test_list_view_orderby(resource, test_data, search, expected, api_settings,
23052223
"""
23062224
profile = RESOURCE_METADATA[resource]['profile']
23072225
id_field = RESOURCE_METADATA[resource]['id_field']
2308-
erecs, trecs = assemble_test_records(profile, id_field, test_data,
2309-
api_solr_env, basic_solr_assembler)
2226+
erecs, trecs = assemble_test_records(profile, id_field, test_data)
23102227
print [r.get('call_number_sort', None) for r in trecs]
2311-
response = do_filter_search(resource, search, api_client)
2228+
full_resource_path = '{}{}'.format(API_ROOT, resource)
2229+
response = do_filter_search(full_resource_path, search, api_client)
23122230
found_ids = get_found_ids(id_field, response)
23132231
assert found_ids == expected
23142232

@@ -2317,8 +2235,8 @@ def test_list_view_orderby(resource, test_data, search, expected, api_settings,
23172235
compile_params(PARAMETERS__FIRSTITEMPERLOCATION),
23182236
ids=compile_ids(PARAMETERS__FIRSTITEMPERLOCATION))
23192237
def test_firstitemperlocation_list(test_data, search, expected, api_settings,
2320-
api_solr_env, basic_solr_assembler,
2321-
api_client):
2238+
assemble_test_records, api_client,
2239+
get_found_ids, do_filter_search):
23222240
"""
23232241
The `firstitemperlocation` resource is basically a custom filter
23242242
for `items` that submits a facet-query to Solr asking for the first
@@ -2339,10 +2257,10 @@ def test_firstitemperlocation_list(test_data, search, expected, api_settings,
23392257
for resource in data.keys():
23402258
profile = RESOURCE_METADATA[resource]['profile']
23412259
id_field = RESOURCE_METADATA[resource]['id_field']
2342-
assemble_test_records(profile, id_field, data[resource], api_solr_env,
2343-
basic_solr_assembler)
2260+
assemble_test_records(profile, id_field, data[resource])
23442261

2345-
rsp = do_filter_search('firstitemperlocation', search, api_client)
2262+
full_resource_path = '{}firstitemperlocation'.format(API_ROOT)
2263+
rsp = do_filter_search(full_resource_path, search, api_client)
23462264
found_ids = set(get_found_ids(RESOURCE_METADATA['items']['id_field'], rsp))
23472265
assert all([i in found_ids for i in expected_ids])
23482266
assert all([i not in found_ids for i in not_expected_ids])
@@ -2352,8 +2270,8 @@ def test_firstitemperlocation_list(test_data, search, expected, api_settings,
23522270
compile_params(PARAMETERS__CALLNUMBERMATCHES),
23532271
ids=compile_ids(PARAMETERS__CALLNUMBERMATCHES))
23542272
def test_callnumbermatches_list(test_data, search, expected, api_settings,
2355-
api_solr_env, basic_solr_assembler,
2356-
api_client):
2273+
assemble_test_records, api_client,
2274+
do_filter_search):
23572275
"""
23582276
The `callnumbermatches` resource simply returns an array of
23592277
callnumber strings, in order, matching the critera that's given.
@@ -2369,9 +2287,9 @@ def test_callnumbermatches_list(test_data, search, expected, api_settings,
23692287
for resource in data.keys():
23702288
profile = RESOURCE_METADATA[resource]['profile']
23712289
id_field = RESOURCE_METADATA[resource]['id_field']
2372-
assemble_test_records(profile, id_field, data[resource], api_solr_env,
2373-
basic_solr_assembler)
2290+
assemble_test_records(profile, id_field, data[resource])
23742291

2375-
response = do_filter_search('callnumbermatches', search, api_client)
2292+
full_resource_path = '{}callnumbermatches'.format(API_ROOT)
2293+
response = do_filter_search(full_resource_path, search, api_client)
23762294
assert response.data == expected
23772295

django/sierra/conftest.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import pysolr
99
import pytz
1010
import hashlib
11+
import urllib
12+
import random
1113
from datetime import datetime
1214
from collections import OrderedDict
1315

@@ -728,3 +730,120 @@ def _apiuser_with_custom_defaults(defaults=None):
728730
old_defaults = APIUser.permission_defaults.copy()
729731
yield _apiuser_with_custom_defaults
730732
APIUser.permission_defaults = old_defaults
733+
734+
735+
@pytest.fixture(scope='function')
736+
def pick_reference_object_having_link():
737+
"""
738+
Pytest fixture. Returns a helper function that picks an object (in
739+
JSON terms) from the list of `objects` -- e.g., from an API list
740+
view -- and randomly chooses one that has the given `link_field`
741+
populated.
742+
"""
743+
def _pick_reference_object_having_link(objects, link_field):
744+
choices = [o for o in objects if o['_links'].get(link_field, None)]
745+
return random.choice(choices)
746+
return _pick_reference_object_having_link
747+
748+
749+
@pytest.fixture(scope='function')
750+
def assert_obj_fields_match_serializer():
751+
"""
752+
Pytest fixture. Returns a helper function that asserts that the
753+
given `obj` conforms to the given `serializer` -- all fields on the
754+
serializer are represented on the object.
755+
"""
756+
def _assert_obj_fields_match_serializer(obj, serializer):
757+
for field_name in serializer.fields:
758+
assert serializer.render_field_name(field_name) in obj
759+
return _assert_obj_fields_match_serializer
760+
761+
762+
@pytest.fixture(scope='function')
763+
def get_linked_view_and_objects():
764+
"""
765+
Pytest fixture. Returns a helper function, where, given a `client`
766+
object fixture and `ref_obj` (i.e. reference object, from the API)
767+
-- grab objects from the given `link_field` and return them.
768+
Returns a tuple: (view_obj, linked_objs). The returned linked_objs
769+
is ALWAYS a list, even if the link references a single object.
770+
"""
771+
def _get_linked_view_and_objects(client, ref_obj, link_field):
772+
linked_objs = []
773+
try:
774+
resp = client.get(ref_obj['_links'][link_field]['href'])
775+
except TypeError:
776+
for link in ref_obj['_links'][link_field]:
777+
resp = client.get(link['href'])
778+
linked_objs.append(resp.data)
779+
else:
780+
try:
781+
linked_objs = resp.data['_embedded'].values()[0]
782+
except KeyError:
783+
linked_objs = [resp.data]
784+
return resp.renderer_context['view'], linked_objs
785+
return _get_linked_view_and_objects
786+
787+
788+
@pytest.fixture(scope='function')
789+
def assemble_test_records(api_solr_env, basic_solr_assembler):
790+
"""
791+
Pytest fixture. Returns a helper function that assembles & loads a
792+
set of test records (for one test) into an existing module-level
793+
Solr test-data environment.
794+
795+
Defaults to using `api_solr_env` and `basic_solr_assembler`
796+
fixtures, but you can override these via the `env` and `assembler`
797+
kwargs.
798+
799+
Required args include a `profile` string, a set of static
800+
`test_data` partial records, and the name of the unique `id_field`
801+
for each record (for test_data record uniqueness). Returns a tuple
802+
of default solr_env records and the new test records that were
803+
loaded from the provided test data. len(env_recs) + len(test_recs)
804+
should == the total number of Solr records for that profile.
805+
"""
806+
def _assemble_test_records(profile, id_field, test_data, env=api_solr_env,
807+
assembler=basic_solr_assembler):
808+
env_recs = env.records[profile]
809+
test_recs = assembler.load_static_test_data(profile, test_data,
810+
id_field=id_field,
811+
context=env_recs)
812+
return (env_recs, test_recs)
813+
return _assemble_test_records
814+
815+
816+
@pytest.fixture(scope='function')
817+
def do_filter_search():
818+
"""
819+
Pytest fixture. Returns a test helper function that performs the
820+
given `search` (e.g. search query string) on the given API
821+
`resource` via the given `api_client` fixture. Returns the
822+
response.
823+
"""
824+
def _do_filter_search(resource, search, client):
825+
q = '&'.join(['='.join([urllib.quote_plus(v) for v in pair.split('=')])
826+
for pair in search.split('&')])
827+
return client.get('{}/?{}'.format(resource, q))
828+
return _do_filter_search
829+
830+
831+
@pytest.fixture(scope='function')
832+
def get_found_ids():
833+
"""
834+
Returns a list of values for identifying test records, in order,
835+
from the given `response` object. (Usually the response will come
836+
from calling `do_filter_search`.) `solr_id_field` is the name of
837+
that ID field as it exists in Solr.
838+
"""
839+
def _get_found_ids(solr_id_field, response):
840+
serializer = response.renderer_context['view'].get_serializer()
841+
api_id_field = serializer.render_field_name(solr_id_field)
842+
total_found = response.data['totalCount']
843+
data = response.data.get('_embedded', {'data': []}).values()[0]
844+
# reality check: FAIL if there's any data returned on a different
845+
# page of results. If we don't return ALL available data, further
846+
# assertions will be invalid.
847+
assert len(data) == total_found
848+
return [r[api_id_field] for r in data]
849+
return _get_found_ids

0 commit comments

Comments
 (0)