Skip to content

Commit 023f59d

Browse files
Use typed tool handler for get_me tool
1 parent 7e026fc commit 023f59d

File tree

23 files changed

+1153
-96
lines changed

23 files changed

+1153
-96
lines changed

docs/testing.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Testing
2+
3+
This project uses a combination of unit tests and end-to-end (e2e) tests to ensure correctness and stability.
4+
5+
## Unit Testing Patterns
6+
7+
- Unit tests are located alongside implementation, with filenames ending in `_test.go`.
8+
- Currently the preference is to use internal tests i.e. test files do not have `_test` package suffix.
9+
- Tests use [testify](https://github.com/stretchr/testify) for assertions and require statements. Use `require` when continuing the test is not meaningful, for example it is almost never correct to continue after an error expectation.
10+
- Mocking is performed using [go-github-mock](https://github.com/migueleliasweb/go-github-mock) or `githubv4mock` for simulating GitHub rest and GQL API responses.
11+
- Each tool's schema is snapshotted and checked for changes using the `toolsnaps` utility (see below).
12+
- Tests are designed to be explicit and verbose to aid maintainability and clarity.
13+
- Handler unit tests should take the form of:
14+
1. Test tool snapshot
15+
1. Very important expectations against the schema (e.g. `ReadOnly` annotation)
16+
1. Behavioural tests in table-driven form
17+
18+
## End-to-End (e2e) Tests
19+
20+
- E2E tests are located in the [`e2e/`](../e2e/) directory. See the [e2e/README.md](../e2e/README.md) for full details on running and debugging these tests.
21+
22+
## toolsnaps: Tool Schema Snapshots
23+
24+
- The `toolsnaps` utility ensures that the JSON schema for each tool does not change unexpectedly.
25+
- Snapshots are stored in `__toolsnaps__/*.snap` files , where `*` represents the name of the tool
26+
- When running tests, the current tool schema is compared to the snapshot. If there is a difference, the test will fail and show a diff.
27+
- If you intentionally change a tool's schema, update the snapshots by running tests with the environment variable: `UPDATE_TOOLSNAPS=true go test ./...`
28+
- In CI (when `GITHUB_ACTIONS=true`), missing snapshots will cause a test failure to ensure snapshots are always
29+
committed.
30+
31+
## Notes
32+
33+
- Some tools that mutate global state (e.g., marking all notifications as read) are tested primarily with unit tests, not e2e, to avoid side effects.
34+
- For more on the limitations and philosophy of the e2e suite, see the [e2e/README.md](../e2e/README.md).

go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.23.7
44

55
require (
66
github.com/google/go-github/v69 v69.2.0
7+
github.com/josephburnett/jd v1.9.2
78
github.com/mark3labs/mcp-go v0.30.0
89
github.com/migueleliasweb/go-github-mock v1.3.0
910
github.com/sirupsen/logrus v1.9.3
@@ -12,6 +13,16 @@ require (
1213
github.com/stretchr/testify v1.10.0
1314
)
1415

16+
require (
17+
github.com/go-openapi/jsonpointer v0.19.5 // indirect
18+
github.com/go-openapi/swag v0.21.1 // indirect
19+
github.com/josharian/intern v1.0.0 // indirect
20+
github.com/mailru/easyjson v0.7.7 // indirect
21+
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
22+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
23+
gopkg.in/yaml.v2 v2.4.0 // indirect
24+
)
25+
1526
require (
1627
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
1728
github.com/fsnotify/fsnotify v1.8.0 // indirect

go.sum

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
2+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
23
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
34
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
45
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -7,6 +8,11 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
78
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
89
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
910
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
11+
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
12+
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
13+
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
14+
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
15+
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
1016
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
1117
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
1218
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -24,17 +30,28 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
2430
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
2531
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
2632
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
33+
github.com/josephburnett/jd v1.9.2 h1:ECJRRFXCCqbtidkAHckHGSZm/JIaAxS1gygHLF8MI5Y=
34+
github.com/josephburnett/jd v1.9.2/go.mod h1:bImDr8QXpxMb3SD+w1cDRHp97xP6UwI88xUAuxwDQfM=
35+
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
36+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
37+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
2738
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
2839
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
2940
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
3041
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
3142
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
3243
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3344
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
45+
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
46+
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
47+
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
48+
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
49+
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
3450
github.com/mark3labs/mcp-go v0.30.0 h1:Taz7fiefkxY/l8jz1nA90V+WdM2eoMtlvwfWforVYbo=
3551
github.com/mark3labs/mcp-go v0.30.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
3652
github.com/migueleliasweb/go-github-mock v1.3.0 h1:2sVP9JEMB2ubQw1IKto3/fzF51oFC6eVWOOFDgQoq88=
3753
github.com/migueleliasweb/go-github-mock v1.3.0/go.mod h1:ipQhV8fTcj/G6m7BKzin08GaJ/3B5/SonRAkgrk0zCY=
54+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
3855
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
3956
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
4057
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -64,15 +81,21 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
6481
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
6582
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
6683
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
84+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
85+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
6786
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
6887
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6988
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7089
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
7190
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
7291
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
7392
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
93+
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
94+
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
7495
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
7596
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
97+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
98+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
7699
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
77100
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
78101
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -84,8 +107,14 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
84107
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
85108
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
86109
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
110+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
111+
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
87112
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
88113
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
114+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
115+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
116+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
89117
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
118+
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
90119
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
91120
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/toolsnaps/toolsnaps.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Package toolsnaps provides test utilities for ensuring json schemas for tools
2+
// have not changed unexpectedly.
3+
package toolsnaps
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/josephburnett/jd/v2"
12+
)
13+
14+
// Test checks that the JSON schema for a tool has not changed unexpectedly.
15+
// It compares the marshaled JSON of the provided tool against a stored snapshot file.
16+
// If the UPDATE_TOOLSNAPS environment variable is set to "true", it updates the snapshot file instead.
17+
// If the snapshot does not exist and not running in CI, it creates the snapshot file.
18+
// If the snapshot does not exist and running in CI (GITHUB_ACTIONS="true"), it returns an error.
19+
// If the snapshot exists, it compares the tool's JSON to the snapshot and returns an error if they differ.
20+
// Returns an error if marshaling, reading, or comparing fails.
21+
func Test(toolName string, tool any) error {
22+
toolJSON, err := json.MarshalIndent(tool, "", " ")
23+
if err != nil {
24+
return fmt.Errorf("failed to marshal tool %s: %w", toolName, err)
25+
}
26+
27+
snapPath := fmt.Sprintf("__toolsnaps__/%s.snap", toolName)
28+
29+
// If UPDATE_TOOLSNAPS is set, then we write the tool JSON to the snapshot file and exit
30+
if os.Getenv("UPDATE_TOOLSNAPS") == "true" {
31+
return writeSnap(snapPath, toolJSON)
32+
}
33+
34+
snapJSON, err := os.ReadFile(snapPath) //nolint:gosec // filepaths are controlled by the test suite, so this is safe.
35+
// If the snapshot file does not exist, this must be the first time this test is run.
36+
// We write the tool JSON to the snapshot file and exit.
37+
if os.IsNotExist(err) {
38+
// If we're running in CI, we will error if there is not snapshot because it's important that snapshots
39+
// are committed alongside the tests, rather than just being constructed and not committed during a CI run.
40+
if os.Getenv("GITHUB_ACTIONS") == "true" {
41+
return fmt.Errorf("tool snapshot does not exist for %s. Please run the tests with UPDATE_TOOLSNAPS=true to create it", toolName)
42+
}
43+
44+
return writeSnap(snapPath, toolJSON)
45+
}
46+
47+
// Otherwise we will compare the tool JSON to the snapshot JSON
48+
toolNode, err := jd.ReadJsonString(string(toolJSON))
49+
if err != nil {
50+
return fmt.Errorf("failed to parse tool JSON for %s: %w", toolName, err)
51+
}
52+
53+
snapNode, err := jd.ReadJsonString(string(snapJSON))
54+
if err != nil {
55+
return fmt.Errorf("failed to parse snapshot JSON for %s: %w", toolName, err)
56+
}
57+
58+
// jd.Set allows arrays to be compared without order sensitivity,
59+
// which is useful because we don't really care about this when exposing tool schemas.
60+
diff := toolNode.Diff(snapNode, jd.SET).Render()
61+
if diff != "" {
62+
// If there is a difference, we return an error with the diff
63+
return fmt.Errorf("tool schema for %s has changed unexpectedly:\n%s", toolName, diff)
64+
}
65+
66+
return nil
67+
}
68+
69+
func writeSnap(snapPath string, contents []byte) error {
70+
// Ensure the directory exists
71+
if err := os.MkdirAll(filepath.Dir(snapPath), 0700); err != nil {
72+
return fmt.Errorf("failed to create snapshot directory: %w", err)
73+
}
74+
75+
// Write the snapshot file
76+
if err := os.WriteFile(snapPath, contents, 0600); err != nil {
77+
return fmt.Errorf("failed to write snapshot file: %w", err)
78+
}
79+
80+
return nil
81+
}

internal/toolsnaps/toolsnaps_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package toolsnaps
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
type dummyTool struct {
14+
Name string `json:"name"`
15+
Value int `json:"value"`
16+
}
17+
18+
// withIsolatedWorkingDir creates a temp dir, changes to it, and restores the original working dir after the test.
19+
func withIsolatedWorkingDir(t *testing.T) {
20+
dir := t.TempDir()
21+
origDir, err := os.Getwd()
22+
require.NoError(t, err)
23+
t.Cleanup(func() { require.NoError(t, os.Chdir(origDir)) })
24+
require.NoError(t, os.Chdir(dir))
25+
}
26+
27+
func TestSnapshotDoesNotExistNotInCI(t *testing.T) {
28+
withIsolatedWorkingDir(t)
29+
30+
// Given we are not running in CI
31+
t.Setenv("GITHUB_ACTIONS", "false") // This REALLY is required because the tests run in CI
32+
tool := dummyTool{"foo", 42}
33+
34+
// When we test the snapshot
35+
err := Test("dummy", tool)
36+
37+
// Then it should succeed and write the snapshot file
38+
require.NoError(t, err)
39+
path := filepath.Join("__toolsnaps__", "dummy.snap")
40+
_, statErr := os.Stat(path)
41+
assert.NoError(t, statErr, "expected snapshot file to be written")
42+
}
43+
44+
func TestSnapshotDoesNotExistInCI(t *testing.T) {
45+
withIsolatedWorkingDir(t)
46+
47+
// Given we are running in CI
48+
t.Setenv("GITHUB_ACTIONS", "true")
49+
tool := dummyTool{"foo", 42}
50+
51+
// When we test the snapshot
52+
err := Test("dummy", tool)
53+
54+
// Then it should error about missing snapshot in CI
55+
require.Error(t, err)
56+
assert.Contains(t, err.Error(), "tool snapshot does not exist", "expected error about missing snapshot in CI")
57+
}
58+
59+
func TestSnapshotExistsMatch(t *testing.T) {
60+
withIsolatedWorkingDir(t)
61+
62+
// Given a matching snapshot file exists
63+
tool := dummyTool{"foo", 42}
64+
b, _ := json.MarshalIndent(tool, "", " ")
65+
require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
66+
require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), b, 0600))
67+
68+
// When we test the snapshot
69+
err := Test("dummy", tool)
70+
71+
// Then it should succeed (no error)
72+
require.NoError(t, err)
73+
}
74+
75+
func TestSnapshotExistsDiff(t *testing.T) {
76+
withIsolatedWorkingDir(t)
77+
78+
// Given a non-matching snapshot file exists
79+
require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
80+
require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`{"name":"foo","value":1}`), 0600))
81+
tool := dummyTool{"foo", 2}
82+
83+
// When we test the snapshot
84+
err := Test("dummy", tool)
85+
86+
// Then it should error about the schema diff
87+
require.Error(t, err)
88+
assert.Contains(t, err.Error(), "tool schema for dummy has changed unexpectedly", "expected error about diff")
89+
}
90+
91+
func TestUpdateToolsnaps(t *testing.T) {
92+
withIsolatedWorkingDir(t)
93+
94+
// Given UPDATE_TOOLSNAPS is set, regardless of whether a matching snapshot file exists
95+
t.Setenv("UPDATE_TOOLSNAPS", "true")
96+
require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
97+
require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`{"name":"foo","value":1}`), 0600))
98+
tool := dummyTool{"foo", 42}
99+
100+
// When we test the snapshot
101+
err := Test("dummy", tool)
102+
103+
// Then it should succeed and write the snapshot file
104+
require.NoError(t, err)
105+
path := filepath.Join("__toolsnaps__", "dummy.snap")
106+
_, statErr := os.Stat(path)
107+
assert.NoError(t, statErr, "expected snapshot file to be written")
108+
}
109+
110+
func TestMalformedSnapshotJSON(t *testing.T) {
111+
withIsolatedWorkingDir(t)
112+
113+
// Given a malformed snapshot file exists
114+
require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
115+
require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`not-json`), 0600))
116+
tool := dummyTool{"foo", 42}
117+
118+
// When we test the snapshot
119+
err := Test("dummy", tool)
120+
121+
// Then it should error about malformed snapshot JSON
122+
require.Error(t, err)
123+
assert.Contains(t, err.Error(), "failed to parse snapshot JSON for dummy", "expected error about malformed snapshot JSON")
124+
}

pkg/github/__toolsnaps__/get_me.snap

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"annotations": {
3+
"title": "Get my user profile",
4+
"readOnlyHint": true
5+
},
6+
"description": "Get details of the authenticated GitHub user. Use this when a request includes \"me\", \"my\". The output will not change unless the user changes their profile, so only call this once.",
7+
"inputSchema": {
8+
"properties": {
9+
"reason": {
10+
"description": "Optional: the reason for requesting the user information",
11+
"type": "string"
12+
}
13+
},
14+
"type": "object"
15+
},
16+
"name": "get_me"
17+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy