@@ -691,6 +691,119 @@ func TestGetExtensionSetWithMissingAnchor(t *testing.T) {
691691	assert .Contains (t , err .Error (), "invalid plan" )
692692}
693693
694+ // TestGetExtensionSetWithInvalidURN tests that URN references to non-existent URNs are rejected 
695+ func  TestGetExtensionSetWithInvalidURN (t  * testing.T ) {
696+ 	// Create a plan with a URN that doesn't exist in the collection 
697+ 	plan  :=  & proto.Plan {
698+ 		ExtensionUrns : []* extensionspb.SimpleExtensionURN {
699+ 			{
700+ 				ExtensionUrnAnchor : 1 ,
701+ 				Urn :                "extension:nonexistent:extension" ,
702+ 			},
703+ 		},
704+ 		Extensions : []* extensionspb.SimpleExtensionDeclaration {
705+ 			{
706+ 				MappingType : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction_ {
707+ 					ExtensionFunction : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction {
708+ 						ExtensionUrnReference : 1 ,
709+ 						FunctionAnchor :        10 ,
710+ 						Name :                  "nonexistent_function" ,
711+ 					},
712+ 				},
713+ 			},
714+ 		},
715+ 		Relations : []* proto.PlanRel {},
716+ 	}
717+ 
718+ 	collection  :=  extensions .GetDefaultCollectionWithNoError ()
719+ 	extSet , err  :=  extensions .GetExtensionSet (plan , collection )
720+ 	require .Error (t , err )
721+ 	require .Nil (t , extSet )
722+ 	assert .Contains (t , err .Error (), "not found" )
723+ 	assert .Contains (t , err .Error (), "extension:nonexistent:extension" )
724+ }
725+ 
726+ // TestExtensionValidationEdgeCases tests various edge cases in extension validation 
727+ func  TestExtensionValidationEdgeCases (t  * testing.T ) {
728+ 	collection  :=  extensions .GetDefaultCollectionWithNoError ()
729+ 
730+ 	t .Run ("empty URN string" , func (t  * testing.T ) {
731+ 		plan  :=  & proto.Plan {
732+ 			ExtensionUrns : []* extensionspb.SimpleExtensionURN {
733+ 				{ExtensionUrnAnchor : 1 , Urn : "" }, // Empty URN 
734+ 			},
735+ 			Extensions : []* extensionspb.SimpleExtensionDeclaration {
736+ 				{
737+ 					MappingType : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction_ {
738+ 						ExtensionFunction : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction {
739+ 							ExtensionUrnReference : 1 ,
740+ 							FunctionAnchor :        10 ,
741+ 							Name :                  "test_function" ,
742+ 						},
743+ 					},
744+ 				},
745+ 			},
746+ 		}
747+ 
748+ 		_ , err  :=  extensions .GetExtensionSet (plan , collection )
749+ 		require .Error (t , err )
750+ 		assert .Contains (t , err .Error (), "not found" )
751+ 	})
752+ 
753+ 	t .Run ("zero URN reference" , func (t  * testing.T ) {
754+ 		plan  :=  & proto.Plan {
755+ 			Extensions : []* extensionspb.SimpleExtensionDeclaration {
756+ 				{
757+ 					MappingType : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction_ {
758+ 						ExtensionFunction : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction {
759+ 							ExtensionUrnReference : 0 , // Zero reference - should be treated as "no reference" 
760+ 							ExtensionUriReference : 0 , // Both zero 
761+ 							FunctionAnchor :        10 ,
762+ 							Name :                  "test_function" ,
763+ 						},
764+ 					},
765+ 				},
766+ 			},
767+ 		}
768+ 
769+ 		_ , err  :=  extensions .GetExtensionSet (plan , collection )
770+ 		require .Error (t , err )
771+ 		assert .Contains (t , err .Error (), "no URN or URI reference provided" )
772+ 	})
773+ 
774+ 	t .Run ("both URI and URN resolve to same extension" , func (t  * testing.T ) {
775+ 		// This documents that when both URI and URN are provided, URN takes precedence 
776+ 		plan  :=  & proto.Plan {
777+ 			ExtensionUris : []* extensionspb.SimpleExtensionURI {
778+ 				{ExtensionUriAnchor : 1 , Uri : "https://github.com/substrait-io/substrait/blob/main/extensions/functions_arithmetic.yaml" },
779+ 			},
780+ 			ExtensionUrns : []* extensionspb.SimpleExtensionURN {
781+ 				{ExtensionUrnAnchor : 1 , Urn : "extension:io.substrait:functions_arithmetic" },
782+ 			},
783+ 			Extensions : []* extensionspb.SimpleExtensionDeclaration {
784+ 				{
785+ 					MappingType : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction_ {
786+ 						ExtensionFunction : & extensionspb.SimpleExtensionDeclaration_ExtensionFunction {
787+ 							ExtensionUriReference : 1 , // Points to arithmetic functions 
788+ 							ExtensionUrnReference : 1 , // Also points to arithmetic functions (same extension) 
789+ 							FunctionAnchor :        10 ,
790+ 							Name :                  "add:i32_i32" ,
791+ 						},
792+ 					},
793+ 				},
794+ 			},
795+ 		}
796+ 
797+ 		extSet , err  :=  extensions .GetExtensionSet (plan , collection )
798+ 		require .NoError (t , err )
799+ 
800+ 		id , ok  :=  extSet .DecodeFunc (10 )
801+ 		require .True (t , ok )
802+ 		assert .Equal (t , "extension:io.substrait:functions_arithmetic" , id .URN )
803+ 		assert .Equal (t , "add:i32_i32" , id .Name )
804+ 	})
805+ }
806+ 
694807func  TestToProtoPopulatesBothURNAndURI (t  * testing.T ) {
695808	c  :=  & extensions.Collection {}
696809	err  :=  c .Load ("some/uri" , strings .NewReader (sampleYAML ))
0 commit comments