返回 DeepSeek-Reasonix
registry_test.go
根目录 / internal / extension / protocol / registry_test.go
1 package protocol
2
3 import (
4 "strings"
5 "testing"
6 )
7
8 func TestRegistryIsSortedAndPinned(t *testing.T) {
9 if err := ValidateRegistry(); err != nil {
10 t.Fatalf("ValidateRegistry: %v", err)
11 }
12 registry := Registry()
13 if len(registry) != 16 {
14 t.Fatalf("registry has %d methods, want 16", len(registry))
15 }
16 for i := 1; i < len(registry); i++ {
17 if registry[i-1].Name >= registry[i].Name {
18 t.Fatalf("registry is not strictly sorted at %q >= %q", registry[i-1].Name, registry[i].Name)
19 }
20 }
21 }
22
23 func TestRegistryMethodDirections(t *testing.T) {
24 want := map[Method]Direction{
25 MethodExtensionInitialize: DirectionHostToExtensionRequest,
26 MethodExtensionInitialized: DirectionHostToExtensionNotification,
27 MethodExtensionShutdown: DirectionHostToExtensionRequest,
28 MethodExtensionIntercept: DirectionHostToExtensionRequest,
29 MethodExtensionEvent: DirectionHostToExtensionNotification,
30 MethodExtensionResourcesChanged: DirectionHostToExtensionNotification,
31 MethodExtensionProviderCatalog: DirectionHostToExtensionRequest,
32 MethodExtensionProviderStreamOpen: DirectionHostToExtensionRequest,
33 MethodExtensionProviderStreamCancel: DirectionHostToExtensionRequest,
34 MethodExtensionProviderStreamChunk: DirectionExtensionToHostNotification,
35 MethodExtensionProviderStreamEnd: DirectionExtensionToHostNotification,
36 MethodExtensionUIAction: DirectionHostToExtensionRequest,
37 MethodExtensionUISubmit: DirectionHostToExtensionRequest,
38 MethodHostUIPublish: DirectionExtensionToHostRequest,
39 MethodHostUIRequest: DirectionExtensionToHostRequest,
40 MethodHostContentRead: DirectionExtensionToHostRequest,
41 }
42 if len(want) != 16 {
43 t.Fatalf("test pins %d methods, want 16", len(want))
44 }
45 for method, direction := range want {
46 spec, ok := LookupMethod(method)
47 if !ok {
48 t.Fatalf("LookupMethod(%q) not found", method)
49 }
50 if spec.Direction != direction {
51 t.Fatalf("%s direction = %q, want %q", method, spec.Direction, direction)
52 }
53 if spec.Notification() != direction.IsNotification() {
54 t.Fatalf("%s notification flag disagrees with direction %q", method, direction)
55 }
56 }
57 if _, ok := LookupMethod("extension/bogus"); ok {
58 t.Fatal("LookupMethod accepted an unregistered method")
59 }
60 }
61
62 func TestRegistryClasses(t *testing.T) {
63 want := map[Method]OperationClass{
64 MethodExtensionInitialize: ClassLifecycle,
65 MethodExtensionInitialized: ClassLifecycle,
66 MethodExtensionShutdown: ClassLifecycle,
67 MethodExtensionIntercept: ClassIntercept,
68 MethodExtensionEvent: ClassObservation,
69 MethodExtensionResourcesChanged: ClassObservation,
70 MethodExtensionProviderCatalog: ClassProvider,
71 MethodExtensionProviderStreamOpen: ClassProvider,
72 MethodExtensionProviderStreamCancel: ClassProvider,
73 MethodExtensionProviderStreamChunk: ClassProvider,
74 MethodExtensionProviderStreamEnd: ClassProvider,
75 MethodExtensionUIAction: ClassUI,
76 MethodExtensionUISubmit: ClassUI,
77 MethodHostUIPublish: ClassUI,
78 MethodHostUIRequest: ClassUI,
79 MethodHostContentRead: ClassContent,
80 }
81 for method, class := range want {
82 spec, _ := LookupMethod(method)
83 if spec.Class != class {
84 t.Fatalf("%s class = %q, want %q", method, spec.Class, class)
85 }
86 }
87 }
88
89 func TestDecodeHelpersRejectWrongDirection(t *testing.T) {
90 raw := []byte(`{}`)
91 if _, err := DecodeHostRequestParams(MethodHostContentRead, raw); err == nil {
92 t.Fatal("DecodeHostRequestParams accepted an extension request method")
93 }
94 if _, err := DecodeExtensionRequestParams(MethodExtensionInitialize, raw); err == nil {
95 t.Fatal("DecodeExtensionRequestParams accepted a host request method")
96 }
97 if _, err := DecodeHostNotificationParams(MethodExtensionEvent, []byte(`{"event":"session.start","payload":{}}`)); err == nil {
98 // extension/event IS a host notification; must decode.
99 } else {
100 t.Fatalf("DecodeHostNotificationParams(extension/event) = %v", err)
101 }
102 if _, err := DecodeExtensionNotificationParams(MethodExtensionEvent, raw); err == nil {
103 t.Fatal("DecodeExtensionNotificationParams accepted a host notification method")
104 }
105 if _, err := DecodeHostRequestParams("extension/bogus", raw); err == nil {
106 t.Fatal("DecodeHostRequestParams accepted an unregistered method")
107 }
108 if _, err := DecodeHostRequestResult(MethodExtensionEvent, raw); err == nil {
109 t.Fatal("DecodeHostRequestResult accepted a notification (no result)")
110 }
111 if _, err := DecodeExtensionRequestResult(MethodExtensionProviderStreamChunk, raw); err == nil {
112 t.Fatal("DecodeExtensionRequestResult accepted a notification method")
113 }
114 }
115
116 func TestInterceptEventsFrozen(t *testing.T) {
117 events := InterceptEvents()
118 if len(events) != 17 {
119 t.Fatalf("InterceptEvents has %d entries, want 17", len(events))
120 }
121 seen := map[string]bool{}
122 for i, event := range events {
123 if seen[event] {
124 t.Fatalf("duplicate intercept event %q", event)
125 }
126 seen[event] = true
127 if i > 0 && events[i-1] >= event {
128 t.Fatalf("intercept events not sorted at %q", event)
129 }
130 if !strings.Contains(event, ".") {
131 t.Fatalf("intercept event %q does not follow the <area>.<point> shape", event)
132 }
133 }
134 // Every frozen event must round-trip through the strict enum check.
135 for _, event := range events {
136 raw := []byte(`{"event":"` + event + `","payload":{}}`)
137 if _, err := DecodeHostNotificationParams(MethodExtensionEvent, raw); err != nil {
138 t.Fatalf("frozen event %q rejected: %v", event, err)
139 }
140 }
141 raw := []byte(`{"event":"session.bogus","payload":{}}`)
142 if _, err := DecodeHostNotificationParams(MethodExtensionEvent, raw); err == nil {
143 t.Fatal("unknown intercept event accepted")
144 }
145 }
146
146 lines GO