def test_deeply_nested(self):
        sandbox = TempFolderSandbox()
        sandbox.put_files(
            ("rules/file.rule", yaml.safe_dump(self.gen_rule(0))),
            ("rules/a/file.rule", yaml.safe_dump(self.gen_rule(1))),
            ("rules/a/b/file.rule", yaml.safe_dump(self.gen_rule(2))),
        )

        rules = AlertRules(topology=self.topology)
        rules.add_path(os.path.join(sandbox.root, "rules"), recursive=True)
        rules_file_dict_read = rules.as_dict()

        expected_rules_file = {
            "groups": [
                {
                    "name":
                    f"{self.topology.identifier}_file_alerts",
                    "rules": [
                        self.gen_rule(
                            0, labels=self.topology.as_promql_label_dict())
                    ],
                },
                {
                    "name":
                    f"{self.topology.identifier}_a_file_alerts",
                    "rules": [
                        self.gen_rule(
                            1, labels=self.topology.as_promql_label_dict())
                    ],
                },
                {
                    "name":
                    f"{self.topology.identifier}_a_b_file_alerts",
                    "rules": [
                        self.gen_rule(
                            2, labels=self.topology.as_promql_label_dict())
                    ],
                },
            ]
        }
        self.assertDictEqual(expected_rules_file, rules_file_dict_read)
class TestAlertRulesWithOneRulePerFile(unittest.TestCase):
    def setUp(self) -> None:
        free_standing_rule = {
            "alert": "free_standing",
            "expr": "avg(some_vector[5m]) > 5",
        }

        alert_rule = {
            "alert": "CPUOverUse",
            "expr": "process_cpu_seconds_total{%%juju_topology%%} > 0.12",
        }
        rules_file_dict = {
            "groups": [{
                "name": "group1",
                "rules": [alert_rule]
            }]
        }

        self.sandbox = TempFolderSandbox()
        self.sandbox.put_files(
            ("rules/prom/mixed_format/lma_rule.rule",
             yaml.safe_dump(alert_rule)),
            ("rules/prom/mixed_format/standard_rule.rule",
             yaml.safe_dump(rules_file_dict)),
            ("rules/prom/lma_format/free_standing_rule.rule",
             yaml.safe_dump(free_standing_rule)),
            ("rules/prom/prom_format/standard_rule.rule",
             yaml.safe_dump(rules_file_dict)),
        )

        self.topology = ProviderTopology("MyModel", "MyUUID", "MyApp",
                                         "MyCharm")

    def test_non_recursive_is_default(self):
        rules = AlertRules(topology=self.topology)
        rules.add_path(os.path.join(self.sandbox.root, "rules", "prom"))
        rules_file_dict = rules.as_dict()
        self.assertEqual({}, rules_file_dict)

    def test_non_recursive_lma_format_loading_from_root_dir(self):
        rules = AlertRules(topology=self.topology)
        rules.add_path(
            os.path.join(self.sandbox.root, "rules", "prom", "lma_format"))
        rules_file_dict = rules.as_dict()

        expected_freestanding_rule = {
            "alert": "free_standing",
            "expr": "avg(some_vector[5m]) > 5",
            "labels": self.topology.as_promql_label_dict(),
        }

        expected_rules_file = {
            "groups": [
                {
                    "name":
                    f"{self.topology.identifier}_free_standing_rule_alerts",
                    "rules": [expected_freestanding_rule],
                },
            ]
        }

        self.assertEqual(expected_rules_file, rules_file_dict)

    def test_non_recursive_official_format_loading_from_root_dir(self):
        rules = AlertRules(topology=self.topology)
        rules.add_path(
            os.path.join(self.sandbox.root, "rules", "prom", "prom_format"))
        rules_file_dict = rules.as_dict()

        expected_alert_rule = {
            "alert": "CPUOverUse",
            "expr":
            f"process_cpu_seconds_total{{{self.topology.promql_labels}}} > 0.12",
            "labels": self.topology.as_promql_label_dict(),
        }

        expected_rules_file = {
            "groups": [
                {
                    "name": f"{self.topology.identifier}_group1_alerts",
                    "rules": [expected_alert_rule],
                },
            ]
        }

        self.assertEqual(expected_rules_file, rules_file_dict)

    def test_alerts_in_both_formats_are_recursively_aggregated(self):
        """This test covers several aspects of the rules format.

        - Group name:
          - For rules in lma format, core group name is the filename
          - For rules in official format, core group name is the group name in the file
        """
        rules = AlertRules(topology=self.topology)
        rules.add_path(os.path.join(self.sandbox.root, "rules", "prom"),
                       recursive=True)
        rules_file_dict = rules.as_dict()

        expected_alert_rule = {
            "alert": "CPUOverUse",
            "expr":
            f"process_cpu_seconds_total{{{self.topology.promql_labels}}} > 0.12",
            "labels": self.topology.as_promql_label_dict(),
        }

        expected_freestanding_rule = {
            "alert": "free_standing",
            "expr": "avg(some_vector[5m]) > 5",
            "labels": self.topology.as_promql_label_dict(),
        }

        expected_rules_file = {
            "groups": [
                {
                    "name":
                    f"{self.topology.identifier}_mixed_format_group1_alerts",
                    "rules": [expected_alert_rule],
                },
                {
                    "name":
                    f"{self.topology.identifier}_mixed_format_lma_rule_alerts",
                    "rules": [expected_alert_rule],
                },
                {
                    "name":
                    f"{self.topology.identifier}_lma_format_free_standing_rule_alerts",
                    "rules": [expected_freestanding_rule],
                },
                {
                    "name":
                    f"{self.topology.identifier}_prom_format_group1_alerts",
                    "rules": [expected_alert_rule],
                },
            ]
        }

        self.assertEqual({},
                         DeepDiff(expected_rules_file,
                                  rules_file_dict,
                                  ignore_order=True))