Beispiel #1
0
 def test_config_changed(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     config = {
         "cert-path": "/cert/path",
         "chain-path": "/chain/path",
         "combined-path": "/combined/path",
         "deploy-command": "/bin/deploy",
         "fullchain-path": "/fullchain/path",
         "key-path": "/key/path",
     }
     harness.update_config(self._config(harness.charm, **config))
     charm._host.write_config.assert_called_once()
     args = charm._host.write_config.call_args.args
     self.assertEqual(args[0], "/etc/certbot-charm/config.ini")
     self.assertEqual(args[1], {
         'DEFAULT': {
             'cert-path': '/cert/path',
             'chain-path': '/chain/path',
             'combined-path': '/combined/path',
             'fullchain-path': '/fullchain/path',
             'key-path': '/key/path'},
         'deploy': {
             'command': '/bin/deploy'}})
     charm._host.write_file.assert_called_once_with(
         "/etc/certbot-charm/dns-google.json", b"", mode=0o600)
 def test_cron_schedule_set(self, mock_open_call, os_path_isdir,
                            os_makedirs, os_symlink):
     harness = Harness(SimpleStreamsCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     default_config = self.default_config()
     default_config['cron-schedule'] = str(uuid4())
     self.assertEqual(harness.charm._stored.config, {})
     harness.update_config(default_config)
     self.assertEqual(harness.charm._stored.config, default_config)
     mock_open_call.assert_called_with(
         '/etc/cron.d/{}'.format(harness.charm.model.app.name), "w")
     mock_open_call.return_value.write.assert_called_once_with(
         "{} root sstream-mirror --keep"
         " --keyring={} --path={} --log-file={} --max={} {} {} \'{}\'\n".
         format(
             default_config['cron-schedule'],
             default_config['keyring-file'],
             default_config['path'],
             default_config['log-file'],
             default_config['image-max'],
             default_config['image-source'],
             "{}/latest".format(default_config['image-dir']),
             default_config['image-selectors'],
         ))
     self.assertTrue(os_path_isdir.called)
     self.assertFalse(os_makedirs.called)
     self.assertFalse(os_symlink.called)
 def test_config_changed(self):
     harness = Harness(OpenfaasQueueWorkerCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     self.assertEqual(list(harness.charm._stored.things), [])
     harness.update_config({"thing": "foo"})
     self.assertEqual(list(harness.charm._stored.things), ["foo"])
Beispiel #4
0
 def test_get_certificate_action_fail(self):
     os.environ["JUJU_ACTION_UUID"] = "1"
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     event = Mock(
         params={
             "agree-tos": True,
             "credentials": "AAAA",
             "domains": "action.example.com",
             "email": "*****@*****.**",
             "plugin": "dns-google",
             "propagation-seconds": 30
         })
     charm._host.run.side_effect = subprocess.CalledProcessError(
         1, "certbot")
     charm._host.exists.return_value = True
     harness.charm._on_get_certificate_action(event)
     event.fail.assert_called_once_with(
         "cannot get certificate: Command 'certbot' returned non-zero exit status 1."
     )
     charm._host.unlink.assert_called_once_with(
         "/etc/certbot-charm/action-1.cred")
class KafkaCharmTest(unittest.TestCase):
    def setUp(self) -> None:
        self.harness = Harness(KafkaOperator)
        self.addCleanup(self.harness.cleanup)
        self.harness.begin()
        self.harness.add_oci_resource("kafka-image")
        self.harness.update_config(BASE_CONFIG)

    def test_kafka_layer(self):

        expected = {
            "summary": "kafka layer",
            "description": "kafka layer",
            "services": {
                "kafka-setup": {
                    "override": "replace",
                    "summary":
                    "kafka setup step - initialize & format storage",
                    "command":
                    "/opt/bitnami/kafka/bin/kafka-storage.sh format -t test-id -c /opt/bitnami/kafka/config/kraft/server.properties",
                    "startup": "enabled",
                },
                "kafka": {
                    "override": "replace",
                    "summary": "kafka service",
                    "command":
                    "/opt/bitnami/kafka/bin/kafka-server-start.sh /opt/bitnami/kafka/config/kraft/server.properties",
                    "startup": "enabled",
                    "requires": ["kafka-setup"],
                },
            },
        }

        assert self.harness.charm._kafka_layer() == expected
 def test_config_changed(self):
     harness = Harness(JujuDashboardCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     self.assertEqual(list(harness.charm._stored.things), [])
     harness.update_config({"thing": "foo"})
     self.assertEqual(list(harness.charm._stored.things), ["foo"])
Beispiel #7
0
 def test_run_certbot_params(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     config = {
         "agree-tos": False,
         "domains": "charm.example.com,www.charm.example.com",
         "email": "*****@*****.**"
     }
     harness.update_config(self._config(harness.charm, **config))
     harness.charm._run_certbot(
         "test", True, "*****@*****.**",
         "params.example.com,www.params.example.com",
         ["--extra-1", "--extra-2"], {
             "ENV1": "e1",
             "ENV2": "e2"
         })
     charm._host.run.assert_called_once_with([
         "certbot", "certonly", "-n", "--no-eff-email", "--test",
         "--agree-tos", "[email protected]",
         "--domains=params.example.com,www.params.example.com", "--extra-1",
         "--extra-2"
     ],
                                             env={
                                                 "ENV1": "e1",
                                                 "ENV2": "e2"
                                             })
Beispiel #8
0
 def test_get_dns_google_certificate_action_defaults(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     config = {
         "agree-tos": True,
         "dns-google-credentials": "AAAA",
         "dns-google-propagation-seconds": 40,
         "domains": "charm.example.com",
         "email": "*****@*****.**"}
     harness.update_config(self._config(harness.charm, **config))
     event = Mock(params={})
     harness.charm._on_get_dns_google_certificate_action(event)
     self.assertEqual(len(charm._host.run.call_args_list), 2)
     self.assertEqual(
         charm._host.run.call_args_list[0],
         call(["certbot", "certonly", "-n", "--no-eff-email", "--dns-google", "--agree-tos",
               "[email protected]", "--domains=charm.example.com",
               "--dns-google-credentials=/etc/certbot-charm/dns-google.json",
               "--dns-google-propagation-seconds=40"]))
     self.assertEqual(charm._host.run.call_args_list[1][0], ([
                      '/etc/letsencrypt/renewal-hooks/deploy/certbot-charm'],))
     self.assertEqual(charm._host.run.call_args_list[1][1]["env"]
                      ["RENEWED_LINEAGE"], "/etc/letsencrypt/live/charm.example.com")
Beispiel #9
0
 def test_get_certificate_action_dns_rfc2136(self):
     os.environ["JUJU_ACTION_UUID"] = "1"
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     event = Mock(
         params={
             "agree-tos": True,
             "credentials": "AAAA",
             "domains": "action.example.com",
             "email": "*****@*****.**",
             "propagation-seconds": 30,
             "plugin": "dns-rfc2136"
         })
     harness.charm._on_get_certificate_action(event)
     self.assertEqual(len(charm._host.run.call_args_list), 2)
     self.assertEqual(
         charm._host.run.call_args_list[0],
         call([
             "certbot", "certonly", "-n", "--no-eff-email", "--dns-rfc2136",
             "--agree-tos", "[email protected]",
             "--domains=action.example.com",
             "--dns-rfc2136-credentials=/etc/certbot-charm/action-1.cred",
             "--dns-rfc2136-propagation-seconds=30"
         ],
              env=None))
     self.assertEqual(
         charm._host.run.call_args_list[1][0],
         (['/etc/letsencrypt/renewal-hooks/deploy/certbot-charm'], ))
     self.assertEqual(
         charm._host.run.call_args_list[1][1]["env"]["RENEWED_LINEAGE"],
         "/etc/letsencrypt/live/action.example.com")
Beispiel #10
0
class TestCharm(unittest.TestCase):
    def setUp(self):
        self.harness = Harness(MongoDBCharm)
        self.addCleanup(self.harness.cleanup)
        mongo_resource = {
            "registrypath": "mongodb:4.4.1",
            "username": "******",
            "password": "******"
        }
        self.harness.add_oci_resource("mongodb-image", mongo_resource)
        self.harness.begin()

    def test_replica_set_name_can_be_changed(self):
        self.harness.set_leader(True)

        # check default replica set name
        self.harness.charm.on.config_changed.emit()
        pod_spec = self.harness.get_pod_spec()
        self.assertEqual(replica_set_name(pod_spec), "rs0")

        # check replica set name can be changed
        self.harness.update_config({"replica_set_name": "new_name"})
        pod_spec = self.harness.get_pod_spec()
        self.assertEqual(replica_set_name(pod_spec), "new_name")

    @patch("mongoserver.MongoDB.reconfigure_replica_set")
    def test_replica_set_is_reconfigured_when_peer_joins(self, mock_reconf):
        self.harness.set_leader(True)
        rel_id = self.harness.add_relation('mongodb', 'mongodb')
        self.harness.add_relation_unit(rel_id, 'mongodb/1')
        self.harness.update_relation_data(rel_id,
                                          'mongodb/1',
                                          {'private-address': '10.0.0.1'})
        peers = ['mongodb-0.mongodb-endpoints',
                 'mongodb-1.mongodb-endpoints']
        mock_reconf.assert_called_once_with(peers)

    def test_uri_data_is_generated_correctly(self):
        self.harness.set_leader(True)
        standalone_uri = self.harness.charm.mongo.standalone_uri
        replica_set_uri = self.harness.charm.mongo.replica_set_uri
        self.assertEqual(standalone_uri, 'mongodb://mongodb:27017/')
        self.assertEqual(replica_set_uri, 'mongodb://mongodb-0.mongodb-endpoints:27017/')

    def test_database_relation_data_is_set_correctly(self):
        self.harness.set_leader(True)
        rel_id = self.harness.add_relation('database', 'client')
        self.harness.add_relation_unit(rel_id, 'client/1')
        rel = self.harness.framework.model.get_relation('database', rel_id)
        unit = self.harness.framework.model.get_unit('client/1')
        self.harness.charm.on['database'].relation_changed.emit(rel, unit)
        got = self.harness.get_relation_data(rel_id, self.harness.framework.model.unit.name)
        expected = {
            'replicated': 'False',
            'replica_set_name': 'rs0',
            'standalone_uri': 'mongodb://mongodb:27017/',
            'replica_set_uri': 'mongodb://mongodb-0.mongodb-endpoints:27017/'
        }
        self.assertDictEqual(got, expected)
Beispiel #11
0
 def test_config_changed(self):
     harness = Harness(JujuControllerCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     self.assertEqual(list(harness.charm._stored.things), [])
     harness.update_config({"controller-url": "https://controller"})
     self.assertEqual(list(harness.charm._stored.things),
                      ["https://controller"])
 def test_config_changed(self):
     harness = Harness(CharmK8SCassandraCharm)
     # from 0.8 you should also do:
     # self.addCleanup(harness.cleanup)
     harness.begin()
     self.assertEqual(list(harness.charm._stored.things), [])
     harness.update_config({"thing": "foo"})
     self.assertEqual(list(harness.charm._stored.things), ["foo"])
Beispiel #13
0
 def test_get_certificate_unknown_plugin(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     with self.assertRaises(charm.UnsupportedPluginError):
         harness.charm._get_certificate("no-such-plugin", False, "", "")
Beispiel #14
0
 def test_start_no_certificate(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     harness.charm.on.start.emit()
     self.assertEqual(harness.charm.model.unit.status,
                      BlockedStatus('certificate not yet acquired.'))
Beispiel #15
0
 def test_run_certbot_nothing_set(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     harness.charm._run_certbot("test", False, "", "")
     charm._host.run.assert_called_once_with(
         ["certbot", "certonly", "-n", "--no-eff-email", "--test"])
Beispiel #16
0
class TestCharm(unittest.TestCase):
    def setUp(self):
        self.harness = Harness(HookTestCharm)
        self.addCleanup(self.harness.cleanup)
        self.harness.begin()

    def test_config_changed(self):
        self.assertEqual(list(self.harness.charm._stored.things), [])
        self.harness.update_config({"thing": "foo"})
        self.assertEqual(list(self.harness.charm._stored.things), ["foo"])

    def test_action(self):
        # the harness doesn't (yet!) help much with actions themselves
        action_event = Mock(params={"fail": ""})
        self.harness.charm._on_fortune_action(action_event)

        self.assertTrue(action_event.set_results.called)

    def test_action_fail(self):
        action_event = Mock(params={"fail": "fail this"})
        self.harness.charm._on_fortune_action(action_event)

        self.assertEqual(action_event.fail.call_args, [("fail this", )])

    def test_httpbin_pebble_ready(self):
        # Check the initial Pebble plan is empty
        initial_plan = self.harness.get_container_pebble_plan("httpbin")
        self.assertEqual(initial_plan.to_yaml(), "{}\n")
        # Expected plan after Pebble ready with default config
        expected_plan = {
            "services": {
                "httpbin": {
                    "override": "replace",
                    "summary": "httpbin",
                    "command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent",
                    "startup": "enabled",
                    "environment": {
                        "thing": "🎁"
                    },
                }
            },
        }
        # Get the httpbin container from the model
        container = self.harness.model.unit.get_container("httpbin")
        # Emit the PebbleReadyEvent carrying the httpbin container
        self.harness.charm.on.httpbin_pebble_ready.emit(container)
        # Get the plan now we've run PebbleReady
        updated_plan = self.harness.get_container_pebble_plan(
            "httpbin").to_dict()
        # Check we've got the plan we expected
        self.assertEqual(expected_plan, updated_plan)
        # Check the service was started
        service = self.harness.model.unit.get_container("httpbin").get_service(
            "httpbin")
        self.assertTrue(service.is_running())
        # Ensure we set an ActiveStatus with no message
        self.assertEqual(self.harness.model.unit.status, ActiveStatus())
Beispiel #17
0
class TestCharm(unittest.TestCase):
    def setUp(self):
        self.harness = Harness(PlanServicesTestCharm)
        self.addCleanup(self.harness.cleanup)
        self.harness.begin()

    def test_config_changed(self):
        self.harness.update_config({"thing": "foo"})
        self.assertEqual(self.harness.model.unit.status, ActiveStatus())
 def test_repo_register(self, run, wt, rm):
     """Test repo registration"""
     harness = Harness(GithubRunnerOperator)
     harness.update_config({
         "path": "mockorg/repo",
         "token": "mocktoken",
         "reconcile-interval": 5
     })
     harness.begin()
     harness.charm.on.config_changed.emit()
     rm.assert_called_with("mockorg/repo", "mocktoken", "github-runner", 5)
 def test_publish_relation_joined(self, mock_open_call, os_path_isdir,
                                  os_makedirs, os_symlink):
     harness = Harness(SimpleStreamsCharm)
     harness.begin()
     default_config = self.default_config()
     self.assertEqual(harness.charm._stored.config, {})
     harness.update_config(default_config)
     relation_id = harness.add_relation('publish', 'webserver')
     harness.add_relation_unit(relation_id, 'webserver/0')
     assert harness.get_relation_data(relation_id, harness._unit_name)\
            == {'path': '{}/publish'.format(default_config['image-dir'])}
Beispiel #20
0
class TestCharm(unittest.TestCase):
    def setUp(self) -> None:
        self.harness = Harness(TrainingCharm)
        self.addCleanup(self.harness.cleanup)
        self.harness.begin()

    def test__grafana_port_config_changed(self):
        self.harness.set_leader(True)
        self.harness.update_config({"grafana_port": 4000})
        pod_spec = self.harness.get_pod_spec()[0]
        self.assertEqual(pod_spec["containers"][0]["ports"][0]["containerPort"], 4000)
        self.assertEqual(pod_spec["containers"][0]["readinessProbe"]["httpGet"]["port"], 4000)
 def test_config_changed(self, os_path_isdir, os_makedirs, os_symlink):
     harness = Harness(SimpleStreamsCharm)
     self.addCleanup(harness.cleanup)
     os_path_isdir.return_value = False
     harness.begin()
     default_config = self.default_config()
     self.assertEqual(harness.charm._stored.config, {})
     harness.update_config(default_config)
     self.assertTrue(os_path_isdir.called)
     self.assertTrue(os_makedirs.called)
     self.assertTrue(os_symlink.called)
     assert harness.charm._stored.config == default_config
Beispiel #22
0
 def test_config_changed(self):
     harness = Harness(SubCephMonCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     self.assertEqual(harness.charm._stored.rados_gw, {
         'hostname': '',
         'port': ''
     })
     harness.update_config()
     self.assertEqual(harness.charm._stored.rados_gw, {
         'hostname': '',
         'port': ''
     })
Beispiel #23
0
 def test_hooks_enabled_and_disabled(self):
     harness = Harness(RecordingCharm,
                       meta='''
         name: test-charm
     ''')
     # Before begin() there are no events.
     harness.update_config({'value': 'first'})
     # By default, after begin the charm is set up to receive events.
     harness.begin()
     harness.update_config({'value': 'second'})
     self.assertEqual(harness.charm.get_changes(reset=True), [{
         'name': 'config',
         'data': {
             'value': 'second'
         }
     }])
     # Once disabled, we won't see config-changed when we make an update
     harness.disable_hooks()
     harness.update_config({'third': '3'})
     self.assertEqual(harness.charm.get_changes(reset=True), [])
     harness.enable_hooks()
     harness.update_config({'value': 'fourth'})
     self.assertEqual(harness.charm.get_changes(reset=True), [{
         'name': 'config',
         'data': {
             'value': 'fourth',
             'third': '3'
         }
     }])
Beispiel #24
0
 def test_run_certbot_charm_config(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     config = {
         "agree-tos": True,
         "domains": "charm.example.com,www.charm.example.com",
         "email": "*****@*****.**"}
     harness.update_config(self._config(harness.charm, **config))
     harness.charm._run_certbot(plugin="test")
     charm._host.run.assert_called_once_with(
         ["certbot", "certonly", "-n", "--no-eff-email", "--test", "--agree-tos",
          "[email protected]",
          "--domains=charm.example.com,www.charm.example.com"])
Beispiel #25
0
 def test_start_get_certificate(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     config = {
         "agree-tos": True,
         "domains": "example.com",
         "email": "*****@*****.**",
         "plugin": "dns-google",
     }
     harness.update_config(self._config(harness.charm, **config))
     harness.charm.on.start.emit()
     self.assertEqual(harness.charm.model.unit.status,
                      ActiveStatus('maintaining certificate for example.com.'))
Beispiel #26
0
 def test_get_dns_google_certificate_action_fail(self):
     charm._host = Mock()
     harness = Harness(charm.CertbotCharm)
     self.addCleanup(harness.cleanup)
     harness.begin()
     harness.update_config(self._config(harness.charm))
     event = Mock(params={
         "agree-tos": True,
         "credentials": "AAAA",
         "domains": "action.example.com",
         "email": "*****@*****.**",
         "propagation-seconds": 30})
     charm._host.run.side_effect = subprocess.CalledProcessError(1, "certbot")
     harness.charm._on_get_dns_google_certificate_action(event)
     event.fail.assert_called_once_with(
         "cannot get certificate: Command 'certbot' returned non-zero exit status 1.")
Beispiel #27
0
class TestCharm(unittest.TestCase):
    """MetalLB Controller Charm Unit Tests."""
    @patch.dict('charm.os.environ', {'JUJU_MODEL_NAME': 'unit-test-metallb'})
    def setUp(self):
        """Test setup."""
        self.harness = Harness(MetallbControllerCharm)
        self.harness.set_leader(is_leader=True)
        self.harness.begin()

    @patch.dict('charm.os.environ', {'JUJU_MODEL_NAME': 'unit-test-metallb'})
    @patch("utils.bind_role_with_api")
    @patch("utils.create_namespaced_role_with_api")
    @patch("utils.create_pod_security_policy_with_api")
    def test_on_start(self, create_psp, create_ns_role,
                      create_ns_role_binding):
        """Test installation."""
        mock_pod_spec = self.harness.charm.set_pod_spec = Mock()
        self.assertFalse(self.harness.charm._stored.started)
        self.harness.charm.on.start.emit()
        mock_pod_spec.assert_called_once()
        create_psp.assert_called_once()
        create_ns_role.assert_called_once()
        create_ns_role_binding.assert_called_once()
        self.assertTrue(self.harness.charm._stored.started)

    def test_config_changed(self):
        """Test update config upon change."""
        mock_pod_spec = self.harness.charm.set_pod_spec = Mock()
        self.assertFalse(self.harness.charm._stored.configured)
        self.harness.update_config({"iprange": "192.168.1.88-192.168.1.89"})
        mock_pod_spec.assert_called_once()
        self.assertTrue(self.harness.charm._stored.configured)

    @patch("utils.delete_namespaced_role_with_api")
    @patch("utils.delete_namespaced_role_binding_with_api")
    @patch("utils.delete_pod_security_policy_with_api")
    def test_on_remove(self, delete_psp, delete_ns_role_binding,
                       delete_ns_role):
        """Test remove hook."""
        self.harness.charm.on.remove.emit()
        delete_psp.assert_called_once()
        delete_ns_role_binding.assert_called_once()
        delete_ns_role.assert_called_once()
        self.assertFalse(self.harness.charm._stored.configured)
        self.assertFalse(self.harness.charm._stored.started)
Beispiel #28
0
    def test_get_certificate_action_dns_route53_defaults(self):
        charm._host = Mock()
        harness = Harness(charm.CertbotCharm)
        self.addCleanup(harness.cleanup)
        harness.begin()
        config = {
            "agree-tos": True,
            "dns-route53-aws-access-key-id": "test-key-id",
            "dns-route53-aws-secret-access-key": "test-secret-key",
            "domains": "charm.example.com",
            "email": "*****@*****.**",
            "plugin": "dns-route53",
            "propagation-seconds": 40
        }
        harness.update_config(self._config(harness.charm, **config))
        event = Mock(params={})

        with tempfile.TemporaryDirectory() as dir:
            harness.charm._aws_config_file = pathlib.Path(dir).joinpath(
                ".aws", "config")
            harness.charm._on_get_certificate_action(event)
            awsconfig = configparser.ConfigParser()
            awsconfig.read(harness.charm._aws_config_file)
            self.assertEqual(awsconfig["default"]["aws_access_key_id"],
                             "test-key-id")
            self.assertEqual(awsconfig["default"]["aws_secret_access_key"],
                             "test-secret-key")
            self.assertEqual(len(charm._host.run.call_args_list), 2)

        self.assertEqual(len(charm._host.run.call_args_list), 2)
        expectCmd = [
            "certbot", "certonly", "-n", "--no-eff-email", "--dns-route53",
            "--agree-tos", "[email protected]",
            "--domains=charm.example.com",
            "--dns-route53-propagation-seconds=40"
        ]
        self.assertEqual(charm._host.run.call_args_list[0], call(expectCmd))
        self.assertEqual(
            charm._host.run.call_args_list[1][0],
            (['/etc/letsencrypt/renewal-hooks/deploy/certbot-charm'], ))
        self.assertEqual(
            charm._host.run.call_args_list[1][1]["env"]["RENEWED_LINEAGE"],
            "/etc/letsencrypt/live/charm.example.com")
class TestCharm(unittest.TestCase):
    def setUp(self):
        """Setup the test harness."""
        self.harness = Harness(PostgreSQLCharm)
        self.harness.begin()
        self.harness.disable_hooks()
        self.maxDiff = None

    def test_check_for_empty_config_no_image(self):
        """Check for correctly reported empty required image."""
        self.harness.update_config(CONFIG_NO_IMAGE)
        expected = "required setting(s) empty: image"
        self.assertEqual(self.harness.charm._check_for_config_problems(), expected)

    def test_check_for_missing_config_no_image_password(self):
        """Check for correctly reported empty required image_password."""
        self.harness.update_config(CONFIG_NO_IMAGE_PASSWORD)
        expected = "required setting(s) empty: image_password"
        self.assertEqual(self.harness.charm._check_for_config_problems(), expected)
    def test_update_config(self, run, wt, rm):
        rm.return_value = mock_rm = MagicMock()
        harness = Harness(GithubRunnerOperator)
        harness.update_config({"path": "mockorg/repo", "token": "mocktoken"})
        harness.begin()

        # update to 10 containers
        harness.update_config({"containers": 10, "virtual-machines": 0})
        harness.charm.on.reconcile_runners.emit()
        rm.assert_called_with("mockorg/repo", "mocktoken", "github-runner", 5)
        mock_rm.reconcile.assert_has_calls([
            call("container", 10),
            call("virtual-machine", 0, VMResources(2, "7GiB", "10GiB")),
        ])
        mock_rm.reset_mock()

        # update to 10 VMs with 4 cpu and 7GiB memory
        harness.update_config({
            "containers": 0,
            "virtual-machines": 10,
            "vm-cpu": 4
        })
        harness.charm.on.reconcile_runners.emit()
        rm.assert_called_with("mockorg/repo", "mocktoken", "github-runner", 5)
        mock_rm.reconcile.assert_has_calls([
            # 3 containers from quantity
            call("container", 3),
            call("virtual-machine", 10, VMResources(4, "7GiB", "10GiB")),
        ])
        mock_rm.reset_mock()