コード例 #1
0
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))
コード例 #2
0
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))