Beispiel #1
0
def fetch_text_structure(urn, endpoint="http://cts.perseids.org/api/cts"):
    """
    Fetches the text structure of a given work from a CTS endpoint.

    :param urn: the work's CTS URN (at the work-level!,
        e.g."urn:cts:greekLit:tlg0012.tlg001")
    :type urn: string
    :param endpoint: the URL of the CTS endpoint to use (defaults to Perseids')
    :type endpoint: string
    :return: a dict with keys "urn", "provenance", "valid_reffs", "levels"
    :rtype: dict
    """

    structure = {"urn": urn, "provenance": endpoint, "valid_reffs": {}}

    orig_edition = None
    suffix = 'grc' if 'greekLit' in urn else 'lat'
    resolver = HttpCtsResolver(HttpCtsRetriever(endpoint))
    work_metadata = resolver.getMetadata(urn)

    # among all editions for this work, pick the one in Greek or Latin
    try:
        orig_edition = next(
            iter(work_metadata.children[edition]
                 for edition in work_metadata.children
                 if suffix in str(work_metadata.children[edition].urn)))
    except Exception as e:
        print(e)
        return None

    assert orig_edition is not None

    # get information about the work's citation scheme
    structure["levels"] = [(n + 1, level.name.lower())
                           for n, level in enumerate(orig_edition.citation)]

    # for each hierarchical level of the text
    # fetch all citable text elements
    for level_n, level_label in structure["levels"]:

        structure["valid_reffs"][level_n] = []
        for ref in resolver.getReffs(urn, level=level_n):
            print(ref)
            element = {
                "current": "{}:{}".format(urn, ref),
            }
            if "." in ref:
                element["parent"] = "{}:{}".format(
                    urn, ".".join(ref.split(".")[:level_n - 1]))
            textual_node = resolver.getTextualNode(textId=urn,
                                                   subreference=ref,
                                                   prevnext=True)
            if textual_node.nextId is not None:
                element["previous"] = "{}:{}".format(urn, textual_node.nextId)
            if textual_node.prevId is not None:
                element["following"] = "{}:{}".format(urn, textual_node.prevId)
            structure["valid_reffs"][level_n].append(element)

    return structure
Beispiel #2
0
from MyCapytain.resolvers.cts.api import HttpCtsResolver
from MyCapytain.retrievers.cts5 import HttpCtsRetriever
from MyCapytain.common.constants import Mimetypes

# We set up a resolver which communicates with an API available in Leipzig
resolver = HttpCtsResolver(
    HttpCtsRetriever("http://cts.dh.uni-leipzig.de/api/cts"))
# We require some metadata information
textMetadata = resolver.getMetadata(
    "urn:cts:latinLit:phi1294.phi002.perseus-lat2")
# Texts in CTS Metadata have one interesting property : its citation scheme.
# XmlCtsCitation are embedded objects that carries information about how a text can be quoted, what depth it has
print(type(textMetadata),
      [citation.name for citation in textMetadata.citation])
# Now, we want to retrieve the first line of poem seventy two of the second book
passage = resolver.getTextualNode(
    "urn:cts:latinLit:phi1294.phi002.perseus-lat2", subreference="2.72.1")
# And we want to have its content exported to plain text and have the siblings of this passage (previous and next line)
print(passage.export(Mimetypes.PLAINTEXT), passage.siblingsId)
poemsInBook3 = resolver.getReffs(
    "urn:cts:latinLit:phi1294.phi002.perseus-lat2", subreference="3")
