Ejemplo n.º 1
0
class MetaDataTest(TestCase):
    def setUp(self):
        self.metadata = MetaData()
        self.sample = sample_configuration

    def change_sample(self):
        """Return a context manager for hacking the sample data.

        This should be used follows:

            with self.change_sample() as data:
                data["some-key"] = "some-data"

        The changed sample file content will be available in self.sample
        once the context is done executing.
        """
        class HackManager(object):
            def __enter__(mgr):
                mgr.data = yaml.load(self.sample)
                return mgr.data

            def __exit__(mgr, exc_type, exc_val, exc_tb):
                self.sample = yaml.dump(mgr.data)
                return False

        return HackManager()

    def test_path_argument_loads_charm_info(self):
        info = MetaData(sample_path)
        self.assertEquals(info.name, "dummy")

    def test_check_basic_info_before_loading(self):
        """
        Attributes should be set to None before anything is loaded.
        """
        self.assertEquals(self.metadata.name, None)
        self.assertEquals(self.metadata.obsolete_revision, None)
        self.assertEquals(self.metadata.summary, None)
        self.assertEquals(self.metadata.description, None)

    def test_parse_and_check_basic_info(self):
        """
        Parsing the content file should work. :-)  Basic information will
        be available as attributes of the info file.
        """
        self.metadata.parse(self.sample)
        self.assertEquals(self.metadata.name, "dummy")
        self.assertEquals(self.metadata.obsolete_revision, None)
        self.assertEquals(self.metadata.summary, u"That's a dummy charm.")
        self.assertEquals(
            self.metadata.description, u"This is a longer description which\n"
            u"potentially contains multiple lines.\n")

    def assert_parse_with_revision(self, with_path):
        """
        Parsing the content file should work. :-)  Basic information will
        be available as attributes of the info file.
        """
        with self.change_sample() as data:
            data["revision"] = 123
        log = self.capture_logging("juju.charm")
        self.metadata.parse(self.sample, "some/path" if with_path else None)
        if with_path:
            self.assertIn(
                "some/path: revision field is obsolete. Move it to the "
                "'revision' file.", log.getvalue())
        self.assertEquals(self.metadata.name, "dummy")
        self.assertEquals(self.metadata.obsolete_revision, 123)
        self.assertEquals(self.metadata.summary, u"That's a dummy charm.")
        self.assertEquals(
            self.metadata.description, u"This is a longer description which\n"
            u"potentially contains multiple lines.\n")
        self.assertEquals(self.metadata.get_serialization_data()["revision"],
                          123)

    def test_parse_with_revision(self):
        self.assert_parse_with_revision(True)
        self.assert_parse_with_revision(False)

    def test_load_calls_parse_calls_parse_serialzation_data(self):
        """
        We'll break the rules a little bit here and test the implementation
        itself just so that we don't have to test *everything* twice. If
        load() calls parse() which calls parse_serialzation_data(), then
        whatever happens with parse_serialization_data(), happens with the
        others.
        """
        serialization_data = {"Hi": "there!"}
        yaml_data = yaml.dump(serialization_data)
        path = self.makeFile(yaml_data)
        mock = self.mocker.patch(self.metadata)
        mock.parse(yaml_data, path)
        self.mocker.passthrough()
        mock.parse_serialization_data(serialization_data, path)
        self.mocker.replay()

        self.metadata.load(path)
        # Do your magic Mocker!

    def test_metadata_parse_error_includes_path_with_load(self):
        broken = ("""\
        description: helo
name: hi
requires: {interface: zebra
revision: 0
summary: hola""")

        path = self.makeFile()
        e = self.assertRaises(yaml.YAMLError, self.metadata.parse, broken,
                              path)
        self.assertIn(path, str(e))

    def test_schema_error_includes_path_with_load(self):
        """
        When using load(), the exception message should mention the
        path name which was attempted.
        """
        with self.change_sample() as data:
            data["revision"] = "1"
        filename = self.makeFile(self.sample)
        error = self.assertRaises(MetaDataError, self.metadata.load, filename)
        self.assertEquals(
            str(error), "Bad data in charm info: %s: revision: "
            "expected int, got '1'" % filename)

    def test_load_missing_file(self):
        """
        When using load(), the exception message should mention the
        path name which was attempted.
        """
        filename = self.makeFile()
        error = self.assertRaises(FileNotFound, self.metadata.load, filename)
        self.assertEquals(error.path, filename)

    def test_name_summary_and_description_are_utf8(self):
        """
        Textual fields are decoded to unicode by the schema using UTF-8.
        """
        value = u"áéíóú"
        str_value = value.encode("utf-8")
        with self.change_sample() as data:
            data["name"] = str_value
            data["summary"] = str_value
            data["description"] = str_value
        self.metadata.parse(self.sample)
        self.assertEquals(self.metadata.name, value)
        self.assertEquals(self.metadata.summary, value)
        self.assertEquals(self.metadata.description, value)

    def test_get_serialized_data(self):
        """
        The get_serialization_data() function should return an object which
        may be passed to parse_serialization_data() to restore the state of
        the instance.
        """
        self.metadata.parse(self.sample)
        serialization_data = self.metadata.get_serialization_data()
        self.assertEquals(serialization_data["name"], "dummy")
