|
14 | 14 |
|
15 | 15 | from .. import settings as app_settings
|
16 | 16 | from ..signals import config_modified, config_status_changed
|
| 17 | +from ..tasks import auto_add_template_to_existing_configs |
17 | 18 | from ..tasks import logger as task_logger
|
18 | 19 | from ..tasks import update_template_related_config_status
|
19 | 20 | from .utils import CreateConfigTemplateMixin, TestVpnX509Mixin
|
@@ -517,6 +518,36 @@ def test_regression_preventing_from_fixing_invalid_conf(self):
|
517 | 518 | template.full_clean()
|
518 | 519 | template.save()
|
519 | 520 |
|
| 521 | + def test_auto_add_to_existing_configs_error_handling(self): |
| 522 | + with self.subTest("Template is not default or required"): |
| 523 | + template = self._create_template( |
| 524 | + name="test-template", required=False, default=False |
| 525 | + ) |
| 526 | + with mock.patch.object(transaction, "atomic") as mocked_atomic: |
| 527 | + template._auto_add_to_existing_configs() |
| 528 | + mocked_atomic.assert_not_called() |
| 529 | + |
| 530 | + with self.subTest("config.templates.add(template) raises error"): |
| 531 | + config = self._create_config(device=self._create_device(name="test-device")) |
| 532 | + template.required = True |
| 533 | + template.full_clean() |
| 534 | + template.save() |
| 535 | + with mock.patch.object( |
| 536 | + Config.templates.related_manager_cls, |
| 537 | + "add", |
| 538 | + side_effect=ValueError("Incompatible template"), |
| 539 | + ), mock.patch("logging.Logger.exception") as mocked_logger: |
| 540 | + template._auto_add_to_existing_configs() |
| 541 | + mocked_logger.assert_called_once_with( |
| 542 | + f"Failed to add template {template.pk} to config {config.pk}:" |
| 543 | + " Incompatible template" |
| 544 | + ) |
| 545 | + |
| 546 | + @mock.patch.object(task_logger, "warning") |
| 547 | + def test_auto_add_template_to_existing_configs_task_failure(self, mocked_warning): |
| 548 | + auto_add_template_to_existing_configs.delay(uuid.uuid4()) |
| 549 | + mocked_warning.assert_called_once() |
| 550 | + |
520 | 551 |
|
521 | 552 | class TestTemplateTransaction(
|
522 | 553 | CreateConfigTemplateMixin,
|
@@ -749,3 +780,153 @@ def test_task_timeout(self, mocked_update_related_config_status):
|
749 | 780 | template.save()
|
750 | 781 | mocked_error.assert_called_once()
|
751 | 782 | mocked_update_related_config_status.assert_called_once()
|
| 783 | + |
| 784 | + def _create_env_for_adding_template_to_existing_config(self): |
| 785 | + org1 = self._get_org() |
| 786 | + org1_config = self._create_config(organization=org1, status="applied") |
| 787 | + org2 = self._create_org(name="org2", slug="org2") |
| 788 | + org2_config = self._create_config(organization=org2, status="applied") |
| 789 | + org2_deactivating_config = self._create_config( |
| 790 | + status="deactivating", |
| 791 | + device=self._create_device( |
| 792 | + name="test-deactivating", |
| 793 | + mac_address="11:22:33:44:55:67", |
| 794 | + organization=org2, |
| 795 | + ), |
| 796 | + ) |
| 797 | + org2_deactivated_config = self._create_config( |
| 798 | + status="deactivated", |
| 799 | + device=self._create_device( |
| 800 | + name="test-deactivated", |
| 801 | + mac_address="11:22:33:44:55:68", |
| 802 | + organization=org2, |
| 803 | + ), |
| 804 | + ) |
| 805 | + return ( |
| 806 | + org1_config, |
| 807 | + org2_config, |
| 808 | + org2_deactivating_config, |
| 809 | + org2_deactivated_config, |
| 810 | + org1, |
| 811 | + org2, |
| 812 | + ) |
| 813 | + |
| 814 | + def _assert_template_added_to_existing_config( |
| 815 | + self, |
| 816 | + template, |
| 817 | + org1_config, |
| 818 | + org2_config, |
| 819 | + org2_deactivating_config, |
| 820 | + org2_deactivated_config, |
| 821 | + ): |
| 822 | + org1_config.refresh_from_db() |
| 823 | + self.assertEqual(org1_config.status, "modified") |
| 824 | + self.assertIn(template, org1_config.templates.all()) |
| 825 | + org2_config.refresh_from_db() |
| 826 | + self.assertEqual(org2_config.status, "modified") |
| 827 | + self.assertIn(template, org2_config.templates.all()) |
| 828 | + |
| 829 | + org2_deactivating_config.refresh_from_db() |
| 830 | + self.assertEqual(org2_deactivating_config.status, "deactivating") |
| 831 | + self.assertEqual(org2_deactivating_config.templates.count(), 0) |
| 832 | + |
| 833 | + org2_deactivated_config.refresh_from_db() |
| 834 | + self.assertEqual(org2_deactivated_config.status, "deactivated") |
| 835 | + self.assertEqual(org2_deactivated_config.templates.count(), 0) |
| 836 | + |
| 837 | + def test_required_template_added_to_existing_config(self): |
| 838 | + ( |
| 839 | + org1_config, |
| 840 | + org2_config, |
| 841 | + org2_deactivating_config, |
| 842 | + org2_deactivated_config, |
| 843 | + _, |
| 844 | + _, |
| 845 | + ) = self._create_env_for_adding_template_to_existing_config() |
| 846 | + shared_template = self._create_template( |
| 847 | + name="shared-template", required=True, default=True |
| 848 | + ) |
| 849 | + self._assert_template_added_to_existing_config( |
| 850 | + shared_template, |
| 851 | + org1_config, |
| 852 | + org2_config, |
| 853 | + org2_deactivating_config, |
| 854 | + org2_deactivated_config, |
| 855 | + ) |
| 856 | + |
| 857 | + def test_default_template_added_to_existing_config(self): |
| 858 | + ( |
| 859 | + org1_config, |
| 860 | + org2_config, |
| 861 | + org2_deactivating_config, |
| 862 | + org2_deactivated_config, |
| 863 | + _, |
| 864 | + _, |
| 865 | + ) = self._create_env_for_adding_template_to_existing_config() |
| 866 | + default_template = self._create_template(name="default-template", default=True) |
| 867 | + self._assert_template_added_to_existing_config( |
| 868 | + default_template, |
| 869 | + org1_config, |
| 870 | + org2_config, |
| 871 | + org2_deactivating_config, |
| 872 | + org2_deactivated_config, |
| 873 | + ) |
| 874 | + |
| 875 | + def test_update_existing_template_to_be_required(self): |
| 876 | + template = self._create_template( |
| 877 | + name="existing-template", required=False, default=False |
| 878 | + ) |
| 879 | + config = self._create_config(organization=self._get_org()) |
| 880 | + self.assertNotIn(template, config.templates.all()) |
| 881 | + |
| 882 | + template.required = True |
| 883 | + template.full_clean() |
| 884 | + template.save() |
| 885 | + |
| 886 | + config.refresh_from_db() |
| 887 | + self.assertEqual(config.status, "modified") |
| 888 | + self.assertIn(template, config.templates.all()) |
| 889 | + |
| 890 | + def test_update_existing_template_to_be_default(self): |
| 891 | + template = self._create_template( |
| 892 | + name="existing-template", required=False, default=False |
| 893 | + ) |
| 894 | + config = self._create_config(organization=self._get_org()) |
| 895 | + self.assertNotIn(template, config.templates.all()) |
| 896 | + |
| 897 | + template.default = True |
| 898 | + template.full_clean() |
| 899 | + template.save() |
| 900 | + |
| 901 | + config.refresh_from_db() |
| 902 | + self.assertEqual(config.status, "modified") |
| 903 | + self.assertIn(template, config.templates.all()) |
| 904 | + |
| 905 | + @mock.patch.object(auto_add_template_to_existing_configs, "delay") |
| 906 | + def test_auto_add_template_to_existing_configs_not_triggered(self, mocked_task): |
| 907 | + """ |
| 908 | + Ensure that auto_add_template_to_existing_configs task is not triggered |
| 909 | + when a required/default template is updated. |
| 910 | + """ |
| 911 | + with self.subTest("Updating default template does not trigger task"): |
| 912 | + default_template = self._create_template( |
| 913 | + name="default-template", default=True |
| 914 | + ) |
| 915 | + mocked_task.assert_called_once_with(str(default_template.pk)) |
| 916 | + mocked_task.reset_mock() |
| 917 | + |
| 918 | + default_template.config["interfaces"][0]["name"] = "eth1" |
| 919 | + default_template.full_clean() |
| 920 | + default_template.save() |
| 921 | + mocked_task.assert_not_called() |
| 922 | + |
| 923 | + with self.subTest("Updating required template does not trigger task"): |
| 924 | + required_template = self._create_template( |
| 925 | + name="required-template", required=True |
| 926 | + ) |
| 927 | + mocked_task.assert_called_once_with(str(required_template.pk)) |
| 928 | + mocked_task.reset_mock() |
| 929 | + required_template.config["interfaces"][0]["name"] = "eth1" |
| 930 | + required_template.full_clean() |
| 931 | + required_template.save() |
| 932 | + mocked_task.assert_not_called() |
0 commit comments