5454Group = load_model ("openwisp_users" , "Group" )
5555
5656
57+ class TestDeviceAdminMixin :
58+ _device_params = {
59+ "name" : "test-device" ,
60+ "hardware_id" : "1234" ,
61+ "mac_address" : CreateConfigTemplateMixin .TEST_MAC_ADDRESS ,
62+ "key" : CreateConfigTemplateMixin .TEST_KEY ,
63+ "model" : "" ,
64+ "os" : "" ,
65+ "notes" : "" ,
66+ "config-0-id" : "" ,
67+ "config-0-device" : "" ,
68+ "config-0-backend" : "netjsonconfig.OpenWrt" ,
69+ "config-0-templates" : "" ,
70+ "config-0-config" : json .dumps ({}),
71+ "config-0-context" : "" ,
72+ "config-TOTAL_FORMS" : 1 ,
73+ "config-INITIAL_FORMS" : 0 ,
74+ "config-MIN_NUM_FORMS" : 0 ,
75+ "config-MAX_NUM_FORMS" : 1 ,
76+ # openwisp_controller.connection
77+ "deviceconnection_set-TOTAL_FORMS" : 0 ,
78+ "deviceconnection_set-INITIAL_FORMS" : 0 ,
79+ "deviceconnection_set-MIN_NUM_FORMS" : 0 ,
80+ "deviceconnection_set-MAX_NUM_FORMS" : 1000 ,
81+ "command_set-TOTAL_FORMS" : 0 ,
82+ "command_set-INITIAL_FORMS" : 0 ,
83+ "command_set-MIN_NUM_FORMS" : 0 ,
84+ "command_set-MAX_NUM_FORMS" : 1000 ,
85+ }
86+ # WARNING - WATCHOUT
87+ # this class attribute is changed dynamically
88+ # by other apps which add inlines to DeviceAdmin
89+ _additional_params = {}
90+
91+ def _get_device_params (self , org ):
92+ p = self ._device_params .copy ()
93+ p .update (self ._additional_params )
94+ p ["organization" ] = org .pk
95+ return p
96+
97+
5798class TestImportExportMixin :
5899 """
59100 Reused in OpenWISP Monitoring
@@ -237,6 +278,7 @@ class TestAdmin(
237278 CreateDeviceGroupMixin ,
238279 CreateConfigTemplateMixin ,
239280 TestVpnX509Mixin ,
281+ TestDeviceAdminMixin ,
240282 TestAdminMixin ,
241283 TestCase ,
242284):
@@ -278,10 +320,6 @@ class TestAdmin(
278320 "command_set-MIN_NUM_FORMS" : 0 ,
279321 "command_set-MAX_NUM_FORMS" : 1000 ,
280322 }
281- # WARNING - WATCHOUT
282- # this class attribute is changed dynamically
283- # by other apps which add inlines to DeviceAdmin
284- _additional_params = {}
285323
286324 def setUp (self ):
287325 self .client .force_login (self ._get_admin ())
@@ -291,12 +329,6 @@ def tearDownClass(cls):
291329 super ().tearDownClass ()
292330 devnull .close ()
293331
294- def _get_device_params (self , org ):
295- p = self ._device_params .copy ()
296- p .update (self ._additional_params )
297- p ["organization" ] = org .pk
298- return p
299-
300332 def test_device_and_template_different_organization (self ):
301333 org1 = self ._get_org ()
302334 template = self ._create_template (organization = org1 )
@@ -2251,6 +2283,7 @@ def test_templates_fetch_queries_10(self):
22512283class TestTransactionAdmin (
22522284 CreateConfigTemplateMixin ,
22532285 TestAdminMixin ,
2286+ TestDeviceAdminMixin ,
22542287 TransactionTestCase ,
22552288):
22562289 app_label = "config"
@@ -2569,6 +2602,136 @@ def test_restoring_template_sends_config_modified(self):
25692602 self .assertEqual (config .status , "modified" )
25702603 self .assertNotEqual (config .checksum , config_checksum )
25712604
2605+ def test_config_modified_signal (self ):
2606+ """
2607+ Verifies multiple config_modified signal is not send for
2608+ a single change
2609+ """
2610+ template1 = self ._create_template (
2611+ default_values = {"ssid" : "OpenWISP" }, default = True
2612+ )
2613+ template2 = self ._create_template (
2614+ name = "template2" ,
2615+ config = {"interfaces" : [{"name" : "{{ ifname }}" , "type" : "ethernet" }]},
2616+ default_values = {"ifname" : "eth1" },
2617+ default = True ,
2618+ )
2619+ path = reverse (f"admin:{ self .app_label } _device_add" )
2620+ params = self ._get_device_params (org = self ._get_org ())
2621+ params .update ({"config-0-templates" : f"{ template1 .pk } ,{ template2 .pk } " })
2622+ response = self .client .post (path , data = params , follow = True )
2623+ self .assertEqual (response .status_code , 200 )
2624+
2625+ config = Device .objects .get (name = params ["name" ]).config
2626+ self .assertEqual (config .templates .count (), 2 )
2627+ path = reverse (f"admin:{ self .app_label } _device_change" , args = [config .device_id ])
2628+ params .update (
2629+ {
2630+ "config-0-id" : str (config .pk ),
2631+ "config-0-device" : str (config .device_id ),
2632+ "config-INITIAL_FORMS" : 1 ,
2633+ "_continue" : True ,
2634+ }
2635+ )
2636+
2637+ config ._delete_config_modified_timeout_cache ()
2638+ with self .subTest (
2639+ "Updating the unused context variable does not send config_modified signal"
2640+ ):
2641+ with patch (
2642+ "openwisp_controller.config.signals.config_modified.send"
2643+ ) as mocked_signal :
2644+ params .update (
2645+ {
2646+ "config-0-context" : json .dumps (
2647+ {"ssid" : "Updated" , "ifname" : "eth1" }
2648+ )
2649+ }
2650+ )
2651+ response = self .client .post (path , data = params , follow = True )
2652+ self .assertEqual (response .status_code , 200 )
2653+ mocked_signal .assert_not_called ()
2654+ config .refresh_from_db ()
2655+ self .assertEqual (config .context ["ssid" ], "Updated" )
2656+
2657+ config ._delete_config_modified_timeout_cache ()
2658+ with self .subTest (
2659+ "Changing used context variable sends config_modified signal"
2660+ ):
2661+ with patch (
2662+ "openwisp_controller.config.signals.config_modified.send"
2663+ ) as mocked_signal :
2664+ params .update (
2665+ {
2666+ "config-0-context" : json .dumps (
2667+ {"ssid" : "Updated" , "ifname" : "eth2" }
2668+ )
2669+ }
2670+ )
2671+ response = self .client .post (path , data = params , follow = True )
2672+ self .assertEqual (response .status_code , 200 )
2673+
2674+ config .refresh_from_db ()
2675+ self .assertEqual (
2676+ config .context ["ssid" ],
2677+ "Updated" ,
2678+ )
2679+ self .assertEqual (config .status , "modified" )
2680+ mocked_signal .assert_called_once ()
2681+
2682+ config ._delete_config_modified_timeout_cache ()
2683+ with self .subTest ("Changing device configuration sends config_modified signal" ):
2684+ with patch (
2685+ "openwisp_controller.config.signals.config_modified.send"
2686+ ) as mocked_signal :
2687+ params .update (
2688+ {
2689+ "config-0-config" : json .dumps (
2690+ {"interfaces" : [{"name" : "eth5" , "type" : "ethernet" }]}
2691+ )
2692+ }
2693+ )
2694+ response = self .client .post (path , data = params , follow = True )
2695+ self .assertEqual (response .status_code , 200 )
2696+ config .refresh_from_db ()
2697+ self .assertEqual (
2698+ config .config ["interfaces" ][0 ]["name" ],
2699+ "eth5" ,
2700+ )
2701+ self .assertEqual (config .status , "modified" )
2702+ mocked_signal .assert_called_once ()
2703+
2704+ config ._delete_config_modified_timeout_cache ()
2705+ with self .subTest ("Changing applied template sends config_modified signal" ):
2706+ with patch (
2707+ "openwisp_controller.config.signals.config_modified.send"
2708+ ) as mocked_signal :
2709+ response = self .client .post (
2710+ reverse (
2711+ f"admin:{ self .app_label } _template_change" , args = [template2 .pk ]
2712+ ),
2713+ data = {
2714+ "name" : template2 .name ,
2715+ "organization" : "" ,
2716+ "type" : template2 .type ,
2717+ "backend" : template2 .backend ,
2718+ "config" : json .dumps (template2 .config ),
2719+ "default_values" : json .dumps (
2720+ {
2721+ "ifname" : "eth3" ,
2722+ }
2723+ ),
2724+ "required" : False ,
2725+ "default" : True ,
2726+ "_continue" : True ,
2727+ },
2728+ follow = True ,
2729+ )
2730+ self .assertEqual (response .status_code , 200 )
2731+ template2 .refresh_from_db ()
2732+ self .assertEqual (template2 .default_values ["ifname" ], "eth3" )
2733+ mocked_signal .assert_called_once ()
2734+
25722735
25732736class TestDeviceGroupAdmin (
25742737 CreateDeviceGroupMixin ,
0 commit comments