summaryrefslogtreecommitdiffstats
path: root/internal/handler/grpc/server_test.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-13 17:48:36 -0800
committerbndw <ben@bdw.to>2026-02-13 17:48:36 -0800
commit62d31434ddbadff18580826576e1169f539e23f0 (patch)
treea3e56da30e33ddf0dbfdea407df6724c9640a7b1 /internal/handler/grpc/server_test.go
parent5fcf5bd1fa5b2e707cea82c4652ea65c3c113c1a (diff)
feat: add gRPC handler with event validation and publishing
Handler implementation: - EventStore interface (consumer-side) - Server with PublishEvent, QueryEvents, CountEvents, PublishBatch - pb.Event <-> nostr.Event conversion helpers - Signature and ID validation using existing nostr package - Canonical JSON generation for storage 9 tests passing
Diffstat (limited to 'internal/handler/grpc/server_test.go')
-rw-r--r--internal/handler/grpc/server_test.go281
1 files changed, 281 insertions, 0 deletions
diff --git a/internal/handler/grpc/server_test.go b/internal/handler/grpc/server_test.go
new file mode 100644
index 0000000..12dde92
--- /dev/null
+++ b/internal/handler/grpc/server_test.go
@@ -0,0 +1,281 @@
1package grpc
2
3import (
4 "context"
5 "fmt"
6 "testing"
7 "time"
8
9 pb "northwest.io/nostr-grpc/api/nostr/v1"
10 "northwest.io/nostr-grpc/internal/nostr"
11 "northwest.io/nostr-grpc/internal/storage"
12)
13
14func TestPublishEvent(t *testing.T) {
15 store, err := storage.New(":memory:")
16 if err != nil {
17 t.Fatalf("failed to create storage: %v", err)
18 }
19 defer store.Close()
20
21 server := NewServer(store)
22 ctx := context.Background()
23
24 key, err := nostr.GenerateKey()
25 if err != nil {
26 t.Fatalf("failed to generate key: %v", err)
27 }
28
29 nostrEvent := &nostr.Event{
30 PubKey: key.Public(),
31 CreatedAt: time.Now().Unix(),
32 Kind: 1,
33 Tags: nostr.Tags{},
34 Content: "test event",
35 }
36
37 if err := key.Sign(nostrEvent); err != nil {
38 t.Fatalf("failed to sign event: %v", err)
39 }
40
41 pbEvent := NostrToPB(nostrEvent)
42
43 resp, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent})
44 if err != nil {
45 t.Fatalf("PublishEvent failed: %v", err)
46 }
47
48 if !resp.Accepted {
49 t.Errorf("expected event to be accepted, got: %s", resp.Message)
50 }
51 if resp.Message != "success" {
52 t.Errorf("expected success message, got: %s", resp.Message)
53 }
54 if len(resp.CanonicalJson) == 0 {
55 t.Error("expected canonical JSON to be returned")
56 }
57}
58
59func TestPublishEventDuplicate(t *testing.T) {
60 store, err := storage.New(":memory:")
61 if err != nil {
62 t.Fatalf("failed to create storage: %v", err)
63 }
64 defer store.Close()
65
66 server := NewServer(store)
67 ctx := context.Background()
68
69 key, err := nostr.GenerateKey()
70 if err != nil {
71 t.Fatalf("failed to generate key: %v", err)
72 }
73
74 nostrEvent := &nostr.Event{
75 PubKey: key.Public(),
76 CreatedAt: time.Now().Unix(),
77 Kind: 1,
78 Tags: nostr.Tags{},
79 Content: "duplicate test",
80 }
81
82 if err := key.Sign(nostrEvent); err != nil {
83 t.Fatalf("failed to sign event: %v", err)
84 }
85
86 pbEvent := NostrToPB(nostrEvent)
87
88 resp1, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent})
89 if err != nil {
90 t.Fatalf("first PublishEvent failed: %v", err)
91 }
92 if !resp1.Accepted {
93 t.Fatalf("first event should be accepted")
94 }
95
96 resp2, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent})
97 if err != nil {
98 t.Fatalf("second PublishEvent failed: %v", err)
99 }
100 if resp2.Accepted {
101 t.Error("duplicate event should not be accepted")
102 }
103 if resp2.Message != "duplicate: event already exists" {
104 t.Errorf("expected duplicate message, got: %s", resp2.Message)
105 }
106}
107
108func TestPublishEventInvalidSignature(t *testing.T) {
109 store, err := storage.New(":memory:")
110 if err != nil {
111 t.Fatalf("failed to create storage: %v", err)
112 }
113 defer store.Close()
114
115 server := NewServer(store)
116 ctx := context.Background()
117
118 key, err := nostr.GenerateKey()
119 if err != nil {
120 t.Fatalf("failed to generate key: %v", err)
121 }
122
123 nostrEvent := &nostr.Event{
124 PubKey: key.Public(),
125 CreatedAt: time.Now().Unix(),
126 Kind: 1,
127 Tags: nostr.Tags{},
128 Content: "test event",
129 }
130
131 if err := key.Sign(nostrEvent); err != nil {
132 t.Fatalf("failed to sign event: %v", err)
133 }
134
135 pbEvent := NostrToPB(nostrEvent)
136 pbEvent.Sig = "invalid_signature"
137
138 resp, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent})
139 if err != nil {
140 t.Fatalf("PublishEvent failed: %v", err)
141 }
142
143 if resp.Accepted {
144 t.Error("event with invalid signature should not be accepted")
145 }
146 if resp.Message != "invalid signature" {
147 t.Errorf("expected invalid signature message, got: %s", resp.Message)
148 }
149}
150
151func TestPublishEventInvalidID(t *testing.T) {
152 store, err := storage.New(":memory:")
153 if err != nil {
154 t.Fatalf("failed to create storage: %v", err)
155 }
156 defer store.Close()
157
158 server := NewServer(store)
159 ctx := context.Background()
160
161 pbEvent := &pb.Event{
162 Id: "wrong_id",
163 Pubkey: "pubkey123",
164 CreatedAt: time.Now().Unix(),
165 Kind: 1,
166 Tags: []*pb.Tag{},
167 Content: "test",
168 Sig: "sig123",
169 }
170
171 resp, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent})
172 if err != nil {
173 t.Fatalf("PublishEvent failed: %v", err)
174 }
175
176 if resp.Accepted {
177 t.Error("event with invalid ID should not be accepted")
178 }
179 if resp.Message != "invalid event ID" {
180 t.Errorf("expected invalid ID message, got: %s", resp.Message)
181 }
182}
183
184func TestQueryEvents(t *testing.T) {
185 store, err := storage.New(":memory:")
186 if err != nil {
187 t.Fatalf("failed to create storage: %v", err)
188 }
189 defer store.Close()
190
191 server := NewServer(store)
192 ctx := context.Background()
193
194 key, err := nostr.GenerateKey()
195 if err != nil {
196 t.Fatalf("failed to generate key: %v", err)
197 }
198
199 for i := 0; i < 3; i++ {
200 nostrEvent := &nostr.Event{
201 PubKey: key.Public(),
202 CreatedAt: time.Now().Unix(),
203 Kind: 1,
204 Tags: nostr.Tags{},
205 Content: fmt.Sprintf("test event %d", i),
206 }
207
208 if err := key.Sign(nostrEvent); err != nil {
209 t.Fatalf("failed to sign event: %v", err)
210 }
211
212 pbEvent := NostrToPB(nostrEvent)
213 if _, err := server.PublishEvent(ctx, &pb.PublishEventRequest{Event: pbEvent}); err != nil {
214 t.Fatalf("failed to publish event: %v", err)
215 }
216
217 time.Sleep(time.Millisecond)
218 }
219
220 resp, err := server.QueryEvents(ctx, &pb.QueryRequest{
221 Filters: []*pb.Filter{
222 {Authors: []string{key.Public()}},
223 },
224 })
225 if err != nil {
226 t.Fatalf("QueryEvents failed: %v", err)
227 }
228
229 if len(resp.Events) != 3 {
230 t.Errorf("expected 3 events, got %d", len(resp.Events))
231 }
232}
233
234func TestPublishBatch(t *testing.T) {
235 store, err := storage.New(":memory:")
236 if err != nil {
237 t.Fatalf("failed to create storage: %v", err)
238 }
239 defer store.Close()
240
241 server := NewServer(store)
242 ctx := context.Background()
243
244 key, err := nostr.GenerateKey()
245 if err != nil {
246 t.Fatalf("failed to generate key: %v", err)
247 }
248
249 var events []*pb.Event
250 for i := 0; i < 3; i++ {
251 nostrEvent := &nostr.Event{
252 PubKey: key.Public(),
253 CreatedAt: time.Now().Unix(),
254 Kind: 1,
255 Tags: nostr.Tags{},
256 Content: fmt.Sprintf("batch test %d", i),
257 }
258
259 if err := key.Sign(nostrEvent); err != nil {
260 t.Fatalf("failed to sign event: %v", err)
261 }
262
263 events = append(events, NostrToPB(nostrEvent))
264 time.Sleep(time.Millisecond)
265 }
266
267 resp, err := server.PublishBatch(ctx, &pb.PublishBatchRequest{Events: events})
268 if err != nil {
269 t.Fatalf("PublishBatch failed: %v", err)
270 }
271
272 if len(resp.Results) != 3 {
273 t.Fatalf("expected 3 results, got %d", len(resp.Results))
274 }
275
276 for i, result := range resp.Results {
277 if !result.Accepted {
278 t.Errorf("event %d should be accepted, got: %s", i, result.Message)
279 }
280 }
281}