@@ -269,7 +269,8 @@ func TestBodySetError(t *testing.T) {
269269 })
270270
271271 t .Run ("invalid path" , func (t * testing.T ) {
272- body := Body {}.Set ("..invalid" , "value" )
272+ // Empty path should error - xmldot rejects empty paths
273+ body := Body {}.Set ("" , "value" )
273274 _ , err := body .String ()
274275 if err == nil {
275276 t .Error ("expected error for invalid path" )
@@ -348,7 +349,7 @@ func TestBodySetAttrError(t *testing.T) {
348349 t .Run ("SetAttr on invalid path" , func (t * testing.T ) {
349350 body := Body {}.
350351 Set ("config.interface" , "test" ).
351- SetAttr ("invalid..path " , "attr" , "value" )
352+ SetAttr ("" , "attr" , "value" ) // Empty path should error
352353
353354 _ , err := body .String ()
354355 if err == nil {
@@ -525,3 +526,74 @@ func ExampleBody_Set() {
525526 // Output: Configuration built successfully
526527 // XML length: 200 bytes
527528}
529+
530+ // TestBodySetMultipleRoots tests that Body.Set() properly handles multiple root-level sibling elements
531+ func TestBodySetMultipleRoots (t * testing.T ) {
532+ t .Run ("ACL with deny and permit siblings" , func (t * testing.T ) {
533+ // This is the real-world use case from terraform-provider-iosxe ACLs
534+ // where we have both deny and permit at the same level
535+ body := Body {}.
536+ Set ("sequence" , "10" ).
537+ Set ("deny.std-ace.ipv4-address-prefix" , "10.0.0.0" ).
538+ Set ("deny.std-ace.mask" , "0.0.0.255" ).
539+ Set ("permit.std-ace.ipv4-address-prefix" , "192.168.0.0" ).
540+ Set ("permit.std-ace.mask" , "0.0.255.255" )
541+
542+ xml , err := body .String ()
543+ if err != nil {
544+ t .Fatalf ("unexpected error: %v" , err )
545+ }
546+
547+ t .Logf ("Generated XML:\n %s" , xml )
548+
549+ // Verify we have <sequence>, <deny>, and <permit> as separate root-level siblings
550+ if ! strings .Contains (xml , "<sequence>10</sequence>" ) {
551+ t .Errorf ("expected separate <sequence> element, got: %s" , xml )
552+ }
553+ if ! strings .Contains (xml , "<deny>" ) {
554+ t .Errorf ("expected <deny> element, got: %s" , xml )
555+ }
556+ if ! strings .Contains (xml , "<permit>" ) {
557+ t .Errorf ("expected <permit> element, got: %s" , xml )
558+ }
559+ if ! strings .Contains (xml , "<ipv4-address-prefix>10.0.0.0</ipv4-address-prefix>" ) {
560+ t .Errorf ("expected deny prefix in XML, got: %s" , xml )
561+ }
562+ if ! strings .Contains (xml , "<ipv4-address-prefix>192.168.0.0</ipv4-address-prefix>" ) {
563+ t .Errorf ("expected permit prefix in XML, got: %s" , xml )
564+ }
565+
566+ // Verify it's NOT all nested inside <sequence> or duplicated
567+ // The XML should have the structure:
568+ // <sequence>10</sequence><deny>...</deny><permit>...</permit>
569+ // NOT: <sequence>10<deny>...</deny><permit>...</permit></sequence>
570+ if strings .Contains (xml , "<sequence>10<deny>" ) || strings .Contains (xml , "</deny></sequence>" ) {
571+ t .Errorf ("deny should not be nested inside sequence, got: %s" , xml )
572+ }
573+ })
574+
575+ t .Run ("multiple different root elements" , func (t * testing.T ) {
576+ body := Body {}.
577+ Set ("hostname" , "router1" ).
578+ Set ("domain" , "example.com" ).
579+ Set ("port" , "8080" )
580+
581+ xml , err := body .String ()
582+ if err != nil {
583+ t .Fatalf ("unexpected error: %v" , err )
584+ }
585+
586+ t .Logf ("Generated XML:\n %s" , xml )
587+
588+ // All should be separate root elements
589+ if ! strings .Contains (xml , "<hostname>router1</hostname>" ) {
590+ t .Errorf ("expected hostname element, got: %s" , xml )
591+ }
592+ if ! strings .Contains (xml , "<domain>example.com</domain>" ) {
593+ t .Errorf ("expected domain element, got: %s" , xml )
594+ }
595+ if ! strings .Contains (xml , "<port>8080</port>" ) {
596+ t .Errorf ("expected port element, got: %s" , xml )
597+ }
598+ })
599+ }
0 commit comments