print(poemsInBook3)
Beispiel #3
0
class TestHttpCtsResolver(TestCase):
    def setUp(self):
        self.resolver = HttpCtsResolver(HttpCtsRetriever("http://localhost"))
        self.resolver.endpoint.getPassagePlus = MagicMock(
            return_value=GET_PASSAGE_PLUS)
        self.resolver.endpoint.getPassage = MagicMock(return_value=GET_PASSAGE)
        self.resolver.endpoint.getPrevNextUrn = MagicMock(
            return_value=NEXT_PREV)
        self.resolver.endpoint.getValidReff = MagicMock(
            return_value=GET_VALID_REFF)
        self.resolver.endpoint.getCapabilities = MagicMock(
            return_value=GET_CAPABILITIES)

    def test_getPassage_full(self):
        """ Test that we can get a full text """
        passage = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2")

        # We check we made a reroute to GetPassage request
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2")
        self.assertIsInstance(
            passage, Passage,
            "GetPassage should always return passages objects")

        children = list(passage.getReffs())

        # We check the passage is able to perform further requests and is well instantiated
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=1)
        self.assertEqual(children[0], '1.1.1',
                         "Resource should be string identifiers")

        self.assertIn("Hic est quem legis ille, quem requiris,",
                      passage.export(output=Mimetypes.PLAINTEXT),
                      "Export CtsTextMetadata should work correctly")

        self.assertEqual(
            passage.export(output=Mimetypes.PYTHON.ETREE).xpath(
                ".//tei:l[@n='1']/text()",
                namespaces=XPATH_NAMESPACES,
                magic_string=False),
            ["Hic est quem legis ille, quem requiris, "],
            "Export to Etree should give an Etree or Etree like object")

    def test_getPassage_subreference(self):
        """ Test that we can get a subreference text passage"""
        passage = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.1")

        # We check we made a reroute to GetPassage request
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1")
        self.assertIsInstance(
            passage, Passage,
            "GetPassage should always return passages objects")

        children = list(passage.getReffs())

        # We check the passage is able to perform further requests and is well instantiated
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1", level=3)
        self.assertEqual(children[0], '1.1.1',
                         "Resource should be string identifiers")

        self.assertIn("Hic est quem legis ille, quem requiris,",
                      passage.export(output=Mimetypes.PLAINTEXT),
                      "Export CtsTextMetadata should work correctly")

        self.assertEqual(
            passage.export(output=Mimetypes.PYTHON.ETREE).xpath(
                ".//tei:l[@n='1']/text()",
                namespaces=XPATH_NAMESPACES,
                magic_string=False),
            ["Hic est quem legis ille, quem requiris, "],
            "Export to Etree should give an Etree or Etree like object")

    def test_getPassage_full_metadata(self):
        """ Test that we can get a full text with its metadata"""
        passage = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", metadata=True)

        # We check we made a reroute to GetPassage request
        self.resolver.endpoint.getPassagePlus.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2")
        self.assertIsInstance(
            passage, Passage,
            "GetPassage should always return passages objects")
        self.assertEqual(
            str(passage.get_title("eng")), "Epigrammata",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            str(passage.get_creator("eng")), "Martial",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            str(passage.get_subject("eng")),
            "M. Valerii Martialis Epigrammaton libri / recognovit W. Heraeus",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            str(passage.get_cts_metadata("label", "eng")),
            "M. Valerii Martialis Epigrammaton libri / recognovit W. Heraeus",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            passage.citation.name, "book",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            len(passage.citation), 3,
            "CTS API Remote HTTP Response should be correctly parsed")

        children = list(passage.getReffs())

        # We check the passage is able to perform further requests and is well instantiated
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=1)
        self.assertEqual(children[0], '1.1.1',
                         "Resource should be string identifiers")

        self.assertIn("Hic est quem legis ille, quem requiris,",
                      passage.export(output=Mimetypes.PLAINTEXT),
                      "Export CtsTextMetadata should work correctly")

        self.assertEqual(
            passage.export(output=Mimetypes.PYTHON.ETREE).xpath(
                ".//tei:l[@n='1']/text()",
                namespaces=XPATH_NAMESPACES,
                magic_string=False),
            ["Hic est quem legis ille, quem requiris, "],
            "Export to Etree should give an Etree or Etree like object")

    def test_getPassage_prevnext(self):
        """ Test that we can get a full text with its metadata"""
        passage = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.1",
            metadata=True)

        # We check we made a reroute to GetPassage request
        self.resolver.endpoint.getPassagePlus.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1")
        self.assertIsInstance(
            passage, Passage,
            "GetPassage should always return passages objects")
        self.assertEqual(passage.prevId, "1.pr",
                         "Previous CapitainsCtsPassage ID should be parsed")
        self.assertEqual(passage.nextId, "1.2",
                         "Next CapitainsCtsPassage ID should be parsed")
        children = list(passage.getReffs())

        _ = passage.next
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.2")
        _ = passage.prev
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr")

        # We check the passage is able to perform further requests and is well instantiated
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1", level=3)
        self.assertEqual(children[0], '1.1.1',
                         "Resource should be string identifiers")

        self.assertIn("Hic est quem legis ille, quem requiris,",
                      passage.export(output=Mimetypes.PLAINTEXT),
                      "Export CtsTextMetadata should work correctly")

        self.assertEqual(
            passage.export(output=Mimetypes.PYTHON.ETREE).xpath(
                ".//tei:l[@n='1']/text()",
                namespaces=XPATH_NAMESPACES,
                magic_string=False),
            ["Hic est quem legis ille, quem requiris, "],
            "Export to Etree should give an Etree or Etree like object")

    def test_getPassage_metadata_prevnext(self):
        """ Test that we can get a full text with its metadata"""
        passage = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.1",
            metadata=True,
            prevnext=True)

        # We check we made a reroute to GetPassage request
        self.resolver.endpoint.getPassagePlus.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1")
        self.assertIsInstance(
            passage, Passage,
            "GetPassage should always return passages objects")
        self.assertEqual(
            str(passage.get_cts_metadata("groupname", "eng")), "Martial",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            str(passage.get_cts_metadata("title", "eng")), "Epigrammata",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            str(passage.get_cts_metadata("label", "eng")),
            "M. Valerii Martialis Epigrammaton libri / recognovit W. Heraeus",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            passage.citation.name, "book",
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(
            len(passage.citation), 3,
            "CTS API Remote HTTP Response should be correctly parsed")
        self.assertEqual(passage.prevId, "1.pr",
                         "Previous CapitainsCtsPassage ID should be parsed")
        self.assertEqual(passage.nextId, "1.2",
                         "Next CapitainsCtsPassage ID should be parsed")
        children = list(passage.getReffs())

        _ = passage.next
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.2")
        _ = passage.prev
        self.resolver.endpoint.getPassage.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr")

        # We check the passage is able to perform further requests and is well instantiated
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1", level=3)
        self.assertEqual(children[0], '1.1.1',
                         "Resource should be string identifiers")

        self.assertIn("Hic est quem legis ille, quem requiris,",
                      passage.export(output=Mimetypes.PLAINTEXT),
                      "Export CtsTextMetadata should work correctly")

        self.assertEqual(
            passage.export(output=Mimetypes.PYTHON.ETREE).xpath(
                ".//tei:l[@n='1']/text()",
                namespaces=XPATH_NAMESPACES,
                magic_string=False),
            ["Hic est quem legis ille, quem requiris, "],
            "Export to Etree should give an Etree or Etree like object")

    def test_getMetadata_full(self):
        """ Checks retrieval of Metadata information """
        metadata = self.resolver.getMetadata()
        self.resolver.endpoint.getCapabilities.assert_called_with()
        self.assertIsInstance(metadata, Collection,
                              "Resolver should return a collection object")
        self.assertIsInstance(metadata.members[0], XmlCtsTextgroupMetadata,
                              "Members of Inventory should be TextGroups")
        self.assertEqual(
            len(metadata.descendants), 33,
            "There should be as many descendants as there is edition, translation, commentaries, works and textgroup"
        )
        self.assertEqual(
            len(metadata.readableDescendants), 16,
            "There should be as many readable descendants as there is edition, translation, and commentaries"
        )
        self.assertEqual(
            len([
                x for x in metadata.readableDescendants
                if isinstance(x, XmlCtsTextMetadata)
            ]), 16,
            "There should be 14 editions + 1 translations + 1 commentary in readableDescendants"
        )
        self.assertEqual(
            len(
                metadata.export(output=Mimetypes.PYTHON.ETREE).xpath(
                    "//ti:edition[@urn='urn:cts:latinLit:phi1294.phi002.perseus-lat2']",
                    namespaces=XPATH_NAMESPACES)), 1,
            "There should be one node in exported format corresponding to lat2"
        )
        self.assertCountEqual([
            x["@id"] for x in metadata.export(
                output=Mimetypes.JSON.DTS.Std)["@graph"]["dts:members"]
        ], [
            "urn:cts:latinLit:phi1294", "urn:cts:latinLit:phi0959",
            "urn:cts:greekLit:tlg0003", "urn:cts:latinLit:phi1276"
        ], "There should be 4 Members in DTS JSON")

    def test_getMetadata_subset(self):
        """ Checks retrieval of Metadata information """
        self.resolver.endpoint.getCapabilities = MagicMock(
            return_value=GET_CAPABILITIES_FILTERED)
        metadata = self.resolver.getMetadata(
            objectId="urn:cts:latinLit:phi1294.phi002")
        self.resolver.endpoint.getCapabilities.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002")
        self.assertIsInstance(
            metadata, Collection,
            "Resolver should return a collection object (specifically here a CtsWorkMetadata)"
        )
        self.assertIsInstance(
            metadata.members[0], XmlCtsTextMetadata,
            "Members of CtsWorkMetadata should be TextGroups")
        self.assertEqual(
            len(metadata.descendants), 2,
            "There should be as many descendants as there is edition, translation"
        )
        self.assertEqual(
            len(metadata.readableDescendants), 2,
            "There should be 1 edition + 1 translation in readableDescendants")
        self.assertEqual(
            len([
                x for x in metadata.readableDescendants
                if isinstance(x, XmlCtsTextMetadata)
            ]), 2,
            "There should be 1 edition + 1 translation in readableDescendants")
        self.assertEqual(
            len(
                metadata.export(output=Mimetypes.PYTHON.ETREE).xpath(
                    "//ti:edition[@urn='urn:cts:latinLit:phi1294.phi002.perseus-lat2']",
                    namespaces=XPATH_NAMESPACES)), 1,
            "There should be one node in exported format corresponding to lat2"
        )
        self.assertCountEqual([
            x["@id"] for x in metadata.export(
                output=Mimetypes.JSON.DTS.Std)["@graph"]["dts:members"]
        ], [
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            "urn:cts:latinLit:phi1294.phi002.perseus-eng2"
        ], "There should be one member in DTS JSON")
        # Should fail until clear statement about this
        """
        self.assertCountEqual(
            [
                x["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]
                for x in metadata.export(output=Mimetypes.JSON.DTS.Std) \
                    ["http://w3id.org/dts-ontology/capabilities"]\
                    ["http://w3id.org/dts-ontology/navigation"]\
                    ["http://w3id.org/dts-ontology/parents"]
                ],
            ["http://chs.harvard.edu/xmlns/cts/CtsTextgroupMetadata", "http://chs.harvard.edu/xmlns/cts/CtsTextInventoryMetadata"],
            "There should be one member in DTS JSON"
        )
        """

    def test_getSiblings(self):
        """ Ensure getSiblings works well """
        previous, nextious = self.resolver.getSiblings(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.1")
        self.resolver.endpoint.getPrevNextUrn.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1")
        self.assertEqual(previous, "1.pr", "Previous should be well computed")
        self.assertEqual(nextious, "1.2", "Previous should be well computed")

    def test_getSiblings_nextOnly(self):
        """ Ensure getSiblings works well when there is only the next passage"""
        self.resolver.endpoint.getPrevNextUrn = MagicMock(return_value=NEXT)
        previous, nextious = self.resolver.getSiblings(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.pr")
        self.resolver.endpoint.getPrevNextUrn.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr")
        self.assertEqual(previous, None, "Previous Should not exist")
        self.assertEqual(nextious, "1.1", "Next should be well computed")

    def test_getSiblings_prevOnly(self):
        """ Ensure getSiblings works well when there is only the previous passage"""
        self.resolver.endpoint.getPrevNextUrn = MagicMock(return_value=PREV)
        previous, nextious = self.resolver.getSiblings(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.1.6")
        self.resolver.endpoint.getPrevNextUrn.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1.6")
        self.assertEqual(previous, "1.1.5", "Previous should be well computed")
        self.assertEqual(nextious, None, "Next should not exist")

    def test_getReffs_full(self):
        """ Ensure getReffs works well """
        self.resolver.endpoint.getValidReff = MagicMock(
            return_value=GET_VALID_REFF_FULL)
        reffs = self.resolver.getReffs(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=1)
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=1)
        self.assertEqual(len(reffs), 9462, "There should be 9462 references")
        self.assertEqual(reffs[0], "1.pr.1")
        self.resolver.endpoint.getValidReff = MagicMock(
            return_value=GET_VALID_REFF_FULL)
        self.resolver.getReffs(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=2)
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2", level=2)

        self.resolver.endpoint.getValidReff = MagicMock(
            return_value=GET_VALID_REFF)
        reffs = self.resolver.getReffs(
            textId="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1", level=1)
        self.resolver.endpoint.getValidReff.assert_called_with(
            urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1", level=3)
        self.assertEqual(len(reffs), 6, "There should be 6 references")
        self.assertEqual(reffs[0], "1.1.1")

    def test_citation_failure(self):
        """ Example for Resolver failed : some response have an issue with not available Citations ?
        """
        retriever = HttpCtsRetriever(
            "http://cts.dh.uni-leipzig.de/remote/cts/")
        retriever.getPassage = MagicMock(
            return_value=GET_PASSAGE_CITATION_FAILURE)
        resolver = HttpCtsResolver(retriever)
        # We require a passage : passage is now a CapitainsCtsPassage object
        passage = resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.1")
        # We need an export as plaintext
        self.assertEqual(
            passage.export(output=Mimetypes.PLAINTEXT),
            "I Hic est quem legis ille, quem requiris, Toto notus in orbe Martialis Argutis epigrammaton libellis: \n"\
                " Cui, lector studiose, quod dedisti Viventi decus atque sentienti, Rari post cineres habent poetae. ",
            "Parsing should be successful"
        )
Beispiel #4
0
class TestRestAPI(TestCase):
    def setUp(self):
        # Clean up noise...

        app = Flask("Nautilus")
        self.nautilus = FlaskNautilus(
            app=app,
            resolver=NautilusCTSResolver(["./tests/test_data/latinLit"]),
            logger=logger
        )
        self.cache = None
        self.app = app.test_client()
        self.parent = HttpCtsRetriever("/cts")
        self.resolver = HttpCtsResolver(endpoint=self.parent)
        logassert.setup(self, self.nautilus.logger.name)
        self.nautilus.logger.disabled = True

        def call(this, parameters={}):
            """ Call an endpoint given the parameters

            :param parameters: Dictionary of parameters
            :type parameters: dict
            :rtype: text
            """

            parameters = {
                key: str(parameters[key]) for key in parameters if parameters[key] is not None
            }
            if this.inventory is not None and "inv" not in parameters:
                parameters["inv"] = this.inventory

            request = self.app.get("/cts?{}".format(
                "&".join(
                    ["{}={}".format(key, value) for key, value in parameters.items()])
                )
            )
            self.parent.called.append(parameters)
            return request.data.decode()

        self.parent.called = []
        self.parent.call = lambda x: call(self.parent, x)

    def test_cors(self):
        """ Check that CORS enabling works """
        self.assertEqual(self.app.get("/cts?request=GetCapabilities").headers["Access-Control-Allow-Origin"], "*")
        self.assertEqual(self.app.get("/cts?request=GetCapabilities").headers["Access-Control-Allow-Methods"], "OPTIONS, GET")

    def test_restricted_cors(self):
        """ Check that area-restricted cors works """
        app = Flask("Nautilus")
        FlaskNautilus(
            app=app,
            resolver=NautilusCTSResolver(["./tests/test_data/latinLit"]),
            access_Control_Allow_Methods={"r_cts": "OPTIONS", "r_dts_collection": "OPTIONS", "r_dts_collections": "OPTIONS"},
            access_Control_Allow_Origin={"r_cts": "foo.bar", "r_dts_collection":"*", "r_dts_collections":"*"}
        )
        _app = app.test_client()
        self.assertEqual(_app.get("/cts?request=GetCapabilities").headers["Access-Control-Allow-Origin"], "foo.bar")
        self.assertEqual(_app.get("/cts?request=GetCapabilities").headers["Access-Control-Allow-Methods"], "OPTIONS")

    def test_get_capabilities(self):
        """ Check the GetCapabilities request """
        response = self.app.get("/cts?request=GetCapabilities")
        a = TextInventory.parse(resource=response.data.decode())
        self.assertEqual(
            str(a["urn:cts:latinLit:phi1294.phi002.perseus-lat2"].urn), "urn:cts:latinLit:phi1294.phi002.perseus-lat2",
        )
        # Test for cache : only works in Cache situation, with specific SIMPLE BACKEND
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_partial_capabilities(self):
        response = self.resolver.getMetadata(objectId="urn:cts:latinLit:phi1294.phi002.perseus-lat2")
        self.assertEqual(
            response.id, "urn:cts:latinLit:phi1294.phi002.perseus-lat2"
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_get_passage(self):
        """ Check the GetPassage request """
        passage = self.resolver.getTextualNode("urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.pr.1")
        self.assertEqual(
            passage.export(Mimetypes.PLAINTEXT), "Spero me secutum in libellis meis tale temperamen-"
        )
        self.assertCountEqual(
            self.parent.called, [{"request": "GetPassage", "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr.1"}]
        )
        p, n = passage.siblingsId
        self.assertEqual((None, "1.pr.2"), (p, n), "Passage range should be given")
        self.assertCountEqual(
            self.parent.called,
            [
                {"request": "GetPassage", "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr.1"},
                {"request": "GetPrevNextUrn", "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr.1"}
            ]
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_get_valid_reff(self):
        passages = self.resolver.getReffs("urn:cts:latinLit:phi1294.phi002.perseus-lat2", subreference="1.pr")
        self.assertEqual(
            passages, [
                "1.pr.1", '1.pr.2', '1.pr.3', '1.pr.4', '1.pr.5', '1.pr.6', '1.pr.7', '1.pr.8', '1.pr.9', '1.pr.10',
                '1.pr.11', '1.pr.12', '1.pr.13', '1.pr.14', '1.pr.15', '1.pr.16', '1.pr.17', '1.pr.18', '1.pr.19',
                '1.pr.20', '1.pr.21', '1.pr.22'
            ]
        )
        self.assertCountEqual(
            self.parent.called, [{
                "request": "GetValidReff", "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr", "level":"1"
            }]
        )
        passages = self.resolver.getReffs(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2",
            subreference="1.pr.1-1.pr.5",
            level=0
        )
        self.assertEqual(
            passages, [
                "1.pr.1", '1.pr.2', '1.pr.3', '1.pr.4', '1.pr.5'
            ]
        )
        self.assertCountEqual(
            self.parent.called, [{
                "request": "GetValidReff",
                "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr",
                "level": "1"
            },{
                "request": "GetValidReff",
                "urn": "urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr.1-1.pr.5",
                "level": "0"
            }]
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_get_passage_plus(self):
        """ Check the GetPassagePlus request """
        response = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.pr.1",
            prevnext=True, metadata=True
        )
        self.assertEqual(
            response.export(Mimetypes.PLAINTEXT), "Spero me secutum in libellis meis tale temperamen-"
        )
        self.assertEqual(
            response.prev, None
        )
        self.assertEqual(
            str(response.next.reference), "1.pr.2"
        )

        response = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.pr.10",
            prevnext=True, metadata=True
        )
        self.assertEqual(
            response.export(Mimetypes.PLAINTEXT), "borum veritatem, id est epigrammaton linguam, excu-",
            "Check Range works on normal middle GetPassage"
        )
        self.assertEqual(
            str(response.prev.reference), "1.pr.9"
        )
        self.assertEqual(
            str(response.next.reference), "1.pr.11"
        )

        response = self.resolver.getTextualNode(
            "urn:cts:latinLit:phi1294.phi002.perseus-lat2", "1.pr.10-1.pr.11",
            prevnext=True, metadata=True
        )
        self.assertEqual(
            response.export(Mimetypes.PLAINTEXT), "borum veritatem, id est epigrammaton linguam, excu- "
                             "sarem, si meum esset exemplum: sic scribit Catullus, sic ",
            "Check Range works on GetPassagePlus"
        )
        self.assertEqual(
            str(response.prev.reference), "1.pr.8-1.pr.9",
            "Check Range works on GetPassagePlus and compute right prev"
        )
        self.assertEqual(
            str(response.next.reference), "1.pr.12-1.pr.13",
            "Check Range works on GetPassagePlus and compute right next"
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_get_prevnext_urn(self):
        """ Check the GetPrevNext request """
        text = Text(urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2", retriever=self.parent)
        p, n = text.getPrevNextUrn(Reference("1.pr.1"))
        self.assertEqual(
            p, None
        )
        self.assertEqual(
            n, "1.pr.2"
        )

        response = text.getPassagePlus(Reference("1.pr.10"))
        self.assertEqual(
            str(response.prev.reference), "1.pr.9",
            "Check Range works on normal middle GetPassage"
        )
        self.assertEqual(
            str(response.next.reference), "1.pr.11"
        )

        response = text.getPassagePlus(Reference("1.pr.10-1.pr.11"))
        self.assertEqual(
            str(response.prev.reference), "1.pr.8-1.pr.9",
            "Check Range works on GetPassagePlus and compute right prev"
        )
        self.assertEqual(
            str(response.next.reference), "1.pr.12-1.pr.13",
            "Check Range works on GetPassagePlus and compute right next"
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_get_label(self):
        """Check get Label"""
        # Need to parse with Citation and parse individually or simply check for some equality
        data = self.app.get("/cts?request=GetLabel&urn=urn:cts:latinLit:phi1294.phi002.perseus-lat2")\
            .data.decode("utf-8").replace("\n", "")
        parsed = xmlparser(data)
        label = parsed.xpath(".//ti:label", namespaces=XPATH_NAMESPACES)
        label_str = re.sub("\s+", " ", tostring(label[0], encoding=str)).replace("\n", "")
        self.assertIn(
            '<groupname xml:lang="eng">Martial</groupname>',
            label_str,
            "groupname should be exported correctly"
        )
        self.assertIn(
            '<title xml:lang="eng">Epigrammata</title>',
            label_str,
            "title should be exported correctly"
        )
        self.assertIn(
            '<description xml:lang="eng"> M. Valerii Martialis Epigrammaton libri / recognovit W. Heraeus </description>',
            label_str,
            "description should be exported correctly"
        )
        self.assertIn(
            '<label xml:lang="eng">Epigrammata</label>',
            label_str,
            "label should be exported correctly"
        )
        citation = Citation.ingest(label[0])
        self.assertEqual(
            len(citation), 3, "There should be three level of citation"
        )
        self.assertEqual(
            citation.name, "book", "First level is book"
        )
        if self.cache is not None:
            self.assertGreater(
                len(self.cache.cache._cache), 0,
                "There should be something cached"
            )

    def test_missing_request(self):
        """Check get Label"""
        # Need to parse with Citation and parse individually or simply check for some equality
        data = self.app.get("/cts").data.decode("utf-8").replace("\n", "")
        self.assertIn(
            "Request missing one or more required parameters", data, "Error message should be displayed"
        )
        self.assertIn(
            "MissingParameter", data, "Error name should be displayed"
        )

    def test_InvalidUrn_request(self):
        """Check get Label"""
        # Need to parse with Citation and parse individually or simply check for some equality
        data = self.app.get("/cts?request=GetPassage&urn=urn:cts:latinLit:phi1295").data.decode()
        self.assertIn(
            "Syntactically valid URN refers in invalid value ", data, "Error message should be displayed"
        )
        self.assertIn(
            "InvalidURN", data, "Error name should be displayed"
        )
        data = self.app.get("/cts?request=GetPassagePlus&urn=urn:cts:latinLit:phi1294").data.decode()
        self.assertIn(
            "Syntactically valid URN refers in invalid value ", data, "Error message should be displayed"
        )
        self.assertIn(
            "InvalidURN", data, "Error name should be displayed"
        )

    def test_get_firstUrn(self):
        """Check get Label"""
        # Need to parse with Citation and parse individually or simply check for some equality
        data = self.app.get("/cts?request=GetFirstUrn&urn=urn:cts:latinLit:phi1294.phi002.perseus-lat2:1").data.decode()
        self.assertIn(
            "<urn>urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.pr</urn>", data, "First URN is displayed"
        )
        self.assertEqual(
            (data.startswith("<GetFirstUrn"), data.endswith("</GetFirstUrn>")), (True, True), "Nodes are Correct"
        )

    def test_dts_collection_route(self):
        """ Check that DTS Main collection works """
        response = self.app.get("/dts/collections")
        data = json.loads(response.data.decode())
        compared_to = self.resolver.getMetadata().export(Mimetypes.JSON.DTS.Std)
        self.maxDiff = None
        self.assertCountEqual(
            data, compared_to, "Main Collection should export as JSON DTS STD"
        )
        self.assertEqual(
            response.status_code, 200, "Answer code should be correct"
        )
        self.assertEqual(
            response.headers["Access-Control-Allow-Origin"], "*"
        )

    def test_dts_collection_target_route(self):
        """ Check that DTS Main collection works """
        response = self.app.get("/dts/collections/urn:cts:latinLit:phi1294")
        data = json.loads(response.data.decode())
        compared_to = self.resolver.getMetadata(objectId="urn:cts:latinLit:phi1294").export(Mimetypes.JSON.DTS.Std)
        self.maxDiff = None
        self.assertCountEqual(
            data, compared_to, "Main Collection should export as JSON DTS STD"
        )
        self.assertEqual(
            response.status_code, 200, "Answer code should be correct"
        )
        self.assertEqual(
            response.headers["Access-Control-Allow-Origin"], "*"
        )
        self.assertEqual(
            "urn:cts:latinLit:phi1294", data["@graph"]["@id"], "Label should be there"
        )