main.go raw
1 package main
2
3 import (
4 "context"
5 "flag"
6 "fmt"
7 "time"
8
9 "next.orly.dev/pkg/lol/chk"
10 "next.orly.dev/pkg/lol/log"
11 "next.orly.dev/pkg/nostr/encoders/event"
12 "next.orly.dev/pkg/nostr/encoders/filter"
13 "next.orly.dev/pkg/nostr/encoders/kind"
14 "next.orly.dev/pkg/nostr/encoders/tag"
15 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
16 "next.orly.dev/pkg/nostr/ws"
17 )
18
19 func main() {
20 var err error
21 url := flag.String("url", "ws://127.0.0.1:3334", "relay websocket URL")
22 timeout := flag.Duration("timeout", 20*time.Second, "operation timeout")
23 testType := flag.String("type", "event", "test type: 'event' for write control, 'req' for read control, 'both' for both, 'publish-and-query' for full test")
24 eventKind := flag.Int("kind", 4678, "event kind to test")
25 numEvents := flag.Int("count", 2, "number of events to publish (for publish-and-query)")
26 flag.Parse()
27
28 // Connect to relay
29 var rl *ws.Client
30 if rl, err = ws.RelayConnect(context.Background(), *url); chk.E(err) {
31 log.E.F("connect error: %v", err)
32 return
33 }
34 defer rl.Close()
35
36 // Create signer
37 var signer *p8k.Signer
38 if signer, err = p8k.New(); chk.E(err) {
39 log.E.F("signer create error: %v", err)
40 return
41 }
42 if err = signer.Generate(); chk.E(err) {
43 log.E.F("signer generate error: %v", err)
44 return
45 }
46
47 // Perform tests based on type
48 switch *testType {
49 case "event":
50 testEventWrite(rl, signer, *eventKind, *timeout)
51 case "req":
52 testReqRead(rl, signer, *eventKind, *timeout)
53 case "both":
54 log.I.Ln("Testing EVENT (write control)...")
55 testEventWrite(rl, signer, *eventKind, *timeout)
56 log.I.Ln("\nTesting REQ (read control)...")
57 testReqRead(rl, signer, *eventKind, *timeout)
58 case "publish-and-query":
59 testPublishAndQuery(rl, signer, *eventKind, *numEvents, *timeout)
60 default:
61 log.E.F("invalid test type: %s (must be 'event', 'req', 'both', or 'publish-and-query')", *testType)
62 }
63 }
64
65 func testEventWrite(rl *ws.Client, signer *p8k.Signer, eventKind int, timeout time.Duration) {
66 ev := &event.E{
67 CreatedAt: time.Now().Unix(),
68 Kind: uint16(eventKind),
69 Tags: tag.NewS(),
70 Content: []byte("policy test: expect rejection for write"),
71 }
72 if err := ev.Sign(signer); chk.E(err) {
73 log.E.F("sign error: %v", err)
74 return
75 }
76
77 ctx, cancel := context.WithTimeout(context.Background(), timeout)
78 defer cancel()
79
80 if err := rl.Publish(ctx, ev); err != nil {
81 // Expected path if policy rejects: client returns error with reason (from OK false)
82 fmt.Println("EVENT policy reject:", err)
83 return
84 }
85
86 log.I.Ln("EVENT publish result: accepted")
87 fmt.Println("EVENT ACCEPT")
88 }
89
90 func testReqRead(rl *ws.Client, signer *p8k.Signer, eventKind int, timeout time.Duration) {
91 // First, publish a test event to the relay that we'll try to query
92 testEvent := &event.E{
93 CreatedAt: time.Now().Unix(),
94 Kind: uint16(eventKind),
95 Tags: tag.NewS(),
96 Content: []byte("policy test: event for read control test"),
97 }
98 if err := testEvent.Sign(signer); chk.E(err) {
99 log.E.F("sign error: %v", err)
100 return
101 }
102
103 ctx, cancel := context.WithTimeout(context.Background(), timeout)
104 defer cancel()
105
106 // Try to publish the test event first (ignore errors if policy rejects)
107 _ = rl.Publish(ctx, testEvent)
108 log.I.F("published test event kind %d for read testing", eventKind)
109
110 // Now try to query for events of this kind
111 limit := uint(10)
112 f := &filter.F{
113 Kinds: kind.FromIntSlice([]int{eventKind}),
114 Limit: &limit,
115 }
116
117 ctx2, cancel2 := context.WithTimeout(context.Background(), timeout)
118 defer cancel2()
119
120 events, err := rl.QuerySync(ctx2, f)
121 if chk.E(err) {
122 log.E.F("query error: %v", err)
123 fmt.Println("REQ query error:", err)
124 return
125 }
126
127 // Check if we got the expected events
128 if len(events) == 0 {
129 // Could mean policy filtered it out, or it wasn't stored
130 fmt.Println("REQ policy reject: no events returned (filtered by read policy)")
131 log.I.F("REQ result: no events of kind %d returned (policy filtered or not stored)", eventKind)
132 return
133 }
134
135 // Events were returned - read access allowed
136 fmt.Printf("REQ ACCEPT: %d events returned\n", len(events))
137 log.I.F("REQ result: %d events of kind %d returned", len(events), eventKind)
138 }
139
140 func testPublishAndQuery(rl *ws.Client, signer *p8k.Signer, eventKind int, numEvents int, timeout time.Duration) {
141 log.I.F("Publishing %d events of kind %d...", numEvents, eventKind)
142
143 publishedIDs := make([][]byte, 0, numEvents)
144 acceptedCount := 0
145 rejectedCount := 0
146
147 // Publish multiple events
148 for i := 0; i < numEvents; i++ {
149 ev := &event.E{
150 CreatedAt: time.Now().Unix() + int64(i), // Slightly different timestamps
151 Kind: uint16(eventKind),
152 Tags: tag.NewS(),
153 Content: []byte(fmt.Sprintf("policy test event %d/%d", i+1, numEvents)),
154 }
155 if err := ev.Sign(signer); chk.E(err) {
156 log.E.F("sign error for event %d: %v", i+1, err)
157 continue
158 }
159
160 ctx, cancel := context.WithTimeout(context.Background(), timeout)
161 err := rl.Publish(ctx, ev)
162 cancel()
163
164 if err != nil {
165 log.W.F("Event %d/%d rejected: %v", i+1, numEvents, err)
166 rejectedCount++
167 } else {
168 log.I.F("Event %d/%d published successfully (id: %x...)", i+1, numEvents, ev.ID[:8])
169 publishedIDs = append(publishedIDs, ev.ID)
170 acceptedCount++
171 }
172 }
173
174 fmt.Printf("PUBLISH: %d accepted, %d rejected out of %d total\n", acceptedCount, rejectedCount, numEvents)
175
176 if acceptedCount == 0 {
177 fmt.Println("No events were accepted, skipping query test")
178 return
179 }
180
181 // Wait a moment for events to be stored
182 time.Sleep(500 * time.Millisecond)
183
184 // Now query for events of this kind
185 log.I.F("Querying for events of kind %d...", eventKind)
186
187 limit := uint(100)
188 f := &filter.F{
189 Kinds: kind.FromIntSlice([]int{eventKind}),
190 Limit: &limit,
191 }
192
193 ctx, cancel := context.WithTimeout(context.Background(), timeout)
194 defer cancel()
195
196 events, err := rl.QuerySync(ctx, f)
197 if chk.E(err) {
198 log.E.F("query error: %v", err)
199 fmt.Println("QUERY ERROR:", err)
200 return
201 }
202
203 log.I.F("Query returned %d events", len(events))
204
205 // Check if we got our published events back
206 foundCount := 0
207 for _, pubID := range publishedIDs {
208 found := false
209 for _, ev := range events {
210 if string(ev.ID) == string(pubID) {
211 found = true
212 break
213 }
214 }
215 if found {
216 foundCount++
217 }
218 }
219
220 fmt.Printf("QUERY: found %d/%d published events (total returned: %d)\n", foundCount, len(publishedIDs), len(events))
221
222 if foundCount == len(publishedIDs) {
223 fmt.Println("SUCCESS: All published events were retrieved")
224 } else if foundCount > 0 {
225 fmt.Printf("PARTIAL: Only %d/%d events retrieved (some filtered by read policy?)\n", foundCount, len(publishedIDs))
226 } else {
227 fmt.Println("FAILURE: None of the published events were retrieved (read policy blocked?)")
228 }
229 }
230