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