@@ -2287,6 +2287,171 @@ func Test_GetLatestRelease(t *testing.T) {
22872287 }
22882288}
22892289
2290+ func Test_GetReleaseByTag (t * testing.T ) {
2291+ mockClient := github .NewClient (nil )
2292+ tool , _ := GetReleaseByTag (stubGetClientFn (mockClient ), translations .NullTranslationHelper )
2293+ require .NoError (t , toolsnaps .Test (tool .Name , tool ))
2294+
2295+ assert .Equal (t , "get_release_by_tag" , tool .Name )
2296+ assert .NotEmpty (t , tool .Description )
2297+ assert .Contains (t , tool .InputSchema .Properties , "owner" )
2298+ assert .Contains (t , tool .InputSchema .Properties , "repo" )
2299+ assert .Contains (t , tool .InputSchema .Properties , "tag" )
2300+ assert .ElementsMatch (t , tool .InputSchema .Required , []string {"owner" , "repo" , "tag" })
2301+
2302+ mockRelease := & github.RepositoryRelease {
2303+ ID : github .Ptr (int64 (1 )),
2304+ TagName : github .Ptr ("v1.0.0" ),
2305+ Name : github .Ptr ("Release v1.0.0" ),
2306+ Body : github .Ptr ("This is the first stable release." ),
2307+ Assets : []* github.ReleaseAsset {
2308+ {
2309+ ID : github .Ptr (int64 (1 )),
2310+ Name : github .Ptr ("release-v1.0.0.tar.gz" ),
2311+ },
2312+ },
2313+ }
2314+
2315+ tests := []struct {
2316+ name string
2317+ mockedClient * http.Client
2318+ requestArgs map [string ]interface {}
2319+ expectError bool
2320+ expectedResult * github.RepositoryRelease
2321+ expectedErrMsg string
2322+ }{
2323+ {
2324+ name : "successful release by tag fetch" ,
2325+ mockedClient : mock .NewMockedHTTPClient (
2326+ mock .WithRequestMatch (
2327+ mock .GetReposReleasesTagsByOwnerByRepoByTag ,
2328+ mockRelease ,
2329+ ),
2330+ ),
2331+ requestArgs : map [string ]interface {}{
2332+ "owner" : "owner" ,
2333+ "repo" : "repo" ,
2334+ "tag" : "v1.0.0" ,
2335+ },
2336+ expectError : false ,
2337+ expectedResult : mockRelease ,
2338+ },
2339+ {
2340+ name : "missing owner parameter" ,
2341+ mockedClient : mock .NewMockedHTTPClient (),
2342+ requestArgs : map [string ]interface {}{
2343+ "repo" : "repo" ,
2344+ "tag" : "v1.0.0" ,
2345+ },
2346+ expectError : false , // Returns tool error, not Go error
2347+ expectedErrMsg : "missing required parameter: owner" ,
2348+ },
2349+ {
2350+ name : "missing repo parameter" ,
2351+ mockedClient : mock .NewMockedHTTPClient (),
2352+ requestArgs : map [string ]interface {}{
2353+ "owner" : "owner" ,
2354+ "tag" : "v1.0.0" ,
2355+ },
2356+ expectError : false , // Returns tool error, not Go error
2357+ expectedErrMsg : "missing required parameter: repo" ,
2358+ },
2359+ {
2360+ name : "missing tag parameter" ,
2361+ mockedClient : mock .NewMockedHTTPClient (),
2362+ requestArgs : map [string ]interface {}{
2363+ "owner" : "owner" ,
2364+ "repo" : "repo" ,
2365+ },
2366+ expectError : false , // Returns tool error, not Go error
2367+ expectedErrMsg : "missing required parameter: tag" ,
2368+ },
2369+ {
2370+ name : "release by tag not found" ,
2371+ mockedClient : mock .NewMockedHTTPClient (
2372+ mock .WithRequestMatchHandler (
2373+ mock .GetReposReleasesTagsByOwnerByRepoByTag ,
2374+ http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
2375+ w .WriteHeader (http .StatusNotFound )
2376+ _ , _ = w .Write ([]byte (`{"message": "Not Found"}` ))
2377+ }),
2378+ ),
2379+ ),
2380+ requestArgs : map [string ]interface {}{
2381+ "owner" : "owner" ,
2382+ "repo" : "repo" ,
2383+ "tag" : "v999.0.0" ,
2384+ },
2385+ expectError : false , // API errors return tool errors, not Go errors
2386+ expectedErrMsg : "failed to get release by tag: v999.0.0" ,
2387+ },
2388+ {
2389+ name : "server error" ,
2390+ mockedClient : mock .NewMockedHTTPClient (
2391+ mock .WithRequestMatchHandler (
2392+ mock .GetReposReleasesTagsByOwnerByRepoByTag ,
2393+ http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
2394+ w .WriteHeader (http .StatusInternalServerError )
2395+ _ , _ = w .Write ([]byte (`{"message": "Internal Server Error"}` ))
2396+ }),
2397+ ),
2398+ ),
2399+ requestArgs : map [string ]interface {}{
2400+ "owner" : "owner" ,
2401+ "repo" : "repo" ,
2402+ "tag" : "v1.0.0" ,
2403+ },
2404+ expectError : false , // API errors return tool errors, not Go errors
2405+ expectedErrMsg : "failed to get release by tag: v1.0.0" ,
2406+ },
2407+ }
2408+
2409+ for _ , tc := range tests {
2410+ t .Run (tc .name , func (t * testing.T ) {
2411+ client := github .NewClient (tc .mockedClient )
2412+ _ , handler := GetReleaseByTag (stubGetClientFn (client ), translations .NullTranslationHelper )
2413+
2414+ request := createMCPRequest (tc .requestArgs )
2415+
2416+ result , err := handler (context .Background (), request )
2417+
2418+ if tc .expectError {
2419+ require .Error (t , err )
2420+ assert .Contains (t , err .Error (), tc .expectedErrMsg )
2421+ return
2422+ }
2423+
2424+ require .NoError (t , err )
2425+
2426+ if tc .expectedErrMsg != "" {
2427+ require .True (t , result .IsError )
2428+ errorContent := getErrorResult (t , result )
2429+ assert .Contains (t , errorContent .Text , tc .expectedErrMsg )
2430+ return
2431+ }
2432+
2433+ require .False (t , result .IsError )
2434+
2435+ textContent := getTextResult (t , result )
2436+
2437+ var returnedRelease github.RepositoryRelease
2438+ err = json .Unmarshal ([]byte (textContent .Text ), & returnedRelease )
2439+ require .NoError (t , err )
2440+
2441+ assert .Equal (t , * tc .expectedResult .ID , * returnedRelease .ID )
2442+ assert .Equal (t , * tc .expectedResult .TagName , * returnedRelease .TagName )
2443+ assert .Equal (t , * tc .expectedResult .Name , * returnedRelease .Name )
2444+ if tc .expectedResult .Body != nil {
2445+ assert .Equal (t , * tc .expectedResult .Body , * returnedRelease .Body )
2446+ }
2447+ if len (tc .expectedResult .Assets ) > 0 {
2448+ require .Len (t , returnedRelease .Assets , len (tc .expectedResult .Assets ))
2449+ assert .Equal (t , * tc .expectedResult .Assets [0 ].Name , * returnedRelease .Assets [0 ].Name )
2450+ }
2451+ })
2452+ }
2453+ }
2454+
22902455func Test_filterPaths (t * testing.T ) {
22912456 tests := []struct {
22922457 name string
0 commit comments