github.com/anthropics/anthropic-sdk-go@v1.70.1/error_type_test.go (about) 1 package anthropic 2 3 import ( 4 "encoding/json" 5 "errors" 6 "io" 7 "net/http" 8 "strings" 9 "testing" 10 11 "github.com/anthropics/anthropic-sdk-go/internal/apierror" 12 "github.com/anthropics/anthropic-sdk-go/packages/ssestream" 13 "github.com/anthropics/anthropic-sdk-go/shared" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestStreamingErrorType(t *testing.T) { 19 sseBody := "event: error\ndata: {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}\n\n" 20 httpResp := &http.Response{ 21 StatusCode: 200, 22 Header: http.Header{"Request-Id": []string{"req_stream123"}, "Anthropic-Workspace-Id": []string{"wrkspc_123"}}, 23 Body: io.NopCloser(strings.NewReader(sseBody)), 24 Request: mustNewRequest("POST", "https://api.anthropic.com/v1/messages"), 25 } 26 27 stream := ssestream.NewStream[json.RawMessage](ssestream.NewDecoder(httpResp), nil) 28 29 stream.Next() 30 require.Error(t, stream.Err()) 31 32 var apierr *apierror.Error 33 require.True(t, errors.As(stream.Err(), &apierr)) 34 assert.Equal(t, shared.ErrorTypeOverloadedError, apierr.Type()) 35 assert.Equal(t, 200, apierr.StatusCode) 36 assert.Equal(t, "req_stream123", apierr.RequestID) 37 assert.Equal(t, "wrkspc_123", apierr.WorkspaceID) 38 assert.Contains(t, apierr.Error(), "200 OK") 39 assert.Contains(t, apierr.Error(), "(Request-ID: req_stream123) (Workspace-ID: wrkspc_123)") 40 assert.Contains(t, apierr.Error(), "overloaded_error") 41 } 42 43 func TestStreamingErrorMalformedBody(t *testing.T) { 44 sseBody := "event: error\ndata: not valid json\n\n" 45 httpResp := &http.Response{ 46 StatusCode: 200, 47 Body: io.NopCloser(strings.NewReader(sseBody)), 48 Request: mustNewRequest("POST", "https://api.anthropic.com/v1/messages"), 49 } 50 51 stream := ssestream.NewStream[json.RawMessage](ssestream.NewDecoder(httpResp), nil) 52 53 stream.Next() 54 require.Error(t, stream.Err()) 55 56 // Even with malformed JSON, UnmarshalJSON succeeds (gjson is lenient), 57 // so we still get an *apierror.Error — but Type() is empty. 58 var apierr *apierror.Error 59 require.True(t, errors.As(stream.Err(), &apierr)) 60 assert.Equal(t, shared.ErrorType(""), apierr.Type()) 61 } 62 63 // When the HTTP response has no Request (e.g. a synthetic response in tests), 64 // the decoder falls back to a plain-text error instead of constructing an 65 // *apierror.Error, since apierror.Error.Error() requires a non-nil Request. 66 func TestStreamingErrorNoRequest(t *testing.T) { 67 sseBody := "event: error\ndata: {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}\n\n" 68 httpResp := &http.Response{ 69 StatusCode: 200, 70 Body: io.NopCloser(strings.NewReader(sseBody)), 71 } 72 73 stream := ssestream.NewStream[json.RawMessage](ssestream.NewDecoder(httpResp), nil) 74 75 stream.Next() 76 require.Error(t, stream.Err()) 77 78 var apierr *apierror.Error 79 assert.False(t, errors.As(stream.Err(), &apierr), "expected plain error, not *apierror.Error, when Request is nil") 80 assert.Contains(t, stream.Err().Error(), "received error while streaming:") 81 } 82 83 func mustNewRequest(method, url string) *http.Request { 84 req, err := http.NewRequest(method, url, nil) 85 if err != nil { 86 panic(err) 87 } 88 return req 89 }