Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9403161

Browse files
add test
1 parentf46f435 commit9403161

File tree

1 file changed

+379
-0
lines changed

1 file changed

+379
-0
lines changed

‎pkg/errors/error_test.go

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
package errors
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/google/go-github/v72/github"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
funcTestGitHubErrorContext(t*testing.T) {
15+
t.Run("API errors can be added to context and retrieved",func(t*testing.T) {
16+
// Given a context with GitHub error tracking enabled
17+
ctx:=ContextWithGitHubErrors(context.Background())
18+
19+
// Create a mock GitHub response
20+
resp:=&github.Response{
21+
Response:&http.Response{
22+
StatusCode:404,
23+
Status:"404 Not Found",
24+
},
25+
}
26+
originalErr:=fmt.Errorf("resource not found")
27+
28+
// When we add an API error to the context
29+
updatedCtx,err:=NewGitHubAPIErrorToCtx(ctx,"failed to fetch resource",resp,originalErr)
30+
require.NoError(t,err)
31+
32+
// Then we should be able to retrieve the error from the updated context
33+
apiErrors,err:=GetGitHubAPIErrors(updatedCtx)
34+
require.NoError(t,err)
35+
require.Len(t,apiErrors,1)
36+
37+
apiError:=apiErrors[0]
38+
assert.Equal(t,"failed to fetch resource",apiError.Message)
39+
assert.Equal(t,resp,apiError.Response)
40+
assert.Equal(t,originalErr,apiError.Err)
41+
assert.Equal(t,"failed to fetch resource: resource not found",apiError.Error())
42+
})
43+
44+
t.Run("GraphQL errors can be added to context and retrieved",func(t*testing.T) {
45+
// Given a context with GitHub error tracking enabled
46+
ctx:=ContextWithGitHubErrors(context.Background())
47+
48+
originalErr:=fmt.Errorf("GraphQL query failed")
49+
50+
// When we add a GraphQL error to the context
51+
graphQLErr:=newGitHubGraphQLError("failed to execute mutation",originalErr)
52+
updatedCtx,err:=addGitHubGraphQLErrorToContext(ctx,graphQLErr)
53+
require.NoError(t,err)
54+
55+
// Then we should be able to retrieve the error from the updated context
56+
gqlErrors,err:=GetGitHubGraphQLErrors(updatedCtx)
57+
require.NoError(t,err)
58+
require.Len(t,gqlErrors,1)
59+
60+
gqlError:=gqlErrors[0]
61+
assert.Equal(t,"failed to execute mutation",gqlError.Message)
62+
assert.Equal(t,originalErr,gqlError.Err)
63+
assert.Equal(t,"failed to execute mutation: GraphQL query failed",gqlError.Error())
64+
})
65+
66+
t.Run("multiple errors can be accumulated in context",func(t*testing.T) {
67+
// Given a context with GitHub error tracking enabled
68+
ctx:=ContextWithGitHubErrors(context.Background())
69+
70+
// When we add multiple API errors
71+
resp1:=&github.Response{Response:&http.Response{StatusCode:404}}
72+
resp2:=&github.Response{Response:&http.Response{StatusCode:403}}
73+
74+
ctx,err:=NewGitHubAPIErrorToCtx(ctx,"first error",resp1,fmt.Errorf("not found"))
75+
require.NoError(t,err)
76+
77+
ctx,err=NewGitHubAPIErrorToCtx(ctx,"second error",resp2,fmt.Errorf("forbidden"))
78+
require.NoError(t,err)
79+
80+
// And add a GraphQL error
81+
gqlErr:=newGitHubGraphQLError("graphql error",fmt.Errorf("query failed"))
82+
ctx,err=addGitHubGraphQLErrorToContext(ctx,gqlErr)
83+
require.NoError(t,err)
84+
85+
// Then we should be able to retrieve all errors
86+
apiErrors,err:=GetGitHubAPIErrors(ctx)
87+
require.NoError(t,err)
88+
assert.Len(t,apiErrors,2)
89+
90+
gqlErrors,err:=GetGitHubGraphQLErrors(ctx)
91+
require.NoError(t,err)
92+
assert.Len(t,gqlErrors,1)
93+
94+
// Verify error details
95+
assert.Equal(t,"first error",apiErrors[0].Message)
96+
assert.Equal(t,"second error",apiErrors[1].Message)
97+
assert.Equal(t,"graphql error",gqlErrors[0].Message)
98+
})
99+
100+
t.Run("context pointer sharing allows middleware to inspect errors without context propagation",func(t*testing.T) {
101+
// This test demonstrates the key behavior: even when the context itself
102+
// isn't propagated through function calls, the pointer to the error slice
103+
// is shared, allowing middleware to inspect errors that were added later.
104+
105+
// Given a context with GitHub error tracking enabled
106+
originalCtx:=ContextWithGitHubErrors(context.Background())
107+
108+
// Simulate a middleware that captures the context early
109+
varmiddlewareCtx context.Context
110+
111+
// Middleware function that captures the context
112+
middleware:=func(ctx context.Context) {
113+
middlewareCtx=ctx// Middleware saves the context reference
114+
}
115+
116+
// Call middleware with the original context
117+
middleware(originalCtx)
118+
119+
// Simulate some business logic that adds errors to the context
120+
// but doesn't propagate the updated context back to middleware
121+
businessLogic:=func(ctx context.Context) {
122+
resp:=&github.Response{Response:&http.Response{StatusCode:500}}
123+
124+
// Add an error to the context (this modifies the shared pointer)
125+
_,err:=NewGitHubAPIErrorToCtx(ctx,"business logic failed",resp,fmt.Errorf("internal error"))
126+
require.NoError(t,err)
127+
128+
// Add another error
129+
_,err=NewGitHubAPIErrorToCtx(ctx,"second failure",resp,fmt.Errorf("another error"))
130+
require.NoError(t,err)
131+
}
132+
133+
// Execute business logic - note that we don't propagate the returned context
134+
businessLogic(originalCtx)
135+
136+
// Then the middleware should be able to see the errors that were added
137+
// even though it only has a reference to the original context
138+
apiErrors,err:=GetGitHubAPIErrors(middlewareCtx)
139+
require.NoError(t,err)
140+
assert.Len(t,apiErrors,2,"Middleware should see errors added after it captured the context")
141+
142+
assert.Equal(t,"business logic failed",apiErrors[0].Message)
143+
assert.Equal(t,"second failure",apiErrors[1].Message)
144+
})
145+
146+
t.Run("context without GitHub errors returns error",func(t*testing.T) {
147+
// Given a regular context without GitHub error tracking
148+
ctx:=context.Background()
149+
150+
// When we try to retrieve errors
151+
apiErrors,err:=GetGitHubAPIErrors(ctx)
152+
153+
// Then it should return an error
154+
assert.Error(t,err)
155+
assert.Contains(t,err.Error(),"context does not contain GitHubCtxErrors")
156+
assert.Nil(t,apiErrors)
157+
158+
// Same for GraphQL errors
159+
gqlErrors,err:=GetGitHubGraphQLErrors(ctx)
160+
assert.Error(t,err)
161+
assert.Contains(t,err.Error(),"context does not contain GitHubCtxErrors")
162+
assert.Nil(t,gqlErrors)
163+
})
164+
165+
t.Run("ContextWithGitHubErrors resets existing errors",func(t*testing.T) {
166+
// Given a context with existing errors
167+
ctx:=ContextWithGitHubErrors(context.Background())
168+
resp:=&github.Response{Response:&http.Response{StatusCode:404}}
169+
ctx,err:=NewGitHubAPIErrorToCtx(ctx,"existing error",resp,fmt.Errorf("error"))
170+
require.NoError(t,err)
171+
172+
// Verify error exists
173+
apiErrors,err:=GetGitHubAPIErrors(ctx)
174+
require.NoError(t,err)
175+
assert.Len(t,apiErrors,1)
176+
177+
// When we call ContextWithGitHubErrors again
178+
resetCtx:=ContextWithGitHubErrors(ctx)
179+
180+
// Then the errors should be cleared
181+
apiErrors,err=GetGitHubAPIErrors(resetCtx)
182+
require.NoError(t,err)
183+
assert.Len(t,apiErrors,0,"Errors should be reset")
184+
})
185+
186+
t.Run("NewGitHubAPIErrorResponse creates MCP error result and stores context error",func(t*testing.T) {
187+
// Given a context with GitHub error tracking enabled
188+
ctx:=ContextWithGitHubErrors(context.Background())
189+
190+
resp:=&github.Response{Response:&http.Response{StatusCode:422}}
191+
originalErr:=fmt.Errorf("validation failed")
192+
193+
// When we create an API error response
194+
result:=NewGitHubAPIErrorResponse(ctx,"API call failed",resp,originalErr)
195+
196+
// Then it should return an MCP error result
197+
require.NotNil(t,result)
198+
assert.True(t,result.IsError)
199+
200+
// And the error should be stored in the context
201+
apiErrors,err:=GetGitHubAPIErrors(ctx)
202+
require.NoError(t,err)
203+
require.Len(t,apiErrors,1)
204+
205+
apiError:=apiErrors[0]
206+
assert.Equal(t,"API call failed",apiError.Message)
207+
assert.Equal(t,resp,apiError.Response)
208+
assert.Equal(t,originalErr,apiError.Err)
209+
})
210+
211+
t.Run("NewGitHubGraphQLErrorResponse creates MCP error result and stores context error",func(t*testing.T) {
212+
// Given a context with GitHub error tracking enabled
213+
ctx:=ContextWithGitHubErrors(context.Background())
214+
215+
originalErr:=fmt.Errorf("mutation failed")
216+
217+
// When we create a GraphQL error response
218+
result:=NewGitHubGraphQLErrorResponse(ctx,"GraphQL call failed",originalErr)
219+
220+
// Then it should return an MCP error result
221+
require.NotNil(t,result)
222+
assert.True(t,result.IsError)
223+
224+
// And the error should be stored in the context
225+
gqlErrors,err:=GetGitHubGraphQLErrors(ctx)
226+
require.NoError(t,err)
227+
require.Len(t,gqlErrors,1)
228+
229+
gqlError:=gqlErrors[0]
230+
assert.Equal(t,"GraphQL call failed",gqlError.Message)
231+
assert.Equal(t,originalErr,gqlError.Err)
232+
})
233+
234+
t.Run("NewGitHubAPIErrorToCtx with uninitialized context does not error",func(t*testing.T) {
235+
// Given a regular context without GitHub error tracking initialized
236+
ctx:=context.Background()
237+
238+
// Create a mock GitHub response
239+
resp:=&github.Response{
240+
Response:&http.Response{
241+
StatusCode:500,
242+
Status:"500 Internal Server Error",
243+
},
244+
}
245+
originalErr:=fmt.Errorf("internal server error")
246+
247+
// When we try to add an API error to an uninitialized context
248+
updatedCtx,err:=NewGitHubAPIErrorToCtx(ctx,"failed operation",resp,originalErr)
249+
250+
// Then it should not return an error (graceful handling)
251+
assert.NoError(t,err,"NewGitHubAPIErrorToCtx should handle uninitialized context gracefully")
252+
assert.Equal(t,ctx,updatedCtx,"Context should be returned unchanged when not initialized")
253+
254+
// And attempting to retrieve errors should still return an error since context wasn't initialized
255+
apiErrors,err:=GetGitHubAPIErrors(updatedCtx)
256+
assert.Error(t,err)
257+
assert.Contains(t,err.Error(),"context does not contain GitHubCtxErrors")
258+
assert.Nil(t,apiErrors)
259+
})
260+
261+
t.Run("NewGitHubAPIErrorToCtx with nil context does not error",func(t*testing.T) {
262+
// Given a nil context
263+
varctx context.Context=nil
264+
265+
// Create a mock GitHub response
266+
resp:=&github.Response{
267+
Response:&http.Response{
268+
StatusCode:400,
269+
Status:"400 Bad Request",
270+
},
271+
}
272+
originalErr:=fmt.Errorf("bad request")
273+
274+
// When we try to add an API error to a nil context
275+
updatedCtx,err:=NewGitHubAPIErrorToCtx(ctx,"failed with nil context",resp,originalErr)
276+
277+
// Then it should not return an error (graceful handling)
278+
assert.NoError(t,err,"NewGitHubAPIErrorToCtx should handle nil context gracefully")
279+
assert.Nil(t,updatedCtx,"Context should remain nil when passed as nil")
280+
})
281+
}
282+
283+
funcTestGitHubErrorTypes(t*testing.T) {
284+
t.Run("GitHubAPIError implements error interface",func(t*testing.T) {
285+
resp:=&github.Response{Response:&http.Response{StatusCode:404}}
286+
originalErr:=fmt.Errorf("not found")
287+
288+
apiErr:=newGitHubAPIError("test message",resp,originalErr)
289+
290+
// Should implement error interface
291+
varerrerror=apiErr
292+
assert.Equal(t,"test message: not found",err.Error())
293+
})
294+
295+
t.Run("GitHubGraphQLError implements error interface",func(t*testing.T) {
296+
originalErr:=fmt.Errorf("query failed")
297+
298+
gqlErr:=newGitHubGraphQLError("test message",originalErr)
299+
300+
// Should implement error interface
301+
varerrerror=gqlErr
302+
assert.Equal(t,"test message: query failed",err.Error())
303+
})
304+
}
305+
306+
// TestMiddlewareScenario demonstrates a realistic middleware scenario
307+
funcTestMiddlewareScenario(t*testing.T) {
308+
t.Run("realistic middleware error collection scenario",func(t*testing.T) {
309+
// Simulate a realistic HTTP middleware scenario
310+
311+
// 1. Request comes in, middleware sets up error tracking
312+
ctx:=ContextWithGitHubErrors(context.Background())
313+
314+
// 2. Middleware stores reference to context for later inspection
315+
varmiddlewareCtx context.Context
316+
setupMiddleware:=func(ctx context.Context) context.Context {
317+
middlewareCtx=ctx
318+
returnctx
319+
}
320+
321+
// 3. Setup middleware
322+
ctx=setupMiddleware(ctx)
323+
324+
// 4. Simulate multiple service calls that add errors
325+
simulateServiceCall1:=func(ctx context.Context) {
326+
resp:=&github.Response{Response:&http.Response{StatusCode:403}}
327+
_,err:=NewGitHubAPIErrorToCtx(ctx,"insufficient permissions",resp,fmt.Errorf("forbidden"))
328+
require.NoError(t,err)
329+
}
330+
331+
simulateServiceCall2:=func(ctx context.Context) {
332+
resp:=&github.Response{Response:&http.Response{StatusCode:404}}
333+
_,err:=NewGitHubAPIErrorToCtx(ctx,"resource not found",resp,fmt.Errorf("not found"))
334+
require.NoError(t,err)
335+
}
336+
337+
simulateGraphQLCall:=func(ctx context.Context) {
338+
gqlErr:=newGitHubGraphQLError("mutation failed",fmt.Errorf("invalid input"))
339+
_,err:=addGitHubGraphQLErrorToContext(ctx,gqlErr)
340+
require.NoError(t,err)
341+
}
342+
343+
// 5. Execute service calls (without context propagation)
344+
simulateServiceCall1(ctx)
345+
simulateServiceCall2(ctx)
346+
simulateGraphQLCall(ctx)
347+
348+
// 6. Middleware inspects errors at the end of request processing
349+
finalizeMiddleware:=func(ctx context.Context) ([]string, []string) {
350+
varapiErrorMessages []string
351+
vargqlErrorMessages []string
352+
353+
ifapiErrors,err:=GetGitHubAPIErrors(ctx);err==nil {
354+
for_,apiErr:=rangeapiErrors {
355+
apiErrorMessages=append(apiErrorMessages,apiErr.Message)
356+
}
357+
}
358+
359+
ifgqlErrors,err:=GetGitHubGraphQLErrors(ctx);err==nil {
360+
for_,gqlErr:=rangegqlErrors {
361+
gqlErrorMessages=append(gqlErrorMessages,gqlErr.Message)
362+
}
363+
}
364+
365+
returnapiErrorMessages,gqlErrorMessages
366+
}
367+
368+
// 7. Middleware can see all errors that were added during request processing
369+
apiMessages,gqlMessages:=finalizeMiddleware(middlewareCtx)
370+
371+
// Verify all errors were captured
372+
assert.Len(t,apiMessages,2)
373+
assert.Contains(t,apiMessages,"insufficient permissions")
374+
assert.Contains(t,apiMessages,"resource not found")
375+
376+
assert.Len(t,gqlMessages,1)
377+
assert.Contains(t,gqlMessages,"mutation failed")
378+
})
379+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp