class TestCirculationManagerAnnotator(VendorIDTest): def setup(self): super(TestCirculationManagerAnnotator, self).setup() self.work = self._work(with_open_access_download=True) lane = self._lane(display_name="Fantasy") self.annotator = CirculationManagerAnnotator( None, lane, self._default_library, test_mode=True, top_level_title="Test Top Level Title") def test_add_configuration_links(self): mock_feed = [] link_config = { CirculationManagerAnnotator.TERMS_OF_SERVICE: "http://terms/", CirculationManagerAnnotator.PRIVACY_POLICY: "http://privacy/", CirculationManagerAnnotator.COPYRIGHT: "http://copyright/", CirculationManagerAnnotator.ABOUT: "http://about/", CirculationManagerAnnotator.LICENSE: "http://license/", Configuration.HELP_EMAIL: "help@me", Configuration.HELP_WEB: "http://help/", Configuration.HELP_URI: "uri:help", } # Set up configuration settings for links. for rel, value in link_config.iteritems(): ConfigurationSetting.for_library( rel, self._default_library).value = value self.annotator.add_configuration_links(mock_feed) # Eight links were added to the "feed" eq_(8, len(mock_feed)) # They are the links we'd expect. links = {} for link in mock_feed: rel = link.attrib['rel'] href = link.attrib['href'] if rel == 'help': continue # Tested below # Check that the configuration value made it into the link. eq_(href, link_config[rel]) eq_("text/html", link.attrib['type']) # There are three help links using different protocols. help_links = [ x.attrib['href'] for x in mock_feed if x.attrib['rel'] == 'help' ] eq_(set(["mailto:help@me", "http://help/", "uri:help"]), set(help_links)) def test_open_access_link(self): # The resource URL associated with a LicensePoolDeliveryMechanism # becomes the `href` of an open-access `link` tag. [lpdm] = self.work.license_pools[0].delivery_mechanisms lpdm.resource.url = "http://foo.com/thefile.epub" link_tag = self.annotator.open_access_link(lpdm) eq_(lpdm.resource.url, link_tag.get('href')) # If we have a CDN set up for open-access links, the CDN hostname # replaces the original hostname. with temp_config() as config: config[Configuration.INTEGRATIONS][ExternalIntegration.CDN] = { 'foo.com': 'https://cdn.com/' } link_tag = self.annotator.open_access_link(lpdm) link_url = link_tag.get('href') eq_("https://cdn.com/thefile.epub", link_url) def test_top_level_title(self): eq_("Test Top Level Title", self.annotator.top_level_title()) def test_group_uri_with_flattened_lane(self): spanish_lane = self._lane(display_name="Spanish", languages=["spa"]) flat_spanish_lane = dict({ "lane": spanish_lane, "label": "All Spanish", "link_to_list_feed": True }) spanish_work = self._work(title="Spanish Book", with_license_pool=True, language="spa") lp = spanish_work.license_pools[0] self.annotator.lanes_by_work[spanish_work].append(flat_spanish_lane) feed_url = self.annotator.feed_url(spanish_lane) group_uri = self.annotator.group_uri(spanish_work, lp, lp.identifier) eq_((feed_url, "All Spanish"), group_uri) def test_lane_url(self): fantasy_lane_with_sublanes = self._lane( display_name="Fantasy with sublanes", languages=["eng"]) fantasy_lane_with_sublanes.add_genre(Fantasy.name) urban_fantasy_lane = self._lane(display_name="Urban Fantasy") urban_fantasy_lane.add_genre(Urban_Fantasy.name) fantasy_lane_with_sublanes.sublanes.append(urban_fantasy_lane) fantasy_lane_without_sublanes = self._lane( display_name="Fantasy without sublanes", languages=["eng"]) fantasy_lane_without_sublanes.add_genre(Fantasy.name) default_lane_url = self.annotator.lane_url(None) eq_(default_lane_url, self.annotator.default_lane_url()) groups_url = self.annotator.lane_url(fantasy_lane_with_sublanes) eq_(groups_url, self.annotator.groups_url(fantasy_lane_with_sublanes)) feed_url = self.annotator.lane_url(fantasy_lane_without_sublanes) eq_(feed_url, self.annotator.feed_url(fantasy_lane_without_sublanes)) def test_fulfill_link_includes_device_registration_tags(self): """Verify that when Adobe Vendor ID delegation is included, the fulfill link for an Adobe delivery mechanism includes instructions on how to get a Vendor ID. """ self.initialize_adobe(self._default_library) [pool] = self.work.license_pools identifier = pool.identifier patron = self._patron() old_credentials = list(patron.credentials) loan, ignore = pool.loan_to(patron, start=datetime.datetime.utcnow()) adobe_delivery_mechanism, ignore = DeliveryMechanism.lookup( self._db, "text/html", DeliveryMechanism.ADOBE_DRM) other_delivery_mechanism, ignore = DeliveryMechanism.lookup( self._db, "text/html", DeliveryMechanism.OVERDRIVE_DRM) # The fulfill link for non-Adobe DRM does not # include the drm:licensor tag. link = self.annotator.fulfill_link(pool, loan, other_delivery_mechanism) for child in link.getchildren(): assert child.tag != "{http://librarysimplified.org/terms/drm}licensor" # No new Credential has been associated with the patron. eq_(old_credentials, patron.credentials) # The fulfill link for Adobe DRM includes information # on how to get an Adobe ID in the drm:licensor tag. link = self.annotator.fulfill_link(pool, loan, adobe_delivery_mechanism) licensor = link.getchildren()[-1] eq_("{http://librarysimplified.org/terms/drm}licensor", licensor.tag) # An Adobe ID-specific identifier has been created for the patron. [adobe_id_identifier ] = [x for x in patron.credentials if x not in old_credentials] eq_(AuthdataUtility.ADOBE_ACCOUNT_ID_PATRON_IDENTIFIER, adobe_id_identifier.type) eq_(DataSource.INTERNAL_PROCESSING, adobe_id_identifier.data_source.name) eq_(None, adobe_id_identifier.expires) # The drm:licensor tag is the one we get by calling # adobe_id_tags() on that identifier. [expect] = self.annotator.adobe_id_tags(adobe_id_identifier.credential) eq_(etree.tostring(expect), etree.tostring(licensor)) def test_no_adobe_id_tags_when_vendor_id_not_configured(self): """When vendor ID delegation is not configured, adobe_id_tags() returns an empty list. """ eq_([], self.annotator.adobe_id_tags("patron identifier")) def test_adobe_id_tags_when_vendor_id_configured(self): """When vendor ID delegation is configured, adobe_id_tags() returns a list containing a single tag. The tag contains the information necessary to get an Adobe ID and a link to the local DRM Device Management Protocol endpoint. """ self.initialize_adobe(self._default_library) patron_identifier = "patron identifier" [element] = self.annotator.adobe_id_tags(patron_identifier) eq_('{http://librarysimplified.org/terms/drm}licensor', element.tag) key = '{http://librarysimplified.org/terms/drm}vendor' eq_(self.adobe_vendor_id.username, element.attrib[key]) [token, device_management_link] = element.getchildren() eq_('{http://librarysimplified.org/terms/drm}clientToken', token.tag) # token.text is a token which we can decode, since we know # the secret. token = token.text authdata = AuthdataUtility.from_config(self._default_library) decoded = authdata.decode_short_client_token(token) expected_url = ConfigurationSetting.for_library( Configuration.WEBSITE_URL, self._default_library).value eq_((expected_url, patron_identifier), decoded) eq_("link", device_management_link.tag) eq_("http://librarysimplified.org/terms/drm/rel/devices", device_management_link.attrib['rel']) expect_url = self.annotator.url_for( 'adobe_drm_devices', library_short_name=self._default_library.short_name, _external=True) eq_(expect_url, device_management_link.attrib['href']) # If we call adobe_id_tags again we'll get a distinct tag # object that renders to the same XML. [same_tag] = self.annotator.adobe_id_tags(patron_identifier) assert same_tag is not element eq_(etree.tostring(element), etree.tostring(same_tag))
class TestCirculationManagerAnnotator(DatabaseTest): def setup(self): super(TestCirculationManagerAnnotator, self).setup() self.work = self._work(with_open_access_download=True) self.annotator = CirculationManagerAnnotator( None, Fantasy, test_mode=True, top_level_title="Test Top Level Title" ) def test_open_access_link(self): # The resource URL associated with a LicensePoolDeliveryMechanism # becomes the `href` of an open-access `link` tag. [lpdm] = self.work.license_pools[0].delivery_mechanisms link_tag = self.annotator.open_access_link(lpdm) eq_(lpdm.resource.url, link_tag.get('href')) # If we have a CDN set up for open-access links, the CDN hostname # replaces the original hostname. with temp_config() as config: cdn_host = "https://cdn.com/" config[Configuration.INTEGRATIONS] = { Configuration.CDN_INTEGRATION : { Configuration.CDN_OPEN_ACCESS_CONTENT : cdn_host } } link_tag = self.annotator.open_access_link(lpdm) link_url = link_tag.get('href') assert link_url.startswith(cdn_host) assert link_url == cdnify(lpdm.resource.url, cdn_host) def test_top_level_title(self): eq_("Test Top Level Title", self.annotator.top_level_title()) def test_group_uri_with_flattened_lane(self): spanish_lane = Lane( self._db, "Spanish", languages="spa" ) flat_spanish_lane = dict({ "lane": spanish_lane, "label": "All Spanish", "link_to_list_feed": True }) spanish_work = self._work( title="Spanish Book", with_license_pool=True, language="spa" ) lp = spanish_work.license_pools[0] self.annotator.lanes_by_work[spanish_work].append(flat_spanish_lane) feed_url = self.annotator.feed_url(spanish_lane) group_uri = self.annotator.group_uri(spanish_work, lp, lp.identifier) eq_((feed_url, "All Spanish"), group_uri) def test_lane_url(self): everything_lane = Lane( self._db, "Everything", fiction=Lane.BOTH_FICTION_AND_NONFICTION) fantasy_lane_with_sublanes = Lane( self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE, sublanes=[Urban_Fantasy], parent=everything_lane) fantasy_lane_without_sublanes = Lane( self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE, parent=everything_lane) default_lane_url = self.annotator.lane_url(everything_lane) eq_(default_lane_url, self.annotator.default_lane_url()) groups_url = self.annotator.lane_url(fantasy_lane_with_sublanes) eq_(groups_url, self.annotator.groups_url(fantasy_lane_with_sublanes)) feed_url = self.annotator.lane_url(fantasy_lane_without_sublanes) eq_(feed_url, self.annotator.feed_url(fantasy_lane_without_sublanes)) def test_single_entry_no_active_license_pool(self): work = self._work(with_open_access_download=True) pool = work.license_pools[0] # Create an <entry> tag for this work and its LicensePool. feed1 = AcquisitionFeed.single_entry( self._db, work, self.annotator, pool ) # If we don't pass in the license pool, it makes a guess to # figure out which license pool we're talking about. feed2 = AcquisitionFeed.single_entry( self._db, work, self.annotator, None ) # Both entries are identical. eq_(etree.tostring(feed1), etree.tostring(feed2))
class TestCirculationManagerAnnotator(DatabaseTest): def setup(self): super(TestCirculationManagerAnnotator, self).setup() self.work = self._work(with_open_access_download=True) self.annotator = CirculationManagerAnnotator( None, Fantasy, test_mode=True, top_level_title="Test Top Level Title" ) def test_open_access_link(self): # The resource URL associated with a LicensePoolDeliveryMechanism # becomes the `href` of an open-access `link` tag. [lpdm] = self.work.license_pools[0].delivery_mechanisms link_tag = self.annotator.open_access_link(lpdm) eq_(lpdm.resource.url, link_tag.get('href')) # If we have a CDN set up for open-access links, the CDN hostname # replaces the original hostname. with temp_config() as config: cdn_host = "https://cdn.com/" config[Configuration.INTEGRATIONS] = { Configuration.CDN_INTEGRATION : { Configuration.CDN_OPEN_ACCESS_CONTENT : cdn_host } } link_tag = self.annotator.open_access_link(lpdm) link_url = link_tag.get('href') assert link_url.startswith(cdn_host) assert link_url == cdnify(lpdm.resource.url, cdn_host) def test_top_level_title(self): eq_("Test Top Level Title", self.annotator.top_level_title()) def test_group_uri_with_flattened_lane(self): spanish_lane = Lane( self._db, "Spanish", languages="spa" ) flat_spanish_lane = dict({ "lane": spanish_lane, "label": "All Spanish", "link_to_list_feed": True }) spanish_work = self._work( title="Spanish Book", with_license_pool=True, language="spa" ) lp = spanish_work.license_pools[0] self.annotator.lanes_by_work[spanish_work].append(flat_spanish_lane) feed_url = self.annotator.feed_url(spanish_lane) group_uri = self.annotator.group_uri(spanish_work, lp, lp.identifier) eq_((feed_url, "All Spanish"), group_uri) def test_lane_url(self): fantasy_lane_with_sublanes = Lane( self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE, sublanes=[Urban_Fantasy]) fantasy_lane_without_sublanes = Lane( self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE) groups_url = self.annotator.lane_url(fantasy_lane_with_sublanes) eq_(groups_url, self.annotator.groups_url(fantasy_lane_with_sublanes)) feed_url = self.annotator.lane_url(fantasy_lane_without_sublanes) eq_(feed_url, self.annotator.feed_url(fantasy_lane_without_sublanes))
class TestCirculationManagerAnnotator(WithVendorIDTest): def setup(self): super(TestCirculationManagerAnnotator, self).setup() self.work = self._work(with_open_access_download=True) self.annotator = CirculationManagerAnnotator( None, Fantasy, test_mode=True, top_level_title="Test Top Level Title") def test_add_configuration_links(self): mock_feed = [] link_config = { Configuration.TERMS_OF_SERVICE: "http://terms/", Configuration.PRIVACY_POLICY: "http://privacy/", Configuration.COPYRIGHT: "http://copyright/", Configuration.ABOUT: "http://about/", Configuration.LICENSE: "http://license/", } with temp_config() as config: config['links'] = link_config CirculationManagerAnnotator.add_configuration_links(mock_feed) # Five links were added to the "feed" eq_(5, len(mock_feed)) # They are the links we'd expect. links = {} for link in mock_feed: rel = link.attrib['rel'] href = link.attrib['href'] type = link.attrib['type'] eq_("text/html", type) # Convert the link relation into a key to the configuration. config_value = rel.replace('-', '_') # Check that the configuration value made it into the link. eq_(href, link_config[config_value]) def test_open_access_link(self): # The resource URL associated with a LicensePoolDeliveryMechanism # becomes the `href` of an open-access `link` tag. [lpdm] = self.work.license_pools[0].delivery_mechanisms lpdm.resource.url = "http://foo.com/thefile.epub" link_tag = self.annotator.open_access_link(lpdm) eq_(lpdm.resource.url, link_tag.get('href')) # If we have a CDN set up for open-access links, the CDN hostname # replaces the original hostname. with temp_config() as config: cdn_host = "https://cdn.com/" cdns = {"foo.com": cdn_host} config[Configuration.INTEGRATIONS] = { Configuration.CDN_INTEGRATION: cdns } link_tag = self.annotator.open_access_link(lpdm) link_url = link_tag.get('href') eq_("https://cdn.com/thefile.epub", link_url) def test_top_level_title(self): eq_("Test Top Level Title", self.annotator.top_level_title()) def test_group_uri_with_flattened_lane(self): spanish_lane = Lane(self._db, "Spanish", languages="spa") flat_spanish_lane = dict({ "lane": spanish_lane, "label": "All Spanish", "link_to_list_feed": True }) spanish_work = self._work(title="Spanish Book", with_license_pool=True, language="spa") lp = spanish_work.license_pools[0] self.annotator.lanes_by_work[spanish_work].append(flat_spanish_lane) feed_url = self.annotator.feed_url(spanish_lane) group_uri = self.annotator.group_uri(spanish_work, lp, lp.identifier) eq_((feed_url, "All Spanish"), group_uri) def test_lane_url(self): everything_lane = Lane(self._db, "Everything", fiction=Lane.BOTH_FICTION_AND_NONFICTION) fantasy_lane_with_sublanes = Lane(self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE, sublanes=[Urban_Fantasy], parent=everything_lane) fantasy_lane_without_sublanes = Lane( self._db, "Fantasy", genres=[Fantasy], languages="eng", subgenre_behavior=Lane.IN_SAME_LANE, parent=everything_lane) default_lane_url = self.annotator.lane_url(everything_lane) eq_(default_lane_url, self.annotator.default_lane_url()) groups_url = self.annotator.lane_url(fantasy_lane_with_sublanes) eq_(groups_url, self.annotator.groups_url(fantasy_lane_with_sublanes)) feed_url = self.annotator.lane_url(fantasy_lane_without_sublanes) eq_(feed_url, self.annotator.feed_url(fantasy_lane_without_sublanes)) def test_single_entry_no_active_license_pool(self): work = self._work(with_open_access_download=True) pool = work.license_pools[0] # Create an <entry> tag for this work and its LicensePool. feed1 = AcquisitionFeed.single_entry(self._db, work, self.annotator, pool) # If we don't pass in the license pool, it makes a guess to # figure out which license pool we're talking about. feed2 = AcquisitionFeed.single_entry(self._db, work, self.annotator, None) # Both entries are identical. eq_(etree.tostring(feed1), etree.tostring(feed2)) def test_fulfill_link_includes_device_registration_tags(self): """Verify that when Adobe Vendor ID delegation is included, the fulfill link for an Adobe delivery mechanism includes instructions on how to get a Vendor ID. """ [pool] = self.work.license_pools identifier = pool.identifier patron = self._patron() old_credentials = list(patron.credentials) loan, ignore = pool.loan_to(patron, start=datetime.datetime.utcnow()) adobe_delivery_mechanism, ignore = DeliveryMechanism.lookup( self._db, "text/html", DeliveryMechanism.ADOBE_DRM) other_delivery_mechanism, ignore = DeliveryMechanism.lookup( self._db, "text/html", DeliveryMechanism.OVERDRIVE_DRM) with self.temp_config() as config: # The fulfill link for non-Adobe DRM does not # include the drm:licensor tag. link = self.annotator.fulfill_link(pool.data_source.name, pool.identifier, pool, loan, other_delivery_mechanism) for child in link.getchildren(): assert child.tag != "{http://librarysimplified.org/terms/drm}licensor" # No new Credential has been associated with the patron. eq_(old_credentials, patron.credentials) # The fulfill link for Adobe DRM includes information # on how to get an Adobe ID in the drm:licensor tag. link = self.annotator.fulfill_link(pool.data_source.name, pool.identifier, pool, loan, adobe_delivery_mechanism) licensor = link.getchildren()[-1] eq_("{http://librarysimplified.org/terms/drm}licensor", licensor.tag) # An Adobe ID-specific identifier has been created for the patron. [adobe_id_identifier ] = [x for x in patron.credentials if x not in old_credentials] eq_(AuthdataUtility.ADOBE_ACCOUNT_ID_PATRON_IDENTIFIER, adobe_id_identifier.type) eq_(DataSource.INTERNAL_PROCESSING, adobe_id_identifier.data_source.name) eq_(None, adobe_id_identifier.expires) # The drm:licensor tag is the one we get by calling # adobe_id_tags() on that identifier. [expect ] = self.annotator.adobe_id_tags(self._db, adobe_id_identifier.credential) eq_(etree.tostring(expect), etree.tostring(licensor)) def test_no_adobe_id_tags_when_vendor_id_not_configured(self): with temp_config() as config: """When vendor ID delegation is not configured, adobe_id_tags() returns an empty list. """ config[Configuration.INTEGRATIONS][ Configuration.ADOBE_VENDOR_ID_INTEGRATION] = {} eq_([], self.annotator.adobe_id_tags(self._db, "patron identifier")) def test_adobe_id_tags_when_vendor_id_configured(self): """When vendor ID delegation is configured, adobe_id_tags() returns a list containing a single tag. The tag contains the information necessary to get an Adobe ID and a link to the local DRM Device Management Protocol endpoint. """ with self.temp_config() as config: patron_identifier = "patron identifier" [element] = self.annotator.adobe_id_tags(self._db, patron_identifier) eq_('{http://librarysimplified.org/terms/drm}licensor', element.tag) key = '{http://librarysimplified.org/terms/drm}vendor' eq_("Some Vendor", element.attrib[key]) [token, device_management_link] = element.getchildren() eq_('{http://librarysimplified.org/terms/drm}clientToken', token.tag) # token.text is a token which we can decode, since we know # the secret. token = token.text authdata = AuthdataUtility.from_config(self._db) decoded = authdata.decode_short_client_token(token) eq_(("http://a-library/", patron_identifier), decoded) eq_("link", device_management_link.tag) eq_("http://librarysimplified.org/terms/drm/rel/devices", device_management_link.attrib['rel']) expect_url = self.annotator.url_for('adobe_drm_devices', _external=True) eq_(expect_url, device_management_link.attrib['href']) # If we call adobe_id_tags again we'll get a distinct tag # object that renders to the same XML. [same_tag] = self.annotator.adobe_id_tags(self._db, patron_identifier) assert same_tag is not element eq_(etree.tostring(element), etree.tostring(same_tag))