def setup(self): super(TestEnkiAPI, self).setup() self.collection = self._collection(protocol=EnkiAPI.ENKI) self.api = MockEnkiAPI(self._db)
class TestEnkiAPI(DatabaseTest, BaseEnkiTest): def setup(self): super(TestEnkiAPI, self).setup() self.collection = self._collection(protocol=EnkiAPI.ENKI) self.api = MockEnkiAPI(self._db) def test_create_identifier_strings(self): identifier = self._identifier(identifier_type=Identifier.ENKI_ID) values = EnkiAPI.create_identifier_strings(["foo", identifier]) eq_(["foo", identifier.identifier], values) def test_import_instantiation(self): """Test that EnkiImport can be instantiated""" imp = EnkiImport(self._db, self.collection, api_class=self.api.__class__) assert_not_equal(None, imp) def test_fulfillment_open_access(self): """Test that fulfillment info for non-ACS Enki books is parsed correctly.""" data = self.get_data("checked_out_direct.json") self.api.queue_response(200, content=data) result = json.loads(data) fulfill_data = self.api.parse_fulfill_result(result['result']) eq_( fulfill_data[0], """http://cccl.enkilibrary.org/API/UserAPI?method=downloadEContentFile&username=21901000008080&password=deng&lib=1&recordId=2""" ) eq_(fulfill_data[1], 'epub') def test_fulfillment_acs(self): """Test that fulfillment info for ACS Enki books is parsed correctly.""" data = self.get_data("checked_out_acs.json") self.api.queue_response(200, content=data) result = json.loads(data) fulfill_data = self.api.parse_fulfill_result(result['result']) eq_( fulfill_data[0], """http://afs.enkilibrary.org/fulfillment/URLLink.acsm?action=enterloan&ordersource=Califa&orderid=ACS4-9243146841581187248119581&resid=urn%3Auuid%3Ad5f54da9-8177-43de-a53d-ef521bc113b4&gbauthdate=Wed%2C+23+Aug+2017+19%3A42%3A35+%2B0000&dateval=1503517355&rights=%24lat%231505331755%24&gblver=4&auth=8604f0fc3f014365ea8d3c4198c721ed7ed2c16d""" ) eq_(fulfill_data[1], 'epub') def test_checkout_open_access(self): """Test that checkout info for non-ACS Enki books is parsed correctly.""" data = self.get_data("checked_out_direct.json") self.api.queue_response(200, content=data) result = json.loads(data) loan = self.api.parse_patron_loans( result['result']['checkedOutItems'][0]) eq_(loan.data_source_name, DataSource.ENKI) eq_(loan.identifier_type, Identifier.ENKI_ID) eq_(loan.identifier, "econtentRecord2") eq_(loan.start_date, datetime.datetime(2017, 8, 23, 19, 31, 58, 0)) eq_(loan.end_date, datetime.datetime(2017, 9, 13, 19, 31, 58, 0)) def test_checkout_acs(self): """Test that checkout info for ACS Enki books is parsed correctly.""" data = self.get_data("checked_out_acs.json") self.api.queue_response(200, content=data) result = json.loads(data) loan = self.api.parse_patron_loans( result['result']['checkedOutItems'][0]) eq_(loan.data_source_name, DataSource.ENKI) eq_(loan.identifier_type, Identifier.ENKI_ID) eq_(loan.identifier, "econtentRecord3334") eq_(loan.start_date, datetime.datetime(2017, 8, 23, 19, 42, 35, 0)) eq_(loan.end_date, datetime.datetime(2017, 9, 13, 19, 42, 35, 0)) @raises(AuthorizationFailedException) def test_login_fail(self): """Test that the correct exception is thrown upon an unsuccessful login.""" data = self.get_data("login_unsuccessful.json") self.api.queue_response(200, content=data) result = json.loads(data) edition, pool = self._edition(identifier_type=Identifier.ENKI_ID, data_source_name=DataSource.ENKI, with_license_pool=True) pool.identifier.identifier = 'notanid' patron = self._patron(external_identifier='notabarcode') loan = self.api.checkout(patron, 'notapin', pool, None) @raises(NoAvailableCopies) def test_login_fail(self): """Test that the correct exception is thrown upon an unsuccessful login.""" data = self.get_data("no_copies.json") self.api.queue_response(200, content=data) result = json.loads(data) edition, pool = self._edition(identifier_type=Identifier.ENKI_ID, data_source_name=DataSource.ENKI, with_license_pool=True) pool.identifier.identifier = 'econtentRecord1' patron = self._patron(external_identifier='12345678901234') loan = self.api.checkout(patron, '1234', pool, None)
def setup(self): super(BaseEnkiTest, self).setup() self.api = MockEnkiAPI(self._db) self.collection = self.api.collection
def test_update_circulation(self): # Here's information about a book we didn't know about before. circ_data = { "result": { "records": 1, "recentactivity": [{ "historyid": "3738", "id": "34278", "recordId": "econtentRecord34278", "time": "2018-06-26 10:08:23", "action": "Checked Out", "isbn": "9781618856050", "availability": { "accessType": "acs", "totalCopies": "1", "availableCopies": 0, "onHold": 0 } }] } } # Because the book is unknown, update_circulation will do a follow-up # call to api.get_item to get bibliographic information. bib_data = { "result": { "id": "34278", "recordId": "econtentRecord34278", "isbn": "9781618856050", "title": "A book", "availability": { "accessType": "acs", "totalCopies": "1", "availableCopies": 0, "onHold": 0 } } } api = MockEnkiAPI(self._db) api.queue_response(200, content=json.dumps(circ_data)) api.queue_response(200, content=json.dumps(bib_data)) from core.mock_analytics_provider import MockAnalyticsProvider analytics = MockAnalyticsProvider() monitor = EnkiImport(self._db, self.collection, api_class=api, analytics=analytics) since = datetime.datetime.utcnow() monitor.update_circulation(since) # Two requests were made -- one to getRecentActivity # and one to getItem. [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('get', method) eq_('https://enkilibrary.org/API/ItemAPI', url) eq_('getRecentActivity', params['method']) eq_(0, params['minutes']) [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('get', method) eq_('https://enkilibrary.org/API/ItemAPI', url) eq_('getItem', params['method']) eq_('34278', params['recordid']) # We ended up with one Work, one LicensePool, and one Edition. work = self._db.query(Work).one() licensepool = self._db.query(LicensePool).one() edition = self._db.query(Edition).one() eq_([licensepool], work.license_pools) eq_(edition, licensepool.presentation_edition) identifier = licensepool.identifier eq_(Identifier.ENKI_ID, identifier.type) eq_(u"34278", identifier.identifier) # The LicensePool and Edition take their data from the mock API # requests. eq_("A book", work.title) eq_(1, licensepool.licenses_owned) eq_(0, licensepool.licenses_available) # An analytics event was sent out for the newly discovered book. eq_(1, analytics.count) # Now let's see what update_circulation does when the work # already exists. circ_data['result']['recentactivity'][0]['availability'][ 'totalCopies'] = 10 api.queue_response(200, content=json.dumps(circ_data)) # We're not queuing up more bib data, but that's no problem -- # EnkiImport won't ask for it. # Pump the monitor again. monitor.update_circulation(since) # We made a single request, to getRecentActivity. [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('getRecentActivity', params['method']) # The LicensePool was updated, but no new objects were created. eq_(10, licensepool.licenses_owned) for c in (LicensePool, Edition, Work): eq_(1, self._db.query(c).count())
def test__update_circulation(self): # Here's information about a book we didn't know about before. circ_data = { "result": { "records": 1, "recentactivity": [{ "historyid": "3738", "id": "34278", "recordId": "econtentRecord34278", "time": "2018-06-26 10:08:23", "action": "Checked Out", "isbn": "9781618856050", "availability": { "accessType": "acs", "totalCopies": "1", "availableCopies": 0, "onHold": 0, }, }], } } # Because the book is unknown, update_circulation will do a follow-up # call to api.get_item to get bibliographic information. bib_data = { "result": { "id": "34278", "recordId": "econtentRecord34278", "isbn": "9781618856050", "title": "A book", "availability": { "accessType": "acs", "totalCopies": "1", "availableCopies": 0, "onHold": 0, }, } } api = MockEnkiAPI(self._db) api.queue_response(200, content=json.dumps(circ_data)) api.queue_response(200, content=json.dumps(bib_data)) from core.mock_analytics_provider import MockAnalyticsProvider analytics = MockAnalyticsProvider() monitor = EnkiImport(self._db, self.collection, api_class=api, analytics=analytics) end = utc_now() # Ask for circulation events from one hour in 1970. start = datetime_utc(1970, 1, 1, 0, 0, 0) end = datetime_utc(1970, 1, 1, 1, 0, 0) monitor._update_circulation(start, end) # Two requests were made -- one to getRecentActivityTime # and one to getItem. [method, url, headers, data, params, kwargs] = api.requests.pop(0) assert "get" == method assert "https://enkilibrary.org/API/ItemAPI" == url assert "getRecentActivityTime" == params["method"] # The parameters passed to getRecentActivityTime show the # start and end points of the request as seconds since the # epoch. assert "0" == params["stime"] assert "3600" == params["etime"] [method, url, headers, data, params, kwargs] = api.requests.pop(0) assert "get" == method assert "https://enkilibrary.org/API/ItemAPI" == url assert "getItem" == params["method"] assert "34278" == params["recordid"] # We ended up with one Work, one LicensePool, and one Edition. work = self._db.query(Work).one() licensepool = self._db.query(LicensePool).one() edition = self._db.query(Edition).one() assert [licensepool] == work.license_pools assert edition == licensepool.presentation_edition identifier = licensepool.identifier assert Identifier.ENKI_ID == identifier.type assert "34278" == identifier.identifier # The LicensePool and Edition take their data from the mock API # requests. assert "A book" == work.title assert 1 == licensepool.licenses_owned assert 0 == licensepool.licenses_available # An analytics event was sent out for the newly discovered book. assert 1 == analytics.count # Now let's see what update_circulation does when the work # already exists. circ_data["result"]["recentactivity"][0]["availability"][ "totalCopies"] = 10 api.queue_response(200, content=json.dumps(circ_data)) # We're not queuing up more bib data, but that's no problem -- # EnkiImport won't ask for it. # Pump the monitor again. monitor._update_circulation(start, end) # We made a single request, to getRecentActivityTime. [method, url, headers, data, params, kwargs] = api.requests.pop(0) assert "getRecentActivityTime" == params["method"] # The LicensePool was updated, but no new objects were created. assert 10 == licensepool.licenses_owned for c in (LicensePool, Edition, Work): assert 1 == self._db.query(c).count()
def test_update_circulation(self): # Here's information about a book we didn't know about before. circ_data = {"result":{"records":1,"recentactivity":[{"historyid":"3738","id":"34278","recordId":"econtentRecord34278","time":"2018-06-26 10:08:23","action":"Checked Out","isbn":"9781618856050","availability":{"accessType":"acs","totalCopies":"1","availableCopies":0,"onHold":0}}]}} # Because the book is unknown, update_circulation will do a follow-up # call to api.get_item to get bibliographic information. bib_data = {"result":{"id":"34278","recordId":"econtentRecord34278","isbn":"9781618856050","title":"A book","availability":{"accessType":"acs","totalCopies":"1","availableCopies":0,"onHold":0}}} api = MockEnkiAPI(self._db) api.queue_response(200, content=json.dumps(circ_data)) api.queue_response(200, content=json.dumps(bib_data)) from core.mock_analytics_provider import MockAnalyticsProvider analytics = MockAnalyticsProvider() monitor = EnkiImport(self._db, self.collection, api_class=api, analytics=analytics) since = datetime.datetime.utcnow() monitor.update_circulation(since) # Two requests were made -- one to getRecentActivity # and one to getItem. [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('get', method) eq_('https://enkilibrary.org/API/ItemAPI', url) eq_('getRecentActivity', params['method']) eq_(0, params['minutes']) [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('get', method) eq_('https://enkilibrary.org/API/ItemAPI', url) eq_('getItem', params['method']) eq_('34278', params['recordid']) # We ended up with one Work, one LicensePool, and one Edition. work = self._db.query(Work).one() licensepool = self._db.query(LicensePool).one() edition = self._db.query(Edition).one() eq_([licensepool], work.license_pools) eq_(edition, licensepool.presentation_edition) identifier = licensepool.identifier eq_(Identifier.ENKI_ID, identifier.type) eq_(u"34278", identifier.identifier) # The LicensePool and Edition take their data from the mock API # requests. eq_("A book", work.title) eq_(1, licensepool.licenses_owned) eq_(0, licensepool.licenses_available) # An analytics event was sent out for the newly discovered book. eq_(1, analytics.count) # Now let's see what update_circulation does when the work # already exists. circ_data['result']['recentactivity'][0]['availability']['totalCopies'] = 10 api.queue_response(200, content=json.dumps(circ_data)) # We're not queuing up more bib data, but that's no problem -- # EnkiImport won't ask for it. # Pump the monitor again. monitor.update_circulation(since) # We made a single request, to getRecentActivity. [method, url, headers, data, params, kwargs] = api.requests.pop(0) eq_('getRecentActivity', params['method']) # The LicensePool was updated, but no new objects were created. eq_(10, licensepool.licenses_owned) for c in (LicensePool, Edition, Work): eq_(1, self._db.query(c).count())