@@ -1153,3 +1153,194 @@ func TestInput_InvalidJSON(t *testing.T) {
11531153 _ , err := parseInput (json .RawMessage (`{invalid` ))
11541154 assert .Error (t , err )
11551155}
1156+
1157+ // ---------------------------------------------------------------------------
1158+ // Server instructions
1159+ // ---------------------------------------------------------------------------
1160+
1161+ func TestServerInfo_NameAndVersion (t * testing.T ) {
1162+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1163+ session := connectInMemory (t , client )
1164+
1165+ info := session .InitializeResult ()
1166+ require .NotNil (t , info )
1167+ assert .Equal (t , "hookdeck-gateway" , info .ServerInfo .Name )
1168+ assert .NotEmpty (t , info .ServerInfo .Version )
1169+ }
1170+
1171+ // ---------------------------------------------------------------------------
1172+ // Help tool: all topics return valid content
1173+ // ---------------------------------------------------------------------------
1174+
1175+ func TestHelpTool_AllTopics (t * testing.T ) {
1176+ topics := []struct {
1177+ name string
1178+ expectContains string
1179+ }{
1180+ {"hookdeck_projects" , "list" },
1181+ {"hookdeck_connections" , "pause" },
1182+ {"hookdeck_sources" , "list" },
1183+ {"hookdeck_destinations" , "HTTP" },
1184+ {"hookdeck_transformations" , "JavaScript" },
1185+ {"hookdeck_requests" , "raw_body" },
1186+ {"hookdeck_events" , "raw_body" },
1187+ {"hookdeck_attempts" , "event_id" },
1188+ {"hookdeck_issues" , "delivery" },
1189+ {"hookdeck_metrics" , "granularity" },
1190+ {"hookdeck_help" , "topic" },
1191+ }
1192+
1193+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1194+ session := connectInMemory (t , client )
1195+
1196+ for _ , tt := range topics {
1197+ t .Run (tt .name , func (t * testing.T ) {
1198+ result := callTool (t , session , "hookdeck_help" , map [string ]any {"topic" : tt .name })
1199+ assert .False (t , result .IsError , "help for %s should not be an error" , tt .name )
1200+ text := textContent (t , result )
1201+ assert .Contains (t , text , tt .expectContains ,
1202+ "help for %s should mention %q" , tt .name , tt .expectContains )
1203+ })
1204+ }
1205+ }
1206+
1207+ func TestHelpTool_ShortNames (t * testing.T ) {
1208+ shortNames := []string {
1209+ "projects" , "connections" , "sources" , "destinations" ,
1210+ "transformations" , "requests" , "events" , "attempts" ,
1211+ "issues" , "metrics" , "help" ,
1212+ }
1213+
1214+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1215+ session := connectInMemory (t , client )
1216+
1217+ for _ , name := range shortNames {
1218+ t .Run (name , func (t * testing.T ) {
1219+ result := callTool (t , session , "hookdeck_help" , map [string ]any {"topic" : name })
1220+ assert .False (t , result .IsError , "short name %q should resolve" , name )
1221+ assert .Contains (t , textContent (t , result ), "hookdeck_" + name )
1222+ })
1223+ }
1224+ }
1225+
1226+ func TestHelpTool_OverviewListsAllTools (t * testing.T ) {
1227+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1228+ session := connectInMemory (t , client )
1229+
1230+ result := callTool (t , session , "hookdeck_help" , map [string ]any {})
1231+ assert .False (t , result .IsError )
1232+ text := textContent (t , result )
1233+
1234+ expectedTools := []string {
1235+ "hookdeck_projects" , "hookdeck_connections" , "hookdeck_sources" ,
1236+ "hookdeck_destinations" , "hookdeck_transformations" , "hookdeck_requests" ,
1237+ "hookdeck_events" , "hookdeck_attempts" , "hookdeck_issues" ,
1238+ "hookdeck_metrics" , "hookdeck_help" ,
1239+ }
1240+ for _ , tool := range expectedTools {
1241+ assert .Contains (t , text , tool , "overview should list %s" , tool )
1242+ }
1243+ }
1244+
1245+ func TestHelpTool_OverviewShowsProjectNotSet (t * testing.T ) {
1246+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1247+ client .ProjectID = "" // no project set
1248+ session := connectInMemory (t , client )
1249+
1250+ result := callTool (t , session , "hookdeck_help" , map [string ]any {})
1251+ assert .False (t , result .IsError )
1252+ assert .Contains (t , textContent (t , result ), "not set" )
1253+ }
1254+
1255+ func TestHelpTool_UnknownTopicListsAvailable (t * testing.T ) {
1256+ client := newTestClient ("https://api.hookdeck.com" , "test-key" )
1257+ session := connectInMemory (t , client )
1258+
1259+ result := callTool (t , session , "hookdeck_help" , map [string ]any {"topic" : "bogus" })
1260+ assert .True (t , result .IsError )
1261+ text := textContent (t , result )
1262+ assert .Contains (t , text , "No help found" )
1263+ assert .Contains (t , text , "hookdeck_events" ) // lists available tools
1264+ }
1265+
1266+ // ---------------------------------------------------------------------------
1267+ // Error feedback: 500 server error through HTTP flow
1268+ // ---------------------------------------------------------------------------
1269+
1270+ func TestDestinationsGet_500ServerError (t * testing.T ) {
1271+ session := mockAPIWithClient (t , map [string ]http.HandlerFunc {
1272+ "/2025-07-01/destinations/des_fail" : func (w http.ResponseWriter , r * http.Request ) {
1273+ w .WriteHeader (http .StatusInternalServerError )
1274+ json .NewEncoder (w ).Encode (map [string ]any {"message" : "internal server error" })
1275+ },
1276+ })
1277+
1278+ result := callTool (t , session , "hookdeck_destinations" , map [string ]any {"action" : "get" , "id" : "des_fail" })
1279+ assert .True (t , result .IsError )
1280+ assert .Contains (t , textContent (t , result ), "Hookdeck API error" )
1281+ }
1282+
1283+ func TestConnectionsGet_401UnauthorizedError (t * testing.T ) {
1284+ session := mockAPIWithClient (t , map [string ]http.HandlerFunc {
1285+ "/2025-07-01/connections/web_bad" : func (w http.ResponseWriter , r * http.Request ) {
1286+ w .WriteHeader (http .StatusUnauthorized )
1287+ json .NewEncoder (w ).Encode (map [string ]any {"message" : "invalid api key" })
1288+ },
1289+ })
1290+
1291+ result := callTool (t , session , "hookdeck_connections" , map [string ]any {"action" : "get" , "id" : "web_bad" })
1292+ assert .True (t , result .IsError )
1293+ assert .Contains (t , textContent (t , result ), "Authentication failed" )
1294+ }
1295+
1296+ func TestIssuesList_422ValidationError (t * testing.T ) {
1297+ session := mockAPIWithClient (t , map [string ]http.HandlerFunc {
1298+ "/2025-07-01/issues" : func (w http.ResponseWriter , r * http.Request ) {
1299+ w .WriteHeader (http .StatusUnprocessableEntity )
1300+ json .NewEncoder (w ).Encode (map [string ]any {"message" : "invalid filter: bad_field" })
1301+ },
1302+ })
1303+
1304+ result := callTool (t , session , "hookdeck_issues" , map [string ]any {"action" : "list" })
1305+ assert .True (t , result .IsError )
1306+ assert .Contains (t , textContent (t , result ), "invalid filter" )
1307+ }
1308+
1309+ func TestAttemptsList_429RateLimitError (t * testing.T ) {
1310+ session := mockAPIWithClient (t , map [string ]http.HandlerFunc {
1311+ "/2025-07-01/attempts" : func (w http.ResponseWriter , r * http.Request ) {
1312+ w .WriteHeader (http .StatusTooManyRequests )
1313+ json .NewEncoder (w ).Encode (map [string ]any {"message" : "too many requests" })
1314+ },
1315+ })
1316+
1317+ result := callTool (t , session , "hookdeck_attempts" , map [string ]any {"action" : "list" })
1318+ assert .True (t , result .IsError )
1319+ assert .Contains (t , textContent (t , result ), "Rate limited" )
1320+ }
1321+
1322+ // ---------------------------------------------------------------------------
1323+ // Error translation: additional cases
1324+ // ---------------------------------------------------------------------------
1325+
1326+ func TestTranslateAPIError_RetryAfterMessage (t * testing.T ) {
1327+ msg := TranslateAPIError (& hookdeck.APIError {StatusCode : 429 , Message : "rate limited" })
1328+ assert .Contains (t , msg , "Rate limited" )
1329+ assert .Contains (t , msg , "Retry after" )
1330+ }
1331+
1332+ func TestTranslateAPIError_GenericClientError (t * testing.T ) {
1333+ // A 4xx status not explicitly handled should pass through the message
1334+ msg := TranslateAPIError (& hookdeck.APIError {StatusCode : 409 , Message : "conflict on resource" })
1335+ assert .Contains (t , msg , "conflict on resource" )
1336+ }
1337+
1338+ func TestTranslateAPIError_502GatewayError (t * testing.T ) {
1339+ msg := TranslateAPIError (& hookdeck.APIError {StatusCode : 502 , Message : "bad gateway" })
1340+ assert .Contains (t , msg , "Hookdeck API error" )
1341+ }
1342+
1343+ func TestTranslateAPIError_503ServiceUnavailable (t * testing.T ) {
1344+ msg := TranslateAPIError (& hookdeck.APIError {StatusCode : 503 , Message : "service unavailable" })
1345+ assert .Contains (t , msg , "Hookdeck API error" )
1346+ }
0 commit comments