import unittest from unittest.mock import MagicMock, patch from netbox_zabbix_sync.modules.device import PhysicalDevice from netbox_zabbix_sync.modules.usermacros import ZabbixUsermacros class DummyNB: def __init__(self, name="dummy", config_context=None, **kwargs): self.name = name self.config_context = config_context or {} for k, v in kwargs.items(): setattr(self, k, v) def __getitem__(self, key): return getattr(self, key) class TestUsermacroSync(unittest.TestCase): def setUp(self): self.nb = DummyNB(serial="1234") self.logger = MagicMock() self.usermacro_map = {"serial": "{$HW_SERIAL}"} def create_mock_device(self, config=None): """Helper method to create a properly mocked PhysicalDevice""" # Mock the NetBox device with all required attributes mock_nb = MagicMock() mock_nb.id = 1 mock_nb.name = "dummy" mock_nb.status.label = "Active" mock_nb.tenant = None mock_nb.config_context = {} mock_nb.primary_ip.address = "192.168.1.1/24" mock_nb.custom_fields = {"zabbix_hostid": None} device_config = config if config is not None else {"device_cf": "zabbix_hostid"} # Create device with proper initialization device = PhysicalDevice( nb=mock_nb, zabbix=MagicMock(), nb_journal_class=MagicMock(), nb_version="3.0", logger=self.logger, config=device_config, ) return device @patch.object(PhysicalDevice, "_usermacro_map") def test_usermacro_sync_false(self, mock_usermacro_map): mock_usermacro_map.return_value = self.usermacro_map device = self.create_mock_device( config={ "usermacro_sync": False, "device_cf": "zabbix_hostid", "tag_sync": False, } ) # Call set_usermacros result = device.set_usermacros() self.assertEqual(device.usermacros, []) self.assertTrue(result is True or result is None) @patch("netbox_zabbix_sync.modules.device.ZabbixUsermacros") @patch.object(PhysicalDevice, "_usermacro_map") def test_usermacro_sync_true(self, mock_usermacro_map, mock_usermacros_class): mock_usermacro_map.return_value = self.usermacro_map # Mock the ZabbixUsermacros class to return some test data mock_macros_instance = MagicMock() mock_macros_instance.sync = True # This is important - sync must be True mock_macros_instance.generate.return_value = [ {"macro": "{$HW_SERIAL}", "value": "1234"} ] mock_usermacros_class.return_value = mock_macros_instance device = self.create_mock_device( config={ "usermacro_sync": True, "device_cf": "zabbix_hostid", "tag_sync": False, } ) # Call set_usermacros device.set_usermacros() self.assertIsInstance(device.usermacros, list) self.assertGreater(len(device.usermacros), 0) @patch("netbox_zabbix_sync.modules.device.ZabbixUsermacros") @patch.object(PhysicalDevice, "_usermacro_map") def test_usermacro_sync_full(self, mock_usermacro_map, mock_usermacros_class): mock_usermacro_map.return_value = self.usermacro_map # Mock the ZabbixUsermacros class to return some test data mock_macros_instance = MagicMock() mock_macros_instance.sync = True # This is important - sync must be True mock_macros_instance.generate.return_value = [ {"macro": "{$HW_SERIAL}", "value": "1234"} ] mock_usermacros_class.return_value = mock_macros_instance device = self.create_mock_device( config={ "usermacro_sync": "full", "device_cf": "zabbix_hostid", "tag_sync": False, } ) # Call set_usermacros device.set_usermacros() self.assertIsInstance(device.usermacros, list) self.assertGreater(len(device.usermacros), 0) class TestZabbixUsermacros(unittest.TestCase): def setUp(self): self.nb = DummyNB() self.logger = MagicMock() def test_validate_macro_valid(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) self.assertTrue(macros.validate_macro("{$TEST_MACRO}")) self.assertTrue(macros.validate_macro("{$A1_2.3}")) self.assertTrue(macros.validate_macro("{$FOO:bar}")) def test_validate_macro_invalid(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) self.assertFalse(macros.validate_macro("$TEST_MACRO")) self.assertFalse(macros.validate_macro("{TEST_MACRO}")) self.assertFalse(macros.validate_macro("{$test}")) # lower-case not allowed self.assertFalse(macros.validate_macro("")) def test_render_macro_dict(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) macro = macros.render_macro( "{$FOO}", {"value": "bar", "type": "secret", "description": "desc"} ) self.assertEqual(macro["macro"], "{$FOO}") self.assertEqual(macro["value"], "bar") self.assertEqual(macro["type"], "1") self.assertEqual(macro["description"], "desc") def test_render_macro_dict_missing_value(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) result = macros.render_macro("{$FOO}", {"type": "text"}) self.assertFalse(result) self.logger.info.assert_called() def test_render_macro_str(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) macro = macros.render_macro("{$FOO}", "bar") self.assertEqual(macro["macro"], "{$FOO}") self.assertEqual(macro["value"], "bar") self.assertEqual(macro["type"], "0") self.assertEqual(macro["description"], "") def test_render_macro_invalid_name(self): macros = ZabbixUsermacros(self.nb, {}, False, logger=self.logger) result = macros.render_macro("FOO", "bar") self.assertFalse(result) self.logger.warning.assert_called() def test_generate_from_map(self): nb = DummyNB(memory="bar", role="baz") usermacro_map = {"memory": "{$FOO}", "role": "{$BAR}"} macros = ZabbixUsermacros(nb, usermacro_map, True, logger=self.logger) result = macros.generate() self.assertEqual(len(result), 2) self.assertEqual(result[0]["macro"], "{$FOO}") self.assertEqual(result[1]["macro"], "{$BAR}") def test_generate_from_config_context(self): config_context = {"zabbix": {"usermacros": {"{$TEST_MACRO}": "test_value"}}} nb = DummyNB(config_context=config_context) macros = ZabbixUsermacros(nb, {}, True, logger=self.logger) result = macros.generate() self.assertEqual(len(result), 1) self.assertEqual(result[0]["macro"], "{$TEST_MACRO}") self.assertEqual(result[0]["value"], "test_value") if __name__ == "__main__": unittest.main()