-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy patherrors_test.go
More file actions
82 lines (78 loc) · 3.35 KB
/
Copy patherrors_test.go
File metadata and controls
82 lines (78 loc) · 3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package litellm
import (
"context"
"errors"
"fmt"
"io"
"testing"
)
func TestErrorRendering(t *testing.T) {
for _, tc := range []struct {
err *Error
want string
}{
{&Error{Provider: "p", Code: "c", Message: " boom "}, "p: c: boom"},
{&Error{Message: "boom"}, "boom"},
{&Error{Provider: "p", Code: "c"}, "p: c"},
{&Error{Provider: "p", StatusCode: 503, Type: ErrorTypeProvider}, "p: HTTP 503 (provider)"},
{&Error{StatusCode: 503}, "HTTP 503"},
{&Error{Provider: "openrouter", Message: "Provider returned error", StatusCode: 502}, "openrouter: Provider returned error (HTTP 502)"},
{&Error{Code: "c", Message: "upstream said HTTP 429", StatusCode: 429}, "c: upstream said HTTP 429"},
{&Error{Type: ErrorTypeValidation}, "validation"},
{&Error{}, "litellm error"},
{NewNetworkError("p", "read failed", io.EOF), "p: read failed: EOF"},
{NewNetworkError("p", "read: EOF", io.EOF), "p: read: EOF"},
{NewError("p", ErrorTypeProvider, "decode", io.EOF), "p: decode"},
} {
if got := tc.err.Error(); got != tc.want {
t.Errorf("Error() = %q, want %q", got, tc.want)
}
}
if got := WrapError("p", ErrorTypeProvider, errors.New("boom")).Error(); got != "p: boom" {
t.Errorf("wrapped plain error = %q", got)
}
}
func TestTemporaryErrorClassification(t *testing.T) {
for _, tc := range []struct {
name string
err error
temporary bool
}{
{"unknown_provider", NewError("test", ErrorTypeProvider, "unknown", nil), false},
{"network_outcome_unknown", NewNetworkError("test", "read failed", errors.New("EOF")), true},
{"caller_canceled", NewNetworkError("test", "canceled", context.Canceled), false},
{"caller_deadline", NewNetworkError("test", "deadline", context.DeadlineExceeded), false},
{"timeout", NewError("test", ErrorTypeTimeout, "deadline", nil), false},
} {
t.Run(tc.name, func(t *testing.T) {
if got := IsTemporaryError(fmt.Errorf("call: %w", tc.err)); got != tc.temporary {
t.Fatalf("IsTemporaryError = %v, want %v", got, tc.temporary)
}
})
}
}
func TestWrapErrorPreservesCauseAndClassification(t *testing.T) {
original := &Error{Type: ErrorTypeProvider, StatusCode: 503, Temporary: true, Message: "unavailable"}
wrapped := WrapError("test", ErrorTypeProvider, original)
var typed *Error
if !errors.As(wrapped, &typed) || typed.Provider != "test" || !typed.Temporary || typed.StatusCode != 503 {
t.Fatalf("wrapped = %#v", wrapped)
}
if original.Provider != "" {
t.Fatal("WrapError mutated original")
}
canceled := WrapError("test", ErrorTypeProvider, fmt.Errorf("transport: %w", context.Canceled))
if ErrorTypeOf(canceled) != ErrorTypeCanceled || IsTemporaryError(canceled) || !errors.Is(canceled, context.Canceled) {
t.Fatalf("canceled error = %v", canceled)
}
deadline := WrapError("test", ErrorTypeProvider, fmt.Errorf("transport: %w", context.DeadlineExceeded))
if ErrorTypeOf(deadline) != ErrorTypeTimeout || IsTemporaryError(deadline) || !errors.Is(deadline, context.DeadlineExceeded) {
t.Fatalf("deadline error = %v", deadline)
}
// A provider error caused by cancellation is reclassified without
// repeating its provider prefix.
read := WrapError("test", ErrorTypeProvider, NewError("test", ErrorTypeProvider, "read stream", context.DeadlineExceeded))
if ErrorTypeOf(read) != ErrorTypeTimeout || read.Error() != "test: read stream: context deadline exceeded" {
t.Fatalf("read error = %v", read)
}
}