返回 DeepSeek-Reasonix
errors_test.go
根目录 / internal / extension / protocol / errors_test.go
1 package protocol
2
3 import (
4 "encoding/json"
5 "testing"
6
7 "reasonix/internal/extension/rpcwire"
8 )
9
10 func TestErrorTableCoversRequiredReasons(t *testing.T) {
11 required := []ErrorReason{
12 ErrProtocolError, ErrUnknownMethod, ErrInvalidParams, ErrFrameTooLarge,
13 ErrContentRefExpired, ErrUnsupportedVersion, ErrCapabilityNotDeclared,
14 ErrShutdownTimeout, ErrStreamGap, ErrStreamCancelled,
15 ErrProviderFailed, ErrProviderInterrupted, ErrInterceptTimeout,
16 ErrDependencyUnsatisfied, ErrDependencyCycle, ErrSchemaMismatch,
17 ErrActivationFailed, ErrStaleGeneration, ErrCleanupFailed, ErrInternal,
18 }
19 if len(frozenErrorSpecs) != len(required) {
20 t.Fatalf("frozen error table has %d entries, want %d", len(frozenErrorSpecs), len(required))
21 }
22 for _, reason := range required {
23 if _, ok := frozenErrorSpecs[reason]; !ok {
24 t.Fatalf("frozen error table is missing %q", reason)
25 }
26 }
27 }
28
29 func TestErrorContractsAreUniqueSortedAndWellFormed(t *testing.T) {
30 contracts := ErrorContracts()
31 seen := map[ErrorReason]bool{}
32 for i, contract := range contracts {
33 if seen[contract.Reason] {
34 t.Fatalf("duplicate error reason %q", contract.Reason)
35 }
36 seen[contract.Reason] = true
37 if i > 0 && contracts[i-1].Reason >= contract.Reason {
38 t.Fatalf("error contracts not sorted at %q", contract.Reason)
39 }
40 if contract.Message == "" {
41 t.Fatalf("%q has an empty frozen message", contract.Reason)
42 }
43 switch contract.JSONRPCCode {
44 case DomainErrorCode, rpcwire.ErrInvalidRequest, rpcwire.ErrMethodNotFound,
45 rpcwire.ErrInvalidParams, rpcwire.ErrInternal:
46 default:
47 t.Fatalf("%q uses non-contract JSON-RPC code %d", contract.Reason, contract.JSONRPCCode)
48 }
49 spec := frozenErrorSpecs[contract.Reason]
50 if spec.Code != contract.JSONRPCCode || spec.Message != contract.Message || spec.Retryable != contract.Retryable {
51 t.Fatalf("%q contract does not match its frozen spec", contract.Reason)
52 }
53 }
54 }
55
56 func TestProtocolErrorWireShape(t *testing.T) {
57 err := MustProtocolError(ErrStreamGap)
58 if err.Error() == "" {
59 t.Fatal("ProtocolError.Error() is empty")
60 }
61 rpcErr := err.RPCError()
62 if rpcErr.Code != DomainErrorCode {
63 t.Fatalf("RPCError code = %d, want %d", rpcErr.Code, DomainErrorCode)
64 }
65 raw, marshalErr := json.Marshal(rpcErr.Data)
66 if marshalErr != nil {
67 t.Fatal(marshalErr)
68 }
69 var decoded ProtocolErrorData
70 if unmarshalErr := json.Unmarshal(raw, &decoded); unmarshalErr != nil {
71 t.Fatal(unmarshalErr)
72 }
73 if decoded.Reason != ErrStreamGap || !decoded.Retryable {
74 t.Fatalf("wire data = %+v, want stream_gap/retryable", decoded)
75 }
76 if validateErr := decoded.Validate(); validateErr != nil {
77 t.Fatalf("wire data failed validation: %v", validateErr)
78 }
79
80 // Standard JSON-RPC codes ride on the mapped reasons.
81 standard := map[ErrorReason]int{
82 ErrProtocolError: rpcwire.ErrInvalidRequest,
83 ErrUnknownMethod: rpcwire.ErrMethodNotFound,
84 ErrInvalidParams: rpcwire.ErrInvalidParams,
85 ErrInternal: rpcwire.ErrInternal,
86 }
87 for reason, code := range standard {
88 if got := MustProtocolError(reason).RPCError().Code; got != code {
89 t.Fatalf("%s RPCError code = %d, want %d", reason, got, code)
90 }
91 }
92
93 var nilErr *ProtocolError
94 if nilErr.Error() != "" || nilErr.RPCError().Code != rpcwire.ErrInternal {
95 t.Fatal("nil ProtocolError must degrade to a plain internal error")
96 }
97 }
98
99 func TestProtocolErrorDataValidate(t *testing.T) {
100 if err := (ProtocolErrorData{Reason: ErrShutdownTimeout, Retryable: true}).Validate(); err != nil {
101 t.Fatalf("valid data rejected: %v", err)
102 }
103 if err := (ProtocolErrorData{Reason: ErrShutdownTimeout, Retryable: false}).Validate(); err == nil {
104 t.Fatal("retryable mismatch accepted")
105 }
106 if err := (ProtocolErrorData{Reason: "bogus"}).Validate(); err == nil {
107 t.Fatal("unknown reason accepted")
108 }
109 if _, err := NewProtocolError("bogus"); err == nil {
110 t.Fatal("NewProtocolError accepted an unknown reason")
111 }
112 }
113
113 lines GO