Skip to content

Commit 0e748ca

Browse files
authored
Merge pull request #38 from unt-libraries/add-basic-exporter-tests
Add basic exporter tests
2 parents e9a1b04 + 019f666 commit 0e748ca

8 files changed

Lines changed: 514466 additions & 4 deletions

File tree

django/sierra/base/fixtures/eresfixtures.json

Lines changed: 513504 additions & 0 deletions
Large diffs are not rendered by default.

django/sierra/base/fixtures/makefixtures/eresources.json

Lines changed: 697 additions & 0 deletions
Large diffs are not rendered by default.

django/sierra/base/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class ControlField(ReadOnlyModel):
188188
p42 = models.CharField(max_length=1, blank=True)
189189
p43 = models.CharField(max_length=1, blank=True)
190190
occ_num = models.IntegerField(null=True, blank=True)
191-
remainder = models.CharField(max_length=100, blank=True)
191+
remainder = models.CharField(max_length=100, null=True, blank=True)
192192

193193
def get_tag(self):
194194
return '{:03}'.format(self.control_num)
@@ -797,7 +797,7 @@ class BibRecord(MainRecordTypeModel):
797797
index_change_count = models.IntegerField(null=True, blank=True)
798798
is_on_course_reserve = models.NullBooleanField(null=True, blank=True)
799799
is_right_result_exact = models.NullBooleanField(null=True, blank=True)
800-
allocation_rule_code = models.CharField(max_length=1, blank=True)
800+
allocation_rule_code = models.CharField(max_length=1, null=True, blank=True)
801801
skip_num = models.IntegerField(null=True, blank=True)
802802
cataloging_date_gmt = models.DateTimeField(null=True, blank=True)
803803
marc_type_code = models.CharField(max_length=1, blank=True)
@@ -1197,7 +1197,7 @@ class HoldingRecord(MainRecordTypeModel):
11971197
null=True,
11981198
blank=True)
11991199
is_inherit_loc = models.NullBooleanField(null=True, blank=True)
1200-
allocation_rule_code = models.CharField(max_length=1, blank=True)
1200+
allocation_rule_code = models.CharField(max_length=1, null=True, blank=True)
12011201
accounting_unit = models.ForeignKey('AccountingUnit',
12021202
db_column='accounting_unit_code_num',
12031203
to_field='code_num',

django/sierra/conftest.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
Contains all shared pytest fixtures
3+
"""
4+
5+
import pytest
6+
import pysolr
7+
from model_mommy import mommy
8+
9+
from django.conf import settings
10+
11+
import utils
12+
from export import models as em
13+
from base import models as bm
14+
15+
16+
# Base app-related fixtures
17+
18+
@pytest.fixture
19+
def sierra_records_by_recnum_range():
20+
"""
21+
Return Sierra records by record number range.
22+
23+
This is a pytest fixture that returns a set of objects based on the
24+
provided start and (optional) end record number. Uses the
25+
appropriate model based on the type of record, derived from the
26+
first character of the record numbers (e.g., b is BibRecord).
27+
28+
Metadata about the filter used is added to an `info` attribute on
29+
the object queryset that is returned.
30+
"""
31+
def _sierra_records_by_recnum_range(start, end=None):
32+
rectype = start[0]
33+
filter_options = {'record_range_from': start,
34+
'record_range_to': end or start}
35+
modelname = bm.RecordMetadata.record_type_models[rectype]
36+
model = getattr(bm, modelname)
37+
recset = model.objects.filter_by('record_range', filter_options)
38+
recset.info = {
39+
'filter_method': 'filter_by',
40+
'filter_args': ['record_range', filter_options],
41+
'filter_kwargs': {},
42+
'export_filter_type': 'record_range',
43+
'export_filter_options': filter_options,
44+
}
45+
return recset
46+
return _sierra_records_by_recnum_range
47+
48+
49+
@pytest.fixture
50+
def sierra_full_object_set():
51+
"""
52+
Return a full set of objects from a Sierra (base) model.
53+
54+
This is a pytest fixture that returns a full set of model objects
55+
based on the type of model provided.
56+
57+
Metadata about the filter used is added to an `info` attribute on
58+
the object queryset that is returned.
59+
"""
60+
def _sierra_full_object_set(model_name):
61+
recset = getattr(bm, model_name).objects.all()
62+
recset.info = {
63+
'filter_method': 'all',
64+
'filter_args': [],
65+
'filter_kwargs': {},
66+
'export_filter_type': 'full_export',
67+
'export_filter_options': {}
68+
}
69+
return recset
70+
return _sierra_full_object_set
71+
72+
73+
# Export app-related fixtures
74+
75+
76+
@pytest.fixture
77+
def export_type():
78+
def _export_type(code):
79+
return em.ExportType.objects.get(code=code)
80+
return _export_type
81+
82+
83+
@pytest.fixture
84+
def export_filter():
85+
def _export_filter(code):
86+
return em.ExportFilter.objects.get(code=code)
87+
return _export_filter
88+
89+
90+
@pytest.fixture
91+
def status():
92+
def _status(code):
93+
return em.Status.objects.get(code=code)
94+
return _status
95+
96+
97+
@pytest.fixture
98+
def new_export_instance(export_type, export_filter, status):
99+
def _new_export_instance(et_code, ef_code, st_code):
100+
return mommy.make(
101+
em.ExportInstance,
102+
status=status(st_code),
103+
export_type=export_type(et_code),
104+
export_filter=export_filter(ef_code),
105+
errors=0,
106+
warnings=0
107+
)
108+
return _new_export_instance
109+
110+
111+
@pytest.fixture
112+
def new_exporter(new_export_instance):
113+
def _new_exporter(et_code, ef_code, st_code, options={}):
114+
instance = new_export_instance(et_code, ef_code, st_code)
115+
exp_class = utils.load_class(instance.export_type.path)
116+
return exp_class(instance.pk, ef_code, et_code, options)
117+
yield _new_exporter
118+
em.ExportInstance.objects.all().delete()
119+
120+
121+
@pytest.fixture
122+
def get_records():
123+
def _get_records(exporter):
124+
return exporter.get_records()
125+
return _get_records
126+
127+
128+
@pytest.fixture
129+
def export_records():
130+
def _export_records(exporter, records):
131+
exporter.export_records(records)
132+
exporter.final_callback()
133+
return _export_records
134+
135+
136+
@pytest.fixture
137+
def delete_records():
138+
def _delete_records(exporter, records):
139+
exporter.delete_records(records)
140+
exporter.final_callback()
141+
return _delete_records
142+
143+
144+
# Utils-related fixtures
145+
146+
@pytest.fixture
147+
def solr_conn():
148+
url = 'http://{}:{}/solr'.format(settings.SOLR_HOST, settings.SOLR_PORT)
149+
def _solr_conn(core_name):
150+
connection = pysolr.Solr('{}/{}'.format(url, core_name))
151+
_solr_conn.connections.append(connection)
152+
return connection
153+
_solr_conn.connections = []
154+
yield _solr_conn
155+
for conn in _solr_conn.connections:
156+
conn.delete(q='*:*')
157+
158+
159+
@pytest.fixture
160+
def solr_search():
161+
def _solr_search(conn, options):
162+
results = conn.search(**options)
163+
return [r for r in results]
164+
return _solr_search

django/sierra/export/tests/__init__.py

Whitespace-only changes.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Tests classes derived from `export.exporter.Exporter`.
3+
"""
4+
5+
import pytest
6+
7+
8+
# FIXTURES AND TEST DATA
9+
# Fixtures used in the below tests can be found in
10+
# django/sierra/base/tests/conftest.py:
11+
# sierra_records_by_recnum_range, sierra_full_object_set,
12+
# new_exporter, export_records, delete_records, solr_conn,
13+
# solr_search
14+
15+
pytestmark = pytest.mark.django_db
16+
17+
18+
@pytest.fixture
19+
def solr_exporter_test_params(sierra_records_by_recnum_range,
20+
sierra_full_object_set):
21+
bib_set = sierra_records_by_recnum_range('b4371446')
22+
eres_set = sierra_records_by_recnum_range('e1001249')
23+
item_set = sierra_records_by_recnum_range('i4264281')
24+
itype_set = sierra_full_object_set('ItypeProperty')
25+
istatus_set = sierra_full_object_set('ItemStatusProperty')
26+
location_set = sierra_full_object_set('Location')
27+
28+
return {
29+
'BibsToSolr': {
30+
'record_set': bib_set,
31+
'cores': ['bibdata', 'marc'],
32+
'try_delete': True
33+
},
34+
'EResourcesToSolr': {
35+
'record_set': eres_set,
36+
'cores': ['haystack'],
37+
'try_delete': True
38+
},
39+
'ItemsToSolr': {
40+
'record_set': item_set,
41+
'cores': ['haystack'],
42+
'try_delete': True
43+
},
44+
'ItemStatusesToSolr': {
45+
'record_set': istatus_set,
46+
'cores': ['haystack'],
47+
'try_delete': False
48+
},
49+
'ItypesToSolr': {
50+
'record_set': itype_set,
51+
'cores': ['haystack'],
52+
'try_delete': False
53+
},
54+
'LocationsToSolr': {
55+
'record_set': location_set,
56+
'cores': ['haystack'],
57+
'try_delete': False
58+
},
59+
}
60+
61+
62+
# TESTS
63+
64+
@pytest.mark.parametrize('etype_code', [
65+
'BibsToSolr',
66+
'EResourcesToSolr',
67+
'ItemsToSolr',
68+
'ItemStatusesToSolr',
69+
'ItypesToSolr',
70+
'LocationsToSolr'])
71+
def test_exports_to_solr(etype_code, solr_exporter_test_params, new_exporter,
72+
export_records, delete_records, solr_conn,
73+
solr_search):
74+
"""
75+
For Exporter classes that load data into Solr, blah
76+
"""
77+
record_set = solr_exporter_test_params[etype_code]['record_set']
78+
cores = solr_exporter_test_params[etype_code]['cores']
79+
try_delete = solr_exporter_test_params[etype_code]['try_delete']
80+
load_exporter = new_exporter(etype_code, 'full_export', 'waiting')
81+
conns = {c: solr_conn(c) for c in cores}
82+
pre_results = {c: solr_search(conns[c], {'q': '*'}) for c in cores}
83+
export_records(load_exporter, record_set)
84+
load_results = {c: solr_search(conns[c], {'q': '*'}) for c in cores}
85+
del_results = {}
86+
if try_delete:
87+
del_exporter = new_exporter(etype_code, 'full_export', 'waiting')
88+
delete_records(del_exporter, record_set)
89+
del_results = {c: solr_search(conns[c], {'q': '*'}) for c in cores}
90+
91+
for core in cores:
92+
assert len(pre_results[core]) == 0
93+
assert len(load_results[core]) > 0
94+
if try_delete:
95+
assert len(del_results[core]) == 0

django/sierra/sierra/settings/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ def raise_setting_error(setting):
385385
]
386386

387387
# The path (relative or absolute) to the command that runs SolrMarc.
388-
SOLRMARC_COMMAND = '../../solr/solrmarc/indexfile.sh'
388+
SOLRMARC_COMMAND = get_env_variable('SOLRMARC_COMMAND',
389+
'../../solr/solrmarc/indexfile.sh')
389390
# The name of the properties file to use when running SolrMarc.
390391
SOLRMARC_CONFIG_FILE = get_env_variable('SOLRMARC_CONFIG_FILE',
391392
'dev_config.properties')

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ services:
173173
- ./docker_data/test/media:/project/media
174174
environment:
175175
- SOLRMARC_CONFIG_FILE=${TEST_SOLRMARC_CONFIG_FILE:-test_config.properties}
176+
- SOLRMARC_COMMAND=/project/catalog-api/solr/solrmarc/indexfile.sh
176177
working_dir: /project/catalog-api/
177178
networks:
178179
- testing

0 commit comments

Comments
 (0)