@@ -778,3 +778,155 @@ func TestPathJoining(t *testing.T) {
778
778
})
779
779
}
780
780
}
781
+
782
+ func TestToolNameMapping (t * testing.T ) {
783
+ tests := []struct {
784
+ name string
785
+ operationId string
786
+ wantLen int
787
+ wantPrefix string
788
+ }{
789
+ {
790
+ name : "short operation ID" ,
791
+ operationId : "listPets" ,
792
+ wantLen : 8 ,
793
+ wantPrefix : "listPets" ,
794
+ },
795
+ {
796
+ name : "exactly 64 characters" ,
797
+ operationId : strings .Repeat ("a" , 64 ),
798
+ wantLen : 64 ,
799
+ wantPrefix : strings .Repeat ("a" , 64 ),
800
+ },
801
+ {
802
+ name : "long operation ID" ,
803
+ operationId : "thisIsAVeryLongOperationIdThatExceedsTheSixtyFourCharacterLimitAndNeedsToBeHandledProperly" ,
804
+ wantLen : 64 ,
805
+ wantPrefix : "thisIsAVeryLongOperationIdThatExceedsTheSixtyFourCharac" , // 55 chars
806
+ },
807
+ {
808
+ name : "multiple long IDs generate different names" ,
809
+ operationId : "anotherVeryLongOperationIdThatExceedsTheSixtyFourCharacterLimitAndNeedsToBeHandledProperly" ,
810
+ wantLen : 64 ,
811
+ wantPrefix : "anotherVeryLongOperationIdThatExceedsTheSixtyFourCharac" , // 55 chars
812
+ },
813
+ }
814
+
815
+ // Store generated names to check for uniqueness
816
+ generatedNames := make (map [string ]string )
817
+
818
+ for _ , tt := range tests {
819
+ t .Run (tt .name , func (t * testing.T ) {
820
+ // Get the tool name
821
+ toolName := getToolName (tt .operationId )
822
+
823
+ // Check length constraints
824
+ assert .Equal (t , tt .wantLen , len (toolName ), "tool name length mismatch" )
825
+
826
+ // For long names, verify the structure
827
+ if len (tt .operationId ) > 64 {
828
+ // Verify the prefix is exactly 55 characters
829
+ prefix := toolName [:55 ]
830
+ assert .Equal (t , tt .wantPrefix , prefix , "prefix mismatch" )
831
+
832
+ // Check that there's an underscore separator at position 55
833
+ assert .Equal (t , "_" , string (toolName [55 ]), "underscore separator not found at position 55" )
834
+
835
+ // Verify hash part length (should be 8 characters)
836
+ hash := toolName [56 :]
837
+ assert .Equal (t , 8 , len (hash ), "hash suffix should be 8 characters" )
838
+
839
+ // Verify the hash is URL-safe base64
840
+ for _ , c := range hash {
841
+ assert .Contains (t ,
842
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" ,
843
+ string (c ),
844
+ "hash should only contain URL-safe base64 characters" )
845
+ }
846
+ } else {
847
+ // For short names, verify exact match
848
+ assert .Equal (t , tt .wantPrefix , toolName , "tool name mismatch for short operation ID" )
849
+ }
850
+
851
+ // Check bijectivity - each operation ID should generate a unique tool name
852
+ if existing , exists := generatedNames [toolName ]; exists {
853
+ assert .Equal (t , tt .operationId , existing ,
854
+ "tool name collision detected: same name generated for different operation IDs" )
855
+ }
856
+ generatedNames [toolName ] = tt .operationId
857
+ })
858
+ }
859
+ }
860
+
861
+ func TestFindOperationByToolName (t * testing.T ) {
862
+ // Create a test spec with a mix of short and long operation IDs
863
+ longOpId := "thisIsAVeryLongOperationIdThatExceedsTheSixtyFourCharacterLimitAndNeedsToBeHandledProperly"
864
+ spec := fmt .Sprintf (`{
865
+ "openapi": "3.0.0",
866
+ "servers": [{"url": "https://api.example.com"}],
867
+ "paths": {
868
+ "/pets": {
869
+ "get": {
870
+ "operationId": "listPets",
871
+ "description": "List pets"
872
+ }
873
+ },
874
+ "/very/long/path": {
875
+ "post": {
876
+ "operationId": "%s",
877
+ "description": "Operation with long ID"
878
+ }
879
+ }
880
+ }
881
+ }` , longOpId )
882
+
883
+ server , err := NewServer (WithSpecData ([]byte (spec )))
884
+ require .NoError (t , err )
885
+
886
+ tests := []struct {
887
+ name string
888
+ toolName string
889
+ wantMethod string
890
+ wantPath string
891
+ wantFound bool
892
+ }{
893
+ {
894
+ name : "find short operation ID" ,
895
+ toolName : "listPets" ,
896
+ wantMethod : "GET" ,
897
+ wantPath : "/pets" ,
898
+ wantFound : true ,
899
+ },
900
+ {
901
+ name : "find long operation ID" ,
902
+ toolName : getToolName (longOpId ),
903
+ wantMethod : "POST" ,
904
+ wantPath : "/very/long/path" ,
905
+ wantFound : true ,
906
+ },
907
+ {
908
+ name : "operation not found" ,
909
+ toolName : "nonexistentOperation" ,
910
+ wantFound : false ,
911
+ },
912
+ }
913
+
914
+ for _ , tt := range tests {
915
+ t .Run (tt .name , func (t * testing.T ) {
916
+ method , path , operation , pathItem , found := server .findOperationByToolName (tt .toolName )
917
+
918
+ assert .Equal (t , tt .wantFound , found )
919
+ if tt .wantFound {
920
+ assert .Equal (t , tt .wantMethod , method )
921
+ assert .Equal (t , tt .wantPath , path )
922
+ assert .NotNil (t , operation )
923
+ assert .NotNil (t , pathItem )
924
+ } else {
925
+ assert .Empty (t , method )
926
+ assert .Empty (t , path )
927
+ assert .Nil (t , operation )
928
+ assert .Nil (t , pathItem )
929
+ }
930
+ })
931
+ }
932
+ }
0 commit comments