|
5 | 5 | "encoding/json"
|
6 | 6 | "fmt"
|
7 | 7 | "net/http"
|
| 8 | + "strings" |
8 | 9 | "testing"
|
9 | 10 | "time"
|
10 | 11 |
|
@@ -2732,3 +2733,146 @@ func Test_ReprioritizeSubIssue(t *testing.T) {
|
2732 | 2733 | })
|
2733 | 2734 | }
|
2734 | 2735 | }
|
| 2736 | + |
| 2737 | +func Test_ListIssueTypes(t *testing.T) { |
| 2738 | + // Verify tool definition once |
| 2739 | + mockClient := github.NewClient(nil) |
| 2740 | + tool, _ := ListIssueTypes(stubGetClientFn(mockClient), translations.NullTranslationHelper) |
| 2741 | + require.NoError(t, toolsnaps.Test(tool.Name, tool)) |
| 2742 | + |
| 2743 | + assert.Equal(t, "list_issue_types", tool.Name) |
| 2744 | + assert.NotEmpty(t, tool.Description) |
| 2745 | + assert.Contains(t, tool.InputSchema.Properties, "owner") |
| 2746 | + assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner"}) |
| 2747 | + |
| 2748 | + // Setup mock issue types for success case |
| 2749 | + mockIssueTypes := []*github.IssueType{ |
| 2750 | + { |
| 2751 | + ID: github.Ptr(int64(1)), |
| 2752 | + Name: github.Ptr("bug"), |
| 2753 | + Description: github.Ptr("Something isn't working"), |
| 2754 | + Color: github.Ptr("d73a4a"), |
| 2755 | + }, |
| 2756 | + { |
| 2757 | + ID: github.Ptr(int64(2)), |
| 2758 | + Name: github.Ptr("feature"), |
| 2759 | + Description: github.Ptr("New feature or enhancement"), |
| 2760 | + Color: github.Ptr("a2eeef"), |
| 2761 | + }, |
| 2762 | + } |
| 2763 | + |
| 2764 | + tests := []struct { |
| 2765 | + name string |
| 2766 | + mockedClient *http.Client |
| 2767 | + requestArgs map[string]interface{} |
| 2768 | + expectError bool |
| 2769 | + expectedIssueTypes []*github.IssueType |
| 2770 | + expectedErrMsg string |
| 2771 | + }{ |
| 2772 | + { |
| 2773 | + name: "successful issue types retrieval", |
| 2774 | + mockedClient: mock.NewMockedHTTPClient( |
| 2775 | + mock.WithRequestMatchHandler( |
| 2776 | + mock.EndpointPattern{ |
| 2777 | + Pattern: "/orgs/testorg/issue-types", |
| 2778 | + Method: "GET", |
| 2779 | + }, |
| 2780 | + mockResponse(t, http.StatusOK, mockIssueTypes), |
| 2781 | + ), |
| 2782 | + ), |
| 2783 | + requestArgs: map[string]interface{}{ |
| 2784 | + "owner": "testorg", |
| 2785 | + }, |
| 2786 | + expectError: false, |
| 2787 | + expectedIssueTypes: mockIssueTypes, |
| 2788 | + }, |
| 2789 | + { |
| 2790 | + name: "organization not found", |
| 2791 | + mockedClient: mock.NewMockedHTTPClient( |
| 2792 | + mock.WithRequestMatchHandler( |
| 2793 | + mock.EndpointPattern{ |
| 2794 | + Pattern: "/orgs/nonexistent/issue-types", |
| 2795 | + Method: "GET", |
| 2796 | + }, |
| 2797 | + mockResponse(t, http.StatusNotFound, `{"message": "Organization not found"}`), |
| 2798 | + ), |
| 2799 | + ), |
| 2800 | + requestArgs: map[string]interface{}{ |
| 2801 | + "owner": "nonexistent", |
| 2802 | + }, |
| 2803 | + expectError: true, |
| 2804 | + expectedErrMsg: "failed to list issue types", |
| 2805 | + }, |
| 2806 | + { |
| 2807 | + name: "missing owner parameter", |
| 2808 | + mockedClient: mock.NewMockedHTTPClient( |
| 2809 | + mock.WithRequestMatchHandler( |
| 2810 | + mock.EndpointPattern{ |
| 2811 | + Pattern: "/orgs/testorg/issue-types", |
| 2812 | + Method: "GET", |
| 2813 | + }, |
| 2814 | + mockResponse(t, http.StatusOK, mockIssueTypes), |
| 2815 | + ), |
| 2816 | + ), |
| 2817 | + requestArgs: map[string]interface{}{}, |
| 2818 | + expectError: false, // This should be handled by parameter validation, error returned in result |
| 2819 | + expectedErrMsg: "missing required parameter: owner", |
| 2820 | + }, |
| 2821 | + } |
| 2822 | + |
| 2823 | + for _, tc := range tests { |
| 2824 | + t.Run(tc.name, func(t *testing.T) { |
| 2825 | + // Setup client with mock |
| 2826 | + client := github.NewClient(tc.mockedClient) |
| 2827 | + _, handler := ListIssueTypes(stubGetClientFn(client), translations.NullTranslationHelper) |
| 2828 | + |
| 2829 | + // Create call request |
| 2830 | + request := createMCPRequest(tc.requestArgs) |
| 2831 | + |
| 2832 | + // Call handler |
| 2833 | + result, err := handler(context.Background(), request) |
| 2834 | + |
| 2835 | + // Verify results |
| 2836 | + if tc.expectError { |
| 2837 | + if err != nil { |
| 2838 | + assert.Contains(t, err.Error(), tc.expectedErrMsg) |
| 2839 | + return |
| 2840 | + } |
| 2841 | + // Check if error is returned as tool result error |
| 2842 | + require.NotNil(t, result) |
| 2843 | + require.True(t, result.IsError) |
| 2844 | + errorContent := getErrorResult(t, result) |
| 2845 | + assert.Contains(t, errorContent.Text, tc.expectedErrMsg) |
| 2846 | + return |
| 2847 | + } |
| 2848 | + |
| 2849 | + // Check if it's a parameter validation error (returned as tool result error) |
| 2850 | + if result != nil && result.IsError { |
| 2851 | + errorContent := getErrorResult(t, result) |
| 2852 | + if tc.expectedErrMsg != "" && strings.Contains(errorContent.Text, tc.expectedErrMsg) { |
| 2853 | + return // This is expected for parameter validation errors |
| 2854 | + } |
| 2855 | + } |
| 2856 | + |
| 2857 | + require.NoError(t, err) |
| 2858 | + require.NotNil(t, result) |
| 2859 | + require.False(t, result.IsError) |
| 2860 | + textContent := getTextResult(t, result) |
| 2861 | + |
| 2862 | + // Unmarshal and verify the result |
| 2863 | + var returnedIssueTypes []*github.IssueType |
| 2864 | + err = json.Unmarshal([]byte(textContent.Text), &returnedIssueTypes) |
| 2865 | + require.NoError(t, err) |
| 2866 | + |
| 2867 | + if tc.expectedIssueTypes != nil { |
| 2868 | + require.Equal(t, len(tc.expectedIssueTypes), len(returnedIssueTypes)) |
| 2869 | + for i, expected := range tc.expectedIssueTypes { |
| 2870 | + assert.Equal(t, *expected.Name, *returnedIssueTypes[i].Name) |
| 2871 | + assert.Equal(t, *expected.Description, *returnedIssueTypes[i].Description) |
| 2872 | + assert.Equal(t, *expected.Color, *returnedIssueTypes[i].Color) |
| 2873 | + assert.Equal(t, *expected.ID, *returnedIssueTypes[i].ID) |
| 2874 | + } |
| 2875 | + } |
| 2876 | + }) |
| 2877 | + } |
| 2878 | +} |
0 commit comments