@@ -400,134 +400,6 @@ def test_models_not_in_model_classes_skipped(self):
400400 mock_register .assert_not_called ()
401401
402402
403- # ---------------------------------------------------------------------------
404- # _build_add_links
405- # ---------------------------------------------------------------------------
406- class TestBuildAddLinks :
407- def test_returns_empty_when_url_cannot_be_reversed (self ):
408- from django .urls import NoReverseMatch
409-
410- from netbox_custom_objects_tab .views .typed import _build_add_links
411-
412- with patch ("netbox_custom_objects_tab.views.typed.reverse" , side_effect = NoReverseMatch ):
413- links = _build_add_links (
414- "server" ,
415- 42 ,
416- [("device_ref" , CustomFieldTypeChoices .TYPE_OBJECT , "Device Reference" )],
417- "/dcim/devices/42/custom-objects-server/" ,
418- )
419- assert links == []
420-
421- def test_single_field_builds_one_link_with_prefilled_querystring (self ):
422- from netbox_custom_objects_tab .views .typed import _build_add_links
423-
424- with patch (
425- "netbox_custom_objects_tab.views.typed.reverse" ,
426- return_value = "/plugins/custom-objects/server/add/" ,
427- ):
428- links = _build_add_links (
429- "server" ,
430- 42 ,
431- [("device_ref" , CustomFieldTypeChoices .TYPE_OBJECT , "Device Reference" )],
432- "/dcim/devices/42/custom-objects-server/" ,
433- )
434-
435- assert len (links ) == 1
436- link = links [0 ]
437- assert link ["field_name" ] == "device_ref"
438- assert link ["label" ] == "Device Reference"
439- assert link ["url" ].startswith ("/plugins/custom-objects/server/add/?" )
440- assert "device_ref=42" in link ["url" ]
441- # return_url must be URL-encoded (slashes -> %2F)
442- assert "return_url=%2Fdcim%2Fdevices%2F42%2Fcustom-objects-server%2F" in link ["url" ]
443-
444- def test_label_falls_back_to_field_name_when_label_missing (self ):
445- from netbox_custom_objects_tab .views .typed import _build_add_links
446-
447- with patch (
448- "netbox_custom_objects_tab.views.typed.reverse" ,
449- return_value = "/plugins/custom-objects/server/add/" ,
450- ):
451- # 2-tuple (no label) and 3-tuple with empty label -- both fall back to name
452- links = _build_add_links (
453- "server" ,
454- 7 ,
455- [
456- ("ref_a" , CustomFieldTypeChoices .TYPE_OBJECT ),
457- ("ref_b" , CustomFieldTypeChoices .TYPE_OBJECT , "" ),
458- ],
459- "/dcim/sites/7/custom-objects-server/" ,
460- )
461-
462- assert [link ["label" ] for link in links ] == ["ref_a" , "ref_b" ]
463-
464- def test_multiple_fields_produce_one_link_each (self ):
465- from netbox_custom_objects_tab .views .typed import _build_add_links
466-
467- with patch (
468- "netbox_custom_objects_tab.views.typed.reverse" ,
469- return_value = "/plugins/custom-objects/server/add/" ,
470- ):
471- links = _build_add_links (
472- "server" ,
473- 42 ,
474- [
475- ("primary_device" , CustomFieldTypeChoices .TYPE_OBJECT , "Primary Device" ),
476- ("backup_device" , CustomFieldTypeChoices .TYPE_OBJECT , "Backup Device" ),
477- ],
478- "/dcim/devices/42/custom-objects-server/" ,
479- )
480-
481- assert len (links ) == 2
482- assert {link ["field_name" ] for link in links } == {"primary_device" , "backup_device" }
483- assert {link ["label" ] for link in links } == {"Primary Device" , "Backup Device" }
484- urls = [link ["url" ] for link in links ]
485- assert any ("primary_device=42" in u for u in urls )
486- assert any ("backup_device=42" in u for u in urls )
487-
488- def test_duplicate_field_names_deduplicated (self ):
489- from netbox_custom_objects_tab .views .typed import _build_add_links
490-
491- with patch (
492- "netbox_custom_objects_tab.views.typed.reverse" ,
493- return_value = "/plugins/custom-objects/server/add/" ,
494- ):
495- links = _build_add_links (
496- "server" ,
497- 42 ,
498- [
499- ("ref" , CustomFieldTypeChoices .TYPE_OBJECT , "First" ),
500- ("ref" , CustomFieldTypeChoices .TYPE_MULTIOBJECT , "Second" ),
501- ],
502- "/dcim/devices/42/custom-objects-server/" ,
503- )
504-
505- assert len (links ) == 1
506- assert links [0 ]["label" ] == "First" # first occurrence wins
507-
508- def test_return_url_with_querystring_is_encoded_not_double_encoded (self ):
509- from netbox_custom_objects_tab .views .typed import _build_add_links
510-
511- return_url = "/dcim/devices/42/custom-objects-server/?tag=production&q=foo"
512- with patch (
513- "netbox_custom_objects_tab.views.typed.reverse" ,
514- return_value = "/plugins/custom-objects/server/add/" ,
515- ):
516- links = _build_add_links (
517- "server" ,
518- 42 ,
519- [("device_ref" , CustomFieldTypeChoices .TYPE_OBJECT , "Device" )],
520- return_url ,
521- )
522-
523- url = links [0 ]["url" ]
524- # No double-encoding: %25 would mean % got re-encoded
525- assert "%25" not in url
526- # The nested ? and & in return_url must be encoded as %3F and %26
527- assert "%3F" in url or "%3f" in url
528- assert "%26" in url
529-
530-
531403# ---------------------------------------------------------------------------
532404# register_typed_tabs -- label population + deterministic ordering
533405# ---------------------------------------------------------------------------
0 commit comments