Ejemplo n.º 2
0
class MetaDataTest(TestCase):

    def setUp(self):
        self.metadata = MetaData()
        self.sample = sample_configuration

    def change_sample(self):
        """Return a context manager for hacking the sample data.

        This should be used follows:

            with self.change_sample() as data:
                data["some-key"] = "some-data"

        The changed sample file content will be available in self.sample
        once the context is done executing.
        """

        class HackManager(object):

            def __enter__(mgr):
                mgr.data = yaml.load(self.sample)
                return mgr.data

            def __exit__(mgr, exc_type, exc_val, exc_tb):
                self.sample = yaml.dump(mgr.data)
                return False
        return HackManager()

    def test_path_argument_loads_charm_info(self):
        info = MetaData(sample_path)
        self.assertEquals(info.name, "dummy")

    def test_check_basic_info_before_loading(self):
        """
        Attributes should be set to None before anything is loaded.
        """
        self.assertEquals(self.metadata.name, None)
        self.assertEquals(self.metadata.obsolete_revision, None)
        self.assertEquals(self.metadata.summary, None)
        self.assertEquals(self.metadata.description, None)
        self.assertEquals(self.metadata.is_subordinate, False)

    def test_parse_and_check_basic_info(self):
        """
        Parsing the content file should work. :-)  Basic information will
        be available as attributes of the info file.
        """
        self.metadata.parse(self.sample)
        self.assertEquals(self.metadata.name, "dummy")
        self.assertEquals(self.metadata.obsolete_revision, None)
        self.assertEquals(self.metadata.summary, u"That's a dummy charm.")
        self.assertEquals(self.metadata.description,
                          u"This is a longer description which\n"
                          u"potentially contains multiple lines.\n")
        self.assertEquals(self.metadata.is_subordinate, False)

    def test_is_subordinate(self):
        """Validate rules for detecting proper subordinate charms are working"""
        logging_path = os.path.join(
            test_repository_path, "series", "logging", "metadata.yaml")
        logging_configuration = open(logging_path).read()
        self.metadata.parse(logging_configuration)
        self.assertTrue(self.metadata.is_subordinate)

    def test_subordinate_without_container_relation(self):
        """Validate rules for detecting proper subordinate charms are working

        Case where no container relation is specified.
        """
        with self.change_sample() as data:
            data["subordinate"] = True

        error = self.assertRaises(MetaDataError, self.metadata.parse, self.sample, "some/path")
        self.assertIn("some/path labeled subordinate but lacking scope:container `requires` relation",
                      str(error))

    def test_scope_constraint(self):
        """Verify the scope constrain is parsed properly."""
        logging_path = os.path.join(
            test_repository_path, "series", "logging", "metadata.yaml")
        logging_configuration = open(logging_path).read()
        self.metadata.parse(logging_configuration)
        # Verify the scope settings
        self.assertEqual(self.metadata.provides[u"logging-client"]["scope"],
                         "global")
        self.assertEqual(self.metadata.requires[u"logging-directory"]["scope"],
                         "container")
        self.assertTrue(self.metadata.is_subordinate)

    def assert_parse_with_revision(self, with_path):
        """
        Parsing the content file should work. :-)  Basic information will
        be available as attributes of the info file.
        """
        with self.change_sample() as data:
            data["revision"] = 123
        log = self.capture_logging("juju.charm")
        self.metadata.parse(self.sample, "some/path" if with_path else None)
        if with_path:
            self.assertIn(
                "some/path: revision field is obsolete. Move it to the "
                "'revision' file.",
                log.getvalue())
        self.assertEquals(self.metadata.name, "dummy")
        self.assertEquals(self.metadata.obsolete_revision, 123)
        self.assertEquals(self.metadata.summary, u"That's a dummy charm.")
        self.assertEquals(self.metadata.description,
                          u"This is a longer description which\n"
                          u"potentially contains multiple lines.\n")
        self.assertEquals(
            self.metadata.get_serialization_data()["revision"], 123)

    def test_parse_with_revision(self):
        self.assert_parse_with_revision(True)
        self.assert_parse_with_revision(False)

    def test_load_calls_parse_calls_parse_serialzation_data(self):
        """
        We'll break the rules a little bit here and test the implementation
        itself just so that we don't have to test *everything* twice. If
        load() calls parse() which calls parse_serialzation_data(), then
        whatever happens with parse_serialization_data(), happens with the
        others.
        """
        serialization_data = {"Hi": "there!"}
        yaml_data = yaml.dump(serialization_data)
        path = self.makeFile(yaml_data)
        mock = self.mocker.patch(self.metadata)
        mock.parse(yaml_data, path)
        self.mocker.passthrough()
        mock.parse_serialization_data(serialization_data, path)
        self.mocker.replay()

        self.metadata.load(path)
        # Do your magic Mocker!

    def test_metadata_parse_error_includes_path_with_load(self):
        broken = ("""\
        description: helo
name: hi
requires: {interface: zebra
revision: 0
summary: hola""")

        path = self.makeFile()
        e = self.assertRaises(
            yaml.YAMLError, self.metadata.parse, broken, path)
        self.assertIn(path, str(e))

    def test_schema_error_includes_path_with_load(self):
        """
        When using load(), the exception message should mention the
        path name which was attempted.
        """
        with self.change_sample() as data:
            data["revision"] = "1"
        filename = self.makeFile(self.sample)
        error = self.assertRaises(MetaDataError,
                                  self.metadata.load, filename)
        self.assertEquals(str(error),
                          "Bad data in charm info: %s: revision: "
                          "expected int, got '1'" % filename)

    def test_load_missing_file(self):
        """
        When using load(), the exception message should mention the
        path name which was attempted.
        """
        filename = self.makeFile()
        error = self.assertRaises(FileNotFound,
                                  self.metadata.load, filename)
        self.assertEquals(error.path, filename)

    def test_name_summary_and_description_are_utf8(self):
        """
        Textual fields are decoded to unicode by the schema using UTF-8.
        """
        value = u"áéíóú"
        str_value = value.encode("utf-8")
        with self.change_sample() as data:
            data["name"] = str_value
            data["summary"] = str_value
            data["description"] = str_value
        self.metadata.parse(self.sample)
        self.assertEquals(self.metadata.name, value)
        self.assertEquals(self.metadata.summary, value)
        self.assertEquals(self.metadata.description, value)

    def test_get_serialized_data(self):
        """
        The get_serialization_data() function should return an object which
        may be passed to parse_serialization_data() to restore the state of
        the instance.
        """
        self.metadata.parse(self.sample)
        serialization_data = self.metadata.get_serialization_data()
        self.assertEquals(serialization_data["name"], "dummy")

    def test_provide_implicit_relation(self):
        """Verify providing a juju-* reserved relation errors"""
        with self.change_sample() as data:
            data["provides"] = {"juju-foo": {"interface": "juju-magic", "scope": "container"}}

        # verify relation level error
        error = self.assertRaises(MetaDataError,
                                  self.metadata.parse, self.sample)
        self.assertIn("Charm dummy attempting to provide relation in implicit relation namespace: juju-foo",
                      str(error))

        # verify interface level error
        with self.change_sample() as data:
            data["provides"] = {"foo-rel": {"interface": "juju-magic", "scope": "container"}}

        error = self.assertRaises(MetaDataError,
                                  self.metadata.parse, self.sample)
        self.assertIn(
            "Charm dummy attempting to provide interface in implicit namespace: juju-magic (relation: foo-rel)",
            str(error))