benchmark_test.go raw
1 package policy
2
3 import (
4 "context"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 "next.orly.dev/pkg/lol/chk"
12 "next.orly.dev/pkg/nostr/encoders/event"
13 "next.orly.dev/pkg/nostr/encoders/hex"
14 "next.orly.dev/pkg/nostr/encoders/tag"
15 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
16 )
17
18 // Helper function to create test event for benchmarks (reuses signer)
19 func createTestEventBench(b *testing.B, signer *p8k.Signer, content string, kind uint16) *event.E {
20 ev := event.New()
21 ev.CreatedAt = time.Now().Unix()
22 ev.Kind = kind
23 ev.Content = []byte(content)
24 ev.Tags = tag.NewS()
25
26 // Sign the event properly
27 if err := ev.Sign(signer); chk.E(err) {
28 b.Fatalf("Failed to sign test event: %v", err)
29 }
30
31 return ev
32 }
33
34 func BenchmarkCheckKindsPolicy(b *testing.B) {
35 policy := &P{
36 Kind: Kinds{
37 Whitelist: []int{1, 3, 5, 7, 9735},
38 },
39 }
40
41 b.ResetTimer()
42 for i := 0; i < b.N; i++ {
43 policy.checkKindsPolicy("write", 1)
44 }
45 }
46
47 func BenchmarkCheckRulePolicy(b *testing.B) {
48 // Generate keypair once for all events
49 signer, pubkey := generateTestKeypairB(b)
50 testEvent := createTestEventBench(b, signer, "test content", 1)
51
52 rule := Rule{
53 Description: "test rule",
54 AccessControl: AccessControl{
55 WriteAllow: []string{hex.Enc(pubkey)},
56 },
57 Constraints: Constraints{
58 SizeLimit: int64Ptr(10000),
59 ContentLimit: int64Ptr(1000),
60 },
61 TagValidationConfig: TagValidationConfig{
62 MustHaveTags: []string{"p"},
63 },
64 }
65
66 policy := &P{}
67
68 b.ResetTimer()
69 for i := 0; i < b.N; i++ {
70 policy.checkRulePolicy("write", testEvent, rule, pubkey)
71 }
72 }
73
74 func BenchmarkCheckPolicy(b *testing.B) {
75 // Generate keypair once for all events
76 signer, pubkey := generateTestKeypairB(b)
77 testEvent := createTestEventBench(b, signer, "test content", 1)
78
79 policy := &P{
80 Kind: Kinds{
81 Whitelist: []int{1, 3, 5},
82 },
83 rules: map[int]Rule{
84 1: {
85 Description: "test rule",
86 AccessControl: AccessControl{
87 WriteAllow: []string{hex.Enc(pubkey)},
88 },
89 },
90 },
91 }
92
93 b.ResetTimer()
94 for i := 0; i < b.N; i++ {
95 policy.CheckPolicy("write", testEvent, pubkey, "127.0.0.1")
96 }
97 }
98
99 func BenchmarkCheckPolicyWithScript(b *testing.B) {
100 // Create temporary directory
101 tempDir := b.TempDir()
102 scriptPath := filepath.Join(tempDir, "policy.sh")
103
104 // Create a simple test script
105 scriptContent := `#!/bin/bash
106 while IFS= read -r line; do
107 echo '{"id":"test","action":"accept","msg":""}'
108 done
109 `
110 err := os.WriteFile(scriptPath, []byte(scriptContent), 0755)
111 if err != nil {
112 b.Fatalf("Failed to create test script: %v", err)
113 }
114
115 ctx, cancel := context.WithCancel(context.Background())
116 defer cancel()
117
118 manager := &PolicyManager{
119 ctx: ctx,
120 cancel: cancel,
121 configDir: tempDir,
122 scriptPath: scriptPath,
123 enabled: true,
124 runners: make(map[string]*ScriptRunner),
125 }
126
127 // Get or create runner and start it
128 runner := manager.getOrCreateRunner(scriptPath)
129 err = runner.Start()
130 if err != nil {
131 b.Fatalf("Failed to start policy script: %v", err)
132 }
133 defer runner.Stop()
134
135 // Give the script time to start
136 time.Sleep(100 * time.Millisecond)
137
138 // Generate keypair once for all events
139 signer, pubkey := generateTestKeypairB(b)
140 testEvent := createTestEventBench(b, signer, "test content", 1)
141
142 policy := &P{
143 manager: manager,
144 Kind: Kinds{},
145 rules: map[int]Rule{
146 1: {
147 Description: "test rule with script",
148 Script: "policy.sh",
149 },
150 },
151 }
152
153 b.ResetTimer()
154 for i := 0; i < b.N; i++ {
155 policy.CheckPolicy("write", testEvent, pubkey, "127.0.0.1")
156 }
157 }
158
159 func BenchmarkLoadFromFile(b *testing.B) {
160 // Create temporary directory
161 tempDir := b.TempDir()
162 configPath := filepath.Join(tempDir, "policy.json")
163
164 // Create test config
165 configData := `{
166 "kind": {
167 "whitelist": [1, 3, 5, 7, 9735],
168 "blacklist": []
169 },
170 "rules": {
171 "1": {
172 "description": "text notes",
173 "write_allow": [],
174 "size_limit": 32000
175 },
176 "3": {
177 "description": "contacts",
178 "write_allow": ["npub1example1"],
179 "script": "policy.sh"
180 }
181 }
182 }`
183
184 err := os.WriteFile(configPath, []byte(configData), 0644)
185 if err != nil {
186 b.Fatalf("Failed to create test config: %v", err)
187 }
188
189 policy := &P{}
190
191 b.ResetTimer()
192 for i := 0; i < b.N; i++ {
193 err := policy.LoadFromFile(configPath)
194 if err != nil {
195 b.Fatalf("Failed to load config: %v", err)
196 }
197 }
198 }
199
200 func BenchmarkCheckPolicyMultipleKinds(b *testing.B) {
201 // Create policy with many rules
202 rules := make(map[int]Rule)
203 for i := 1; i <= 100; i++ {
204 rules[i] = Rule{
205 Description: "test rule",
206 AccessControl: AccessControl{
207 WriteAllow: []string{"test-pubkey"},
208 },
209 }
210 }
211
212 policy := &P{
213 Kind: Kinds{},
214 rules: rules,
215 }
216
217 // Generate keypair once for all events
218 signer, pubkey := generateTestKeypairB(b)
219
220 // Create test events with different kinds
221 events := make([]*event.E, 100)
222 for i := 0; i < 100; i++ {
223 events[i] = createTestEventBench(b, signer, "test content", uint16(i+1))
224 }
225
226 b.ResetTimer()
227 for i := 0; i < b.N; i++ {
228 event := events[i%100]
229 policy.CheckPolicy("write", event, pubkey, "127.0.0.1")
230 }
231 }
232
233 func BenchmarkCheckPolicyLargeWhitelist(b *testing.B) {
234 // Create large whitelist
235 whitelist := make([]int, 1000)
236 for i := 0; i < 1000; i++ {
237 whitelist[i] = i + 1
238 }
239
240 policy := &P{
241 Kind: Kinds{
242 Whitelist: whitelist,
243 },
244 rules: map[int]Rule{},
245 }
246
247 // Generate keypair once for all events
248 signer, pubkey := generateTestKeypairB(b)
249 testEvent := createTestEventBench(b, signer, "test content", 500) // Kind in the middle of the whitelist
250
251 b.ResetTimer()
252 for i := 0; i < b.N; i++ {
253 policy.CheckPolicy("write", testEvent, pubkey, "127.0.0.1")
254 }
255 }
256
257 func BenchmarkCheckPolicyLargeBlacklist(b *testing.B) {
258 // Create large blacklist
259 blacklist := make([]int, 1000)
260 for i := 0; i < 1000; i++ {
261 blacklist[i] = i + 1
262 }
263
264 policy := &P{
265 Kind: Kinds{
266 Blacklist: blacklist,
267 },
268 rules: map[int]Rule{},
269 }
270
271 // Generate keypair once for all events
272 signer, pubkey := generateTestKeypairB(b)
273 testEvent := createTestEventBench(b, signer, "test content", 1500) // Kind not in blacklist
274
275 b.ResetTimer()
276 for i := 0; i < b.N; i++ {
277 policy.CheckPolicy("write", testEvent, pubkey, "127.0.0.1")
278 }
279 }
280
281 func BenchmarkCheckPolicyComplexRule(b *testing.B) {
282 // Generate keypair once for all events
283 signer, pubkey := generateTestKeypairB(b)
284 testEvent := createTestEventBench(b, signer, "test content", 1)
285
286 // Add many tags
287 for i := 0; i < 100; i++ {
288 tagItem1 := tag.New()
289 tagItem1.T = append(tagItem1.T, []byte("p"), []byte(hex.Enc(pubkey)))
290 *testEvent.Tags = append(*testEvent.Tags, tagItem1)
291
292 tagItem2 := tag.New()
293 tagItem2.T = append(tagItem2.T, []byte("e"), []byte("test-event"))
294 *testEvent.Tags = append(*testEvent.Tags, tagItem2)
295 }
296
297 rule := Rule{
298 Description: "complex rule",
299 AccessControl: AccessControl{
300 WriteAllow: []string{hex.Enc(pubkey)},
301 },
302 Constraints: Constraints{
303 SizeLimit: int64Ptr(100000),
304 ContentLimit: int64Ptr(10000),
305 Privileged: true,
306 },
307 TagValidationConfig: TagValidationConfig{
308 MustHaveTags: []string{"p", "e"},
309 },
310 }
311
312 policy := &P{}
313
314 b.ResetTimer()
315 for i := 0; i < b.N; i++ {
316 policy.checkRulePolicy("write", testEvent, rule, pubkey)
317 }
318 }
319
320 func BenchmarkCheckPolicyLargeEvent(b *testing.B) {
321 // Create large content
322 largeContent := strings.Repeat("a", 100000) // 100KB content
323
324 policy := &P{
325 Kind: Kinds{},
326 rules: map[int]Rule{
327 1: {
328 Description: "size limit test",
329 Constraints: Constraints{
330 SizeLimit: int64Ptr(200000), // 200KB limit
331 ContentLimit: int64Ptr(200000), // 200KB content limit
332 },
333 },
334 },
335 }
336
337 // Generate keypair once for all events
338 signer, pubkey := generateTestKeypairB(b)
339 testEvent := createTestEventBench(b, signer, largeContent, 1)
340
341 b.ResetTimer()
342 for i := 0; i < b.N; i++ {
343 policy.CheckPolicy("write", testEvent, pubkey, "127.0.0.1")
344 }
345 }
346