diff --git a/internal/toolsnaps/toolsnaps_test.go b/internal/toolsnaps/toolsnaps_test.go index 8a3d801ab..c7d7301bc 100644 --- a/internal/toolsnaps/toolsnaps_test.go +++ b/internal/toolsnaps/toolsnaps_test.go @@ -252,3 +252,63 @@ func TestToolSnapKeysSorted(t *testing.T) { assert.Greater(t, mmmIndex, aaaIndex, "mmm should come after aaa") assert.Greater(t, zzzIndex, mmmIndex, "zzz should come after mmm") } + +func TestStructFieldOrderingSortedAlphabetically(t *testing.T) { + withIsolatedWorkingDir(t) + + // Given a struct with fields defined in non-alphabetical order + // This test ensures that struct field order doesn't affect the JSON output + type toolWithNonAlphabeticalFields struct { + ZField string `json:"zField"` // Should appear last in JSON + AField string `json:"aField"` // Should appear first in JSON + MField string `json:"mField"` // Should appear in the middle + } + + tool := toolWithNonAlphabeticalFields{ + ZField: "z value", + AField: "a value", + MField: "m value", + } + + // When we write the snapshot + t.Setenv("UPDATE_TOOLSNAPS", "true") + err := Test("struct_field_order", tool) + require.NoError(t, err) + + // Then the snapshot file should have alphabetically sorted keys despite struct field order + snapJSON, err := os.ReadFile("__toolsnaps__/struct_field_order.snap") + require.NoError(t, err) + + snapStr := string(snapJSON) + + // Find the positions of each field in the JSON string + aFieldIndex := -1 + mFieldIndex := -1 + zFieldIndex := -1 + for i := 0; i < len(snapStr)-7; i++ { + switch snapStr[i : i+6] { + case "aField": + aFieldIndex = i + case "mField": + mFieldIndex = i + case "zField": + zFieldIndex = i + } + } + + // Verify alphabetical ordering in the JSON output + require.NotEqual(t, -1, aFieldIndex, "aField should be present") + require.NotEqual(t, -1, mFieldIndex, "mField should be present") + require.NotEqual(t, -1, zFieldIndex, "zField should be present") + assert.Less(t, aFieldIndex, mFieldIndex, "aField should appear before mField") + assert.Less(t, mFieldIndex, zFieldIndex, "mField should appear before zField") + + // Also verify idempotency - running the test again should produce identical output + err = Test("struct_field_order", tool) + require.NoError(t, err) + + snapJSON2, err := os.ReadFile("__toolsnaps__/struct_field_order.snap") + require.NoError(t, err) + + assert.Equal(t, string(snapJSON), string(snapJSON2), "Multiple runs should produce identical output") +} diff --git a/pkg/github/__toolsnaps__/add_comment_to_pending_review.snap b/pkg/github/__toolsnaps__/add_comment_to_pending_review.snap index 78795c096..af4c41f52 100644 --- a/pkg/github/__toolsnaps__/add_comment_to_pending_review.snap +++ b/pkg/github/__toolsnaps__/add_comment_to_pending_review.snap @@ -4,69 +4,69 @@ }, "description": "Add review comment to the requester's latest pending pull request review. A pending review needs to already exist to call this (check with the user if not sure).", "inputSchema": { - "type": "object", - "required": [ - "owner", - "repo", - "pullNumber", - "path", - "body", - "subjectType" - ], "properties": { "body": { - "type": "string", - "description": "The text of the review comment" + "description": "The text of the review comment", + "type": "string" }, "line": { - "type": "number", - "description": "The line of the blob in the pull request diff that the comment applies to. For multi-line comments, the last line of the range" + "description": "The line of the blob in the pull request diff that the comment applies to. For multi-line comments, the last line of the range", + "type": "number" }, "owner": { - "type": "string", - "description": "Repository owner" + "description": "Repository owner", + "type": "string" }, "path": { - "type": "string", - "description": "The relative path to the file that necessitates a comment" + "description": "The relative path to the file that necessitates a comment", + "type": "string" }, "pullNumber": { - "type": "number", - "description": "Pull request number" + "description": "Pull request number", + "type": "number" }, "repo": { - "type": "string", - "description": "Repository name" + "description": "Repository name", + "type": "string" }, "side": { - "type": "string", "description": "The side of the diff to comment on. LEFT indicates the previous state, RIGHT indicates the new state", "enum": [ "LEFT", "RIGHT" - ] + ], + "type": "string" }, "startLine": { - "type": "number", - "description": "For multi-line comments, the first line of the range that the comment applies to" + "description": "For multi-line comments, the first line of the range that the comment applies to", + "type": "number" }, "startSide": { - "type": "string", "description": "For multi-line comments, the starting side of the diff that the comment applies to. LEFT indicates the previous state, RIGHT indicates the new state", "enum": [ "LEFT", "RIGHT" - ] + ], + "type": "string" }, "subjectType": { - "type": "string", "description": "The level at which the comment is targeted", "enum": [ "FILE", "LINE" - ] + ], + "type": "string" } - } + }, + "required": [ + "owner", + "repo", + "pullNumber", + "path", + "body", + "subjectType" + ], + "type": "object" }, "name": "add_comment_to_pending_review" } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap b/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap index 7ad1922a0..9f0777d1f 100644 --- a/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap +++ b/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap @@ -4,43 +4,43 @@ "title": "Assign Copilot to issue" }, "description": "Assign Copilot to a specific issue in a GitHub repository.\n\nThis tool can help with the following outcomes:\n- a Pull Request created with source code changes to resolve the issue\n\n\nMore information can be found at:\n- https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/about-assigning-tasks-to-copilot\n", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==", + "theme": "dark" + } + ], "inputSchema": { - "type": "object", "properties": { "base_ref": { - "type": "string", - "description": "Git reference (e.g., branch) that the agent will start its work from. If not specified, defaults to the repository's default branch" + "description": "Git reference (e.g., branch) that the agent will start its work from. If not specified, defaults to the repository's default branch", + "type": "string" }, "issue_number": { - "type": "number", - "description": "Issue number" + "description": "Issue number", + "type": "number" }, "owner": { - "type": "string", - "description": "Repository owner" + "description": "Repository owner", + "type": "string" }, "repo": { - "type": "string", - "description": "Repository name" + "description": "Repository name", + "type": "string" } }, "required": [ "owner", "repo", "issue_number" - ] + ], + "type": "object" }, - "name": "assign_copilot_to_issue", - "icons": [ - { - "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=", - "mimeType": "image/png", - "theme": "light" - }, - { - "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==", - "mimeType": "image/png", - "theme": "dark" - } - ] + "name": "assign_copilot_to_issue" } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/get_label.snap b/pkg/github/__toolsnaps__/get_label.snap index 8541044d0..854f048c2 100644 --- a/pkg/github/__toolsnaps__/get_label.snap +++ b/pkg/github/__toolsnaps__/get_label.snap @@ -5,26 +5,26 @@ }, "description": "Get a specific label from a repository.", "inputSchema": { - "type": "object", - "required": [ - "owner", - "repo", - "name" - ], "properties": { "name": { - "type": "string", - "description": "Label name." + "description": "Label name.", + "type": "string" }, "owner": { - "type": "string", - "description": "Repository owner (username or organization name)" + "description": "Repository owner (username or organization name)", + "type": "string" }, "repo": { - "type": "string", - "description": "Repository name" + "description": "Repository name", + "type": "string" } - } + }, + "required": [ + "owner", + "repo", + "name" + ], + "type": "object" }, "name": "get_label" } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/label_write.snap b/pkg/github/__toolsnaps__/label_write.snap index 879817442..f0aca8cc9 100644 --- a/pkg/github/__toolsnaps__/label_write.snap +++ b/pkg/github/__toolsnaps__/label_write.snap @@ -4,48 +4,48 @@ }, "description": "Perform write operations on repository labels. To set labels on issues, use the 'update_issue' tool.", "inputSchema": { - "type": "object", - "required": [ - "method", - "owner", - "repo", - "name" - ], "properties": { "color": { - "type": "string", - "description": "Label color as 6-character hex code without '#' prefix (e.g., 'f29513'). Required for 'create', optional for 'update'." + "description": "Label color as 6-character hex code without '#' prefix (e.g., 'f29513'). Required for 'create', optional for 'update'.", + "type": "string" }, "description": { - "type": "string", - "description": "Label description text. Optional for 'create' and 'update'." + "description": "Label description text. Optional for 'create' and 'update'.", + "type": "string" }, "method": { - "type": "string", "description": "Operation to perform: 'create', 'update', or 'delete'", "enum": [ "create", "update", "delete" - ] + ], + "type": "string" }, "name": { - "type": "string", - "description": "Label name - required for all operations" + "description": "Label name - required for all operations", + "type": "string" }, "new_name": { - "type": "string", - "description": "New name for the label (used only with 'update' method to rename)" + "description": "New name for the label (used only with 'update' method to rename)", + "type": "string" }, "owner": { - "type": "string", - "description": "Repository owner (username or organization name)" + "description": "Repository owner (username or organization name)", + "type": "string" }, "repo": { - "type": "string", - "description": "Repository name" + "description": "Repository name", + "type": "string" } - } + }, + "required": [ + "method", + "owner", + "repo", + "name" + ], + "type": "object" }, "name": "label_write" } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/list_label.snap b/pkg/github/__toolsnaps__/list_label.snap index 0b4f3b20c..debc2d44e 100644 --- a/pkg/github/__toolsnaps__/list_label.snap +++ b/pkg/github/__toolsnaps__/list_label.snap @@ -5,21 +5,21 @@ }, "description": "List labels from a repository", "inputSchema": { - "type": "object", - "required": [ - "owner", - "repo" - ], "properties": { "owner": { - "type": "string", - "description": "Repository owner (username or organization name) - required for all operations" + "description": "Repository owner (username or organization name) - required for all operations", + "type": "string" }, "repo": { - "type": "string", - "description": "Repository name - required for all operations" + "description": "Repository name - required for all operations", + "type": "string" } - } + }, + "required": [ + "owner", + "repo" + ], + "type": "object" }, "name": "list_label" } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/pull_request_review_write.snap b/pkg/github/__toolsnaps__/pull_request_review_write.snap index 92cc19924..7b533f472 100644 --- a/pkg/github/__toolsnaps__/pull_request_review_write.snap +++ b/pkg/github/__toolsnaps__/pull_request_review_write.snap @@ -4,53 +4,53 @@ }, "description": "Create and/or submit, delete review of a pull request.\n\nAvailable methods:\n- create: Create a new review of a pull request. If \"event\" parameter is provided, the review is submitted. If \"event\" is omitted, a pending review is created.\n- submit_pending: Submit an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request. The \"body\" and \"event\" parameters are used when submitting the review.\n- delete_pending: Delete an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request.\n", "inputSchema": { - "type": "object", - "required": [ - "method", - "owner", - "repo", - "pullNumber" - ], "properties": { "body": { - "type": "string", - "description": "Review comment text" + "description": "Review comment text", + "type": "string" }, "commitID": { - "type": "string", - "description": "SHA of commit to review" + "description": "SHA of commit to review", + "type": "string" }, "event": { - "type": "string", "description": "Review action to perform.", "enum": [ "APPROVE", "REQUEST_CHANGES", "COMMENT" - ] + ], + "type": "string" }, "method": { - "type": "string", "description": "The write operation to perform on pull request review.", "enum": [ "create", "submit_pending", "delete_pending" - ] + ], + "type": "string" }, "owner": { - "type": "string", - "description": "Repository owner" + "description": "Repository owner", + "type": "string" }, "pullNumber": { - "type": "number", - "description": "Pull request number" + "description": "Pull request number", + "type": "number" }, "repo": { - "type": "string", - "description": "Repository name" + "description": "Repository name", + "type": "string" } - } + }, + "required": [ + "method", + "owner", + "repo", + "pullNumber" + ], + "type": "object" }, "name": "pull_request_review_write" } \ No newline at end of file