tests.go raw

   1  package relaytester
   2  
   3  import (
   4  	"encoding/json"
   5  	"fmt"
   6  	"strings"
   7  	"time"
   8  
   9  	"next.orly.dev/pkg/nostr/encoders/event"
  10  	"next.orly.dev/pkg/nostr/encoders/hex"
  11  	"next.orly.dev/pkg/nostr/encoders/kind"
  12  	"next.orly.dev/pkg/nostr/encoders/tag"
  13  )
  14  
  15  // Test implementations - these are referenced by test.go
  16  
  17  func testPublishBasicEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
  18  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "test content", nil)
  19  	if err != nil {
  20  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
  21  	}
  22  	if err = client.Publish(ev); err != nil {
  23  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
  24  	}
  25  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
  26  	if err != nil {
  27  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
  28  	}
  29  	if !accepted {
  30  		return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
  31  	}
  32  	return TestResult{Pass: true}
  33  }
  34  
  35  func testFindByID(client *Client, key1, key2 *KeyPair) (result TestResult) {
  36  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by id test", nil)
  37  	if err != nil {
  38  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
  39  	}
  40  	if err = client.Publish(ev); err != nil {
  41  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
  42  	}
  43  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
  44  	if err != nil || !accepted {
  45  		return TestResult{Pass: false, Info: "event not accepted"}
  46  	}
  47  	time.Sleep(200 * time.Millisecond)
  48  	filter := map[string]interface{}{
  49  		"ids": []string{hex.Enc(ev.ID)},
  50  	}
  51  	events, err := client.GetEvents("test-sub", []interface{}{filter}, 2*time.Second)
  52  	if err != nil {
  53  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
  54  	}
  55  	found := false
  56  	for _, e := range events {
  57  		if string(e.ID) == string(ev.ID) {
  58  			found = true
  59  			break
  60  		}
  61  	}
  62  	if !found {
  63  		return TestResult{Pass: false, Info: "event not found by ID"}
  64  	}
  65  	return TestResult{Pass: true}
  66  }
  67  
  68  func testFindByAuthor(client *Client, key1, key2 *KeyPair) (result TestResult) {
  69  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by author test", nil)
  70  	if err != nil {
  71  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
  72  	}
  73  	if err = client.Publish(ev); err != nil {
  74  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
  75  	}
  76  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
  77  	if err != nil || !accepted {
  78  		return TestResult{Pass: false, Info: "event not accepted"}
  79  	}
  80  	time.Sleep(200 * time.Millisecond)
  81  	filter := map[string]interface{}{
  82  		"authors": []string{hex.Enc(key1.Pubkey)},
  83  	}
  84  	events, err := client.GetEvents("test-author", []interface{}{filter}, 2*time.Second)
  85  	if err != nil {
  86  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
  87  	}
  88  	found := false
  89  	for _, e := range events {
  90  		if string(e.ID) == string(ev.ID) {
  91  			found = true
  92  			break
  93  		}
  94  	}
  95  	if !found {
  96  		return TestResult{Pass: false, Info: "event not found by author"}
  97  	}
  98  	return TestResult{Pass: true}
  99  }
 100  
 101  func testFindByKind(client *Client, key1, key2 *KeyPair) (result TestResult) {
 102  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by kind test", nil)
 103  	if err != nil {
 104  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 105  	}
 106  	if err = client.Publish(ev); err != nil {
 107  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 108  	}
 109  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 110  	if err != nil || !accepted {
 111  		return TestResult{Pass: false, Info: "event not accepted"}
 112  	}
 113  	time.Sleep(200 * time.Millisecond)
 114  	filter := map[string]interface{}{
 115  		"kinds": []int{int(kind.TextNote.K)},
 116  	}
 117  	events, err := client.GetEvents("test-kind", []interface{}{filter}, 2*time.Second)
 118  	if err != nil {
 119  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 120  	}
 121  	found := false
 122  	for _, e := range events {
 123  		if string(e.ID) == string(ev.ID) {
 124  			found = true
 125  			break
 126  		}
 127  	}
 128  	if !found {
 129  		return TestResult{Pass: false, Info: "event not found by kind"}
 130  	}
 131  	return TestResult{Pass: true}
 132  }
 133  
 134  func testFindByTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
 135  	tags := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("test-tag")))
 136  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by tags test", tags)
 137  	if err != nil {
 138  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 139  	}
 140  	if err = client.Publish(ev); err != nil {
 141  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 142  	}
 143  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 144  	if err != nil || !accepted {
 145  		return TestResult{Pass: false, Info: "event not accepted"}
 146  	}
 147  	time.Sleep(200 * time.Millisecond)
 148  	filter := map[string]interface{}{
 149  		"#t": []string{"test-tag"},
 150  	}
 151  	events, err := client.GetEvents("test-tags", []interface{}{filter}, 2*time.Second)
 152  	if err != nil {
 153  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 154  	}
 155  	found := false
 156  	for _, e := range events {
 157  		if string(e.ID) == string(ev.ID) {
 158  			found = true
 159  			break
 160  		}
 161  	}
 162  	if !found {
 163  		return TestResult{Pass: false, Info: "event not found by tags"}
 164  	}
 165  	return TestResult{Pass: true}
 166  }
 167  
 168  func testFindByMultipleTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
 169  	tags := tag.NewS(
 170  		tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-1")),
 171  		tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-2")),
 172  	)
 173  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by multiple tags test", tags)
 174  	if err != nil {
 175  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 176  	}
 177  	if err = client.Publish(ev); err != nil {
 178  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 179  	}
 180  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 181  	if err != nil || !accepted {
 182  		return TestResult{Pass: false, Info: "event not accepted"}
 183  	}
 184  	time.Sleep(200 * time.Millisecond)
 185  	filter := map[string]interface{}{
 186  		"#t": []string{"multi-tag-1", "multi-tag-2"},
 187  	}
 188  	events, err := client.GetEvents("test-multi-tags", []interface{}{filter}, 2*time.Second)
 189  	if err != nil {
 190  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 191  	}
 192  	found := false
 193  	for _, e := range events {
 194  		if string(e.ID) == string(ev.ID) {
 195  			found = true
 196  			break
 197  		}
 198  	}
 199  	if !found {
 200  		return TestResult{Pass: false, Info: "event not found by multiple tags"}
 201  	}
 202  	return TestResult{Pass: true}
 203  }
 204  
 205  func testFindByTimeRange(client *Client, key1, key2 *KeyPair) (result TestResult) {
 206  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by time range test", nil)
 207  	if err != nil {
 208  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 209  	}
 210  	if err = client.Publish(ev); err != nil {
 211  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 212  	}
 213  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 214  	if err != nil || !accepted {
 215  		return TestResult{Pass: false, Info: "event not accepted"}
 216  	}
 217  	time.Sleep(200 * time.Millisecond)
 218  	now := time.Now().Unix()
 219  	filter := map[string]interface{}{
 220  		"since": now - 3600,
 221  		"until": now + 3600,
 222  	}
 223  	events, err := client.GetEvents("test-time", []interface{}{filter}, 2*time.Second)
 224  	if err != nil {
 225  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 226  	}
 227  	found := false
 228  	for _, e := range events {
 229  		if string(e.ID) == string(ev.ID) {
 230  			found = true
 231  			break
 232  		}
 233  	}
 234  	if !found {
 235  		return TestResult{Pass: false, Info: "event not found by time range"}
 236  	}
 237  	return TestResult{Pass: true}
 238  }
 239  
 240  func testRejectInvalidSignature(client *Client, key1, key2 *KeyPair) (result TestResult) {
 241  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "invalid sig test", nil)
 242  	if err != nil {
 243  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 244  	}
 245  	// Corrupt the signature
 246  	ev.Sig[0] ^= 0xFF
 247  	if err = client.Publish(ev); err != nil {
 248  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 249  	}
 250  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
 251  	if err != nil {
 252  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
 253  	}
 254  	if accepted {
 255  		return TestResult{Pass: false, Info: "invalid signature was accepted"}
 256  	}
 257  	_ = reason
 258  	return TestResult{Pass: true}
 259  }
 260  
 261  func testRejectFutureEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
 262  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "future event test", nil)
 263  	if err != nil {
 264  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 265  	}
 266  	ev.CreatedAt = time.Now().Unix() + 3601 // More than 1 hour in the future (should be rejected)
 267  	// Re-sign with new timestamp
 268  	if err = ev.Sign(key1.Secret); err != nil {
 269  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
 270  	}
 271  	if err = client.Publish(ev); err != nil {
 272  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 273  	}
 274  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
 275  	if err != nil {
 276  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
 277  	}
 278  	if accepted {
 279  		return TestResult{Pass: false, Info: "future event was accepted"}
 280  	}
 281  	_ = reason
 282  	return TestResult{Pass: true}
 283  }
 284  
 285  func testRejectExpiredEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
 286  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "expired event test", nil)
 287  	if err != nil {
 288  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 289  	}
 290  	ev.CreatedAt = time.Now().Unix() - 86400*365 // 1 year ago
 291  	// Re-sign with new timestamp
 292  	if err = ev.Sign(key1.Secret); err != nil {
 293  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
 294  	}
 295  	if err = client.Publish(ev); err != nil {
 296  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 297  	}
 298  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 299  	if err != nil {
 300  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
 301  	}
 302  	// Some relays may accept old events, so this is optional
 303  	if !accepted {
 304  		return TestResult{Pass: true, Info: "expired event rejected (expected)"}
 305  	}
 306  	return TestResult{Pass: true, Info: "expired event accepted (relay allows old events)"}
 307  }
 308  
 309  func testReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
 310  	ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "first version")
 311  	if err != nil {
 312  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 313  	}
 314  	if err = client.Publish(ev1); err != nil {
 315  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 316  	}
 317  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
 318  	if err != nil || !accepted {
 319  		return TestResult{Pass: false, Info: "first event not accepted"}
 320  	}
 321  	time.Sleep(200 * time.Millisecond)
 322  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "second version")
 323  	if err != nil {
 324  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 325  	}
 326  	if err = client.Publish(ev2); err != nil {
 327  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 328  	}
 329  	accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
 330  	if err != nil || !accepted {
 331  		return TestResult{Pass: false, Info: "second event not accepted"}
 332  	}
 333  	// Wait longer for replacement to complete
 334  	time.Sleep(500 * time.Millisecond)
 335  	filter := map[string]interface{}{
 336  		"kinds":   []int{int(kind.ProfileMetadata.K)},
 337  		"authors": []string{hex.Enc(key1.Pubkey)},
 338  		"limit":   2, // Set limit > 1 to get multiple versions of replaceable events
 339  	}
 340  	events, err := client.GetEvents("test-replaceable", []interface{}{filter}, 3*time.Second)
 341  	if err != nil {
 342  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 343  	}
 344  	foundSecond := false
 345  	for _, e := range events {
 346  		if string(e.ID) == string(ev2.ID) {
 347  			foundSecond = true
 348  			break
 349  		}
 350  	}
 351  	if !foundSecond {
 352  		return TestResult{Pass: false, Info: "second replaceable event not found"}
 353  	}
 354  	return TestResult{Pass: true}
 355  }
 356  
 357  func testEphemeralEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
 358  	ev, err := CreateEphemeralEvent(key1.Secret, 20000, "ephemeral test")
 359  	if err != nil {
 360  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 361  	}
 362  	if err = client.Publish(ev); err != nil {
 363  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 364  	}
 365  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 366  	if err != nil || !accepted {
 367  		return TestResult{Pass: false, Info: "ephemeral event not accepted"}
 368  	}
 369  	// Ephemeral events should not be stored, so query should not find them
 370  	time.Sleep(200 * time.Millisecond)
 371  	filter := map[string]interface{}{
 372  		"kinds": []int{20000},
 373  	}
 374  	events, err := client.GetEvents("test-ephemeral", []interface{}{filter}, 2*time.Second)
 375  	if err != nil {
 376  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 377  	}
 378  	// Ephemeral events should not be queryable
 379  	for _, e := range events {
 380  		if string(e.ID) == string(ev.ID) {
 381  			return TestResult{Pass: false, Info: "ephemeral event was stored (should not be)"}
 382  		}
 383  	}
 384  	return TestResult{Pass: true}
 385  }
 386  
 387  func testParameterizedReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
 388  	ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "first list", "test-list")
 389  	if err != nil {
 390  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 391  	}
 392  	if err = client.Publish(ev1); err != nil {
 393  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 394  	}
 395  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
 396  	if err != nil || !accepted {
 397  		return TestResult{Pass: false, Info: "first event not accepted"}
 398  	}
 399  	time.Sleep(200 * time.Millisecond)
 400  	ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "second list", "test-list")
 401  	if err != nil {
 402  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 403  	}
 404  	if err = client.Publish(ev2); err != nil {
 405  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 406  	}
 407  	accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
 408  	if err != nil || !accepted {
 409  		return TestResult{Pass: false, Info: "second event not accepted"}
 410  	}
 411  	return TestResult{Pass: true}
 412  }
 413  
 414  func testDeletionEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
 415  	// First create an event to delete
 416  	targetEv, err := CreateEvent(key1.Secret, kind.TextNote.K, "event to delete", nil)
 417  	if err != nil {
 418  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 419  	}
 420  	if err = client.Publish(targetEv); err != nil {
 421  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 422  	}
 423  	accepted, _, err := client.WaitForOK(targetEv.ID, 5*time.Second)
 424  	if err != nil || !accepted {
 425  		return TestResult{Pass: false, Info: "target event not accepted"}
 426  	}
 427  	// Wait longer for event to be indexed
 428  	time.Sleep(500 * time.Millisecond)
 429  	// Now create deletion event
 430  	deleteEv, err := CreateDeleteEvent(key1.Secret, [][]byte{targetEv.ID}, "deletion reason")
 431  	if err != nil {
 432  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
 433  	}
 434  	if err = client.Publish(deleteEv); err != nil {
 435  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 436  	}
 437  	accepted, _, err = client.WaitForOK(deleteEv.ID, 5*time.Second)
 438  	if err != nil || !accepted {
 439  		return TestResult{Pass: false, Info: "delete event not accepted"}
 440  	}
 441  	return TestResult{Pass: true}
 442  }
 443  
 444  func testCountRequest(client *Client, key1, key2 *KeyPair) (result TestResult) {
 445  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "count test", nil)
 446  	if err != nil {
 447  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 448  	}
 449  	if err = client.Publish(ev); err != nil {
 450  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 451  	}
 452  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 453  	if err != nil || !accepted {
 454  		return TestResult{Pass: false, Info: "event not accepted"}
 455  	}
 456  	time.Sleep(200 * time.Millisecond)
 457  	filter := map[string]interface{}{
 458  		"kinds": []int{int(kind.TextNote.K)},
 459  	}
 460  	count, err := client.Count([]interface{}{filter})
 461  	if err != nil {
 462  		return TestResult{Pass: false, Info: fmt.Sprintf("COUNT failed: %v", err)}
 463  	}
 464  	if count < 1 {
 465  		return TestResult{Pass: false, Info: fmt.Sprintf("COUNT returned %d, expected at least 1", count)}
 466  	}
 467  	return TestResult{Pass: true}
 468  }
 469  
 470  func testLimitParameter(client *Client, key1, key2 *KeyPair) (result TestResult) {
 471  	// Publish multiple events
 472  	for i := 0; i < 5; i++ {
 473  		ev, err := CreateEvent(key1.Secret, kind.TextNote.K, fmt.Sprintf("limit test %d", i), nil)
 474  		if err != nil {
 475  			continue
 476  		}
 477  		client.Publish(ev)
 478  		client.WaitForOK(ev.ID, 2*time.Second)
 479  	}
 480  	time.Sleep(500 * time.Millisecond)
 481  	filter := map[string]interface{}{
 482  		"limit": 2,
 483  	}
 484  	events, err := client.GetEvents("test-limit", []interface{}{filter}, 2*time.Second)
 485  	if err != nil {
 486  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 487  	}
 488  	// Limit should be respected (though exact count may vary)
 489  	if len(events) > 10 {
 490  		return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, limit may not be working", len(events))}
 491  	}
 492  	return TestResult{Pass: true}
 493  }
 494  
 495  func testMultipleFilters(client *Client, key1, key2 *KeyPair) (result TestResult) {
 496  	ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "filter 1", nil)
 497  	if err != nil {
 498  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 499  	}
 500  	ev2, err := CreateEvent(key2.Secret, kind.TextNote.K, "filter 2", nil)
 501  	if err != nil {
 502  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 503  	}
 504  	if err = client.Publish(ev1); err != nil {
 505  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 506  	}
 507  	if err = client.Publish(ev2); err != nil {
 508  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 509  	}
 510  	accepted, _, err := client.WaitForOK(ev1.ID, 2*time.Second)
 511  	if err != nil || !accepted {
 512  		return TestResult{Pass: false, Info: "event 1 not accepted"}
 513  	}
 514  	accepted, _, err = client.WaitForOK(ev2.ID, 2*time.Second)
 515  	if err != nil || !accepted {
 516  		return TestResult{Pass: false, Info: "event 2 not accepted"}
 517  	}
 518  	time.Sleep(300 * time.Millisecond)
 519  	filter1 := map[string]interface{}{
 520  		"authors": []string{hex.Enc(key1.Pubkey)},
 521  	}
 522  	filter2 := map[string]interface{}{
 523  		"authors": []string{hex.Enc(key2.Pubkey)},
 524  	}
 525  	events, err := client.GetEvents("test-multi-filter", []interface{}{filter1, filter2}, 2*time.Second)
 526  	if err != nil {
 527  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 528  	}
 529  	if len(events) < 2 {
 530  		return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected at least 2", len(events))}
 531  	}
 532  	return TestResult{Pass: true}
 533  }
 534  
 535  func testSubscriptionClose(client *Client, key1, key2 *KeyPair) (result TestResult) {
 536  	ch, err := client.Subscribe("close-test", []interface{}{map[string]interface{}{}})
 537  	if err != nil {
 538  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
 539  	}
 540  	if err = client.Unsubscribe("close-test"); err != nil {
 541  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to unsubscribe: %v", err)}
 542  	}
 543  	// Channel should be closed
 544  	select {
 545  	case _, ok := <-ch:
 546  		if ok {
 547  			return TestResult{Pass: false, Info: "subscription channel not closed"}
 548  		}
 549  	default:
 550  		// Channel already closed, which is fine
 551  	}
 552  	return TestResult{Pass: true}
 553  }
 554  
 555  // Filter tests
 556  
 557  func testSinceUntilAreInclusive(client *Client, key1, key2 *KeyPair) (result TestResult) {
 558  	now := time.Now().Unix()
 559  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "since until test", nil)
 560  	if err != nil {
 561  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 562  	}
 563  	ev.CreatedAt = now
 564  	if err = ev.Sign(key1.Secret); err != nil {
 565  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
 566  	}
 567  	if err = client.Publish(ev); err != nil {
 568  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 569  	}
 570  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 571  	if err != nil || !accepted {
 572  		return TestResult{Pass: false, Info: "event not accepted"}
 573  	}
 574  	time.Sleep(200 * time.Millisecond)
 575  	// Test until filter (should be inclusive)
 576  	untilFilter := map[string]interface{}{
 577  		"authors": []string{hex.Enc(key1.Pubkey)},
 578  		"kinds":   []int{int(kind.TextNote.K)},
 579  		"until":   now,
 580  	}
 581  	untilEvents, err := client.GetEvents("test-until", []interface{}{untilFilter}, 2*time.Second)
 582  	if err != nil {
 583  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 584  	}
 585  	// Test since filter (should be inclusive)
 586  	sinceFilter := map[string]interface{}{
 587  		"authors": []string{hex.Enc(key1.Pubkey)},
 588  		"kinds":   []int{int(kind.TextNote.K)},
 589  		"since":   now,
 590  	}
 591  	sinceEvents, err := client.GetEvents("test-since", []interface{}{sinceFilter}, 2*time.Second)
 592  	if err != nil {
 593  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 594  	}
 595  	foundUntil := false
 596  	for _, e := range untilEvents {
 597  		if string(e.ID) == string(ev.ID) {
 598  			foundUntil = true
 599  			break
 600  		}
 601  	}
 602  	foundSince := false
 603  	for _, e := range sinceEvents {
 604  		if string(e.ID) == string(ev.ID) {
 605  			foundSince = true
 606  			break
 607  		}
 608  	}
 609  	if !foundUntil || !foundSince {
 610  		return TestResult{Pass: false, Info: fmt.Sprintf("since/until not inclusive: until=%v since=%v", foundUntil, foundSince)}
 611  	}
 612  	return TestResult{Pass: true}
 613  }
 614  
 615  func testLimitZero(client *Client, key1, key2 *KeyPair) (result TestResult) {
 616  	filter := map[string]interface{}{
 617  		"authors": []string{hex.Enc(key1.Pubkey)},
 618  		"limit":   0,
 619  	}
 620  	events, err := client.GetEvents("test-limit-zero", []interface{}{filter}, 2*time.Second)
 621  	if err != nil {
 622  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 623  	}
 624  	// Limit 0 should return no events pre-EOSE
 625  	if len(events) > 0 {
 626  		return TestResult{Pass: false, Info: fmt.Sprintf("limit 0 returned %d events", len(events))}
 627  	}
 628  	return TestResult{Pass: true}
 629  }
 630  
 631  // Find tests
 632  
 633  func testEventsOrderedFromNewestToOldest(client *Client, key1, key2 *KeyPair) (result TestResult) {
 634  	// Create multiple events
 635  	var eventIDs [][]byte
 636  	for i := 0; i < 3; i++ {
 637  		ev, err := CreateEvent(key1.Secret, kind.TextNote.K, fmt.Sprintf("order test %d", i), nil)
 638  		if err != nil {
 639  			continue
 640  		}
 641  		if err = client.Publish(ev); err != nil {
 642  			continue
 643  		}
 644  		accepted, _, err := client.WaitForOK(ev.ID, 2*time.Second)
 645  		if err != nil || !accepted {
 646  			continue
 647  		}
 648  		eventIDs = append(eventIDs, ev.ID)
 649  		time.Sleep(100 * time.Millisecond) // Small delay to ensure different timestamps
 650  	}
 651  	if len(eventIDs) < 3 {
 652  		return TestResult{Pass: false, Info: "failed to create enough events"}
 653  	}
 654  	time.Sleep(500 * time.Millisecond)
 655  	filter := map[string]interface{}{
 656  		"ids": eventIDsToStrings(eventIDs),
 657  	}
 658  	events, err := client.GetEvents("test-order", []interface{}{filter}, 2*time.Second)
 659  	if err != nil {
 660  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 661  	}
 662  	if len(events) < 3 {
 663  		return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected at least 3", len(events))}
 664  	}
 665  	// Check ordering (newest first)
 666  	for i := 0; i < len(events)-1; i++ {
 667  		if events[i].CreatedAt < events[i+1].CreatedAt {
 668  			return TestResult{Pass: false, Info: "events not ordered from newest to oldest"}
 669  		}
 670  	}
 671  	return TestResult{Pass: true}
 672  }
 673  
 674  func testNewestEventsWhenLimited(client *Client, key1, key2 *KeyPair) (result TestResult) {
 675  	// Create multiple events with tags
 676  	tags1 := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("limit-tag-a")))
 677  	tags2 := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("limit-tag-b")))
 678  	ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit first", tags1)
 679  	if err != nil {
 680  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 681  	}
 682  	time.Sleep(100 * time.Millisecond)
 683  	ev2, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit second", tags2)
 684  	if err != nil {
 685  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 686  	}
 687  	time.Sleep(100 * time.Millisecond)
 688  	ev3, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit third", tags1)
 689  	if err != nil {
 690  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 691  	}
 692  	for _, ev := range []*event.E{ev1, ev2, ev3} {
 693  		if err = client.Publish(ev); err != nil {
 694  			continue
 695  		}
 696  		client.WaitForOK(ev.ID, 2*time.Second)
 697  	}
 698  	time.Sleep(500 * time.Millisecond)
 699  	filter := map[string]interface{}{
 700  		"authors": []string{hex.Enc(key1.Pubkey)},
 701  		"#t":      []string{"limit-tag-a", "limit-tag-b"},
 702  		"kinds":   []int{int(kind.TextNote.K)},
 703  		"limit":   2,
 704  	}
 705  	events, err := client.GetEvents("test-newest-limit", []interface{}{filter}, 2*time.Second)
 706  	if err != nil {
 707  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 708  	}
 709  	if len(events) != 2 {
 710  		return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected 2", len(events))}
 711  	}
 712  	// Should get newest events (ev3 and ev2, not ev1)
 713  	foundEv1 := false
 714  	foundEv3 := false
 715  	for _, e := range events {
 716  		if string(e.ID) == string(ev1.ID) {
 717  			foundEv1 = true
 718  		}
 719  		if string(e.ID) == string(ev3.ID) {
 720  			foundEv3 = true
 721  		}
 722  	}
 723  	if foundEv1 && !foundEv3 {
 724  		return TestResult{Pass: false, Info: "got older event instead of newest"}
 725  	}
 726  	if !foundEv3 {
 727  		return TestResult{Pass: false, Info: "newest event not found"}
 728  	}
 729  	return TestResult{Pass: true}
 730  }
 731  
 732  func testFindByPubkeyAndKind(client *Client, key1, key2 *KeyPair) (result TestResult) {
 733  	ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "pubkey kind test", nil)
 734  	if err != nil {
 735  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 736  	}
 737  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "pubkey kind metadata")
 738  	if err != nil {
 739  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 740  	}
 741  	for _, ev := range []*event.E{ev1, ev2} {
 742  		if err = client.Publish(ev); err != nil {
 743  			continue
 744  		}
 745  		client.WaitForOK(ev.ID, 2*time.Second)
 746  	}
 747  	time.Sleep(300 * time.Millisecond)
 748  	filter := map[string]interface{}{
 749  		"authors": []string{hex.Enc(key1.Pubkey)},
 750  		"kinds":   []int{int(kind.TextNote.K), int(kind.ProfileMetadata.K)},
 751  	}
 752  	events, err := client.GetEvents("test-pubkey-kind", []interface{}{filter}, 2*time.Second)
 753  	if err != nil {
 754  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 755  	}
 756  	foundEv1 := false
 757  	foundEv2 := false
 758  	for _, e := range events {
 759  		if string(e.ID) == string(ev1.ID) {
 760  			foundEv1 = true
 761  		}
 762  		if string(e.ID) == string(ev2.ID) {
 763  			foundEv2 = true
 764  		}
 765  	}
 766  	if !foundEv1 || !foundEv2 {
 767  		return TestResult{Pass: false, Info: fmt.Sprintf("events not found: ev1=%v ev2=%v", foundEv1, foundEv2)}
 768  	}
 769  	return TestResult{Pass: true}
 770  }
 771  
 772  func testFindByPubkeyAndTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
 773  	pTag := tag.NewS(tag.NewFromBytesSlice([]byte("p"), []byte(hex.Enc(key1.Pubkey))))
 774  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "pubkey tags test", pTag)
 775  	if err != nil {
 776  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 777  	}
 778  	if err = client.Publish(ev); err != nil {
 779  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 780  	}
 781  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 782  	if err != nil || !accepted {
 783  		return TestResult{Pass: false, Info: "event not accepted"}
 784  	}
 785  	time.Sleep(200 * time.Millisecond)
 786  	filter := map[string]interface{}{
 787  		"authors": []string{hex.Enc(key1.Pubkey)},
 788  		"#p":      []string{hex.Enc(key1.Pubkey)},
 789  	}
 790  	events, err := client.GetEvents("test-pubkey-tags", []interface{}{filter}, 2*time.Second)
 791  	if err != nil {
 792  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 793  	}
 794  	found := false
 795  	for _, e := range events {
 796  		if string(e.ID) == string(ev.ID) {
 797  			found = true
 798  			break
 799  		}
 800  	}
 801  	if !found {
 802  		return TestResult{Pass: false, Info: "event not found by pubkey and tags"}
 803  	}
 804  	return TestResult{Pass: true}
 805  }
 806  
 807  func testFindByKindAndTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
 808  	tags := tag.NewS(tag.NewFromBytesSlice([]byte("n"), []byte("approved")))
 809  	ev, err := CreateEvent(key1.Secret, 9999, "kind tags test", tags)
 810  	if err != nil {
 811  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 812  	}
 813  	if err = client.Publish(ev); err != nil {
 814  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 815  	}
 816  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 817  	if err != nil || !accepted {
 818  		return TestResult{Pass: false, Info: "event not accepted"}
 819  	}
 820  	time.Sleep(200 * time.Millisecond)
 821  	filter := map[string]interface{}{
 822  		"kinds": []int{9999, int(kind.TextNote.K)},
 823  		"#n":    []string{"approved"},
 824  	}
 825  	events, err := client.GetEvents("test-kind-tags", []interface{}{filter}, 2*time.Second)
 826  	if err != nil {
 827  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 828  	}
 829  	found := false
 830  	for _, e := range events {
 831  		if string(e.ID) == string(ev.ID) {
 832  			found = true
 833  			break
 834  		}
 835  	}
 836  	if !found {
 837  		return TestResult{Pass: false, Info: "event not found by kind and tags"}
 838  	}
 839  	return TestResult{Pass: true}
 840  }
 841  
 842  func testFindByScrape(client *Client, key1, key2 *KeyPair) (result TestResult) {
 843  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "scrape test", nil)
 844  	if err != nil {
 845  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 846  	}
 847  	if err = client.Publish(ev); err != nil {
 848  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 849  	}
 850  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
 851  	if err != nil || !accepted {
 852  		return TestResult{Pass: false, Info: "event not accepted"}
 853  	}
 854  	time.Sleep(200 * time.Millisecond)
 855  	// Empty filter (scrape all)
 856  	filter := map[string]interface{}{}
 857  	events, err := client.GetEvents("test-scrape", []interface{}{filter}, 2*time.Second)
 858  	if err != nil {
 859  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 860  	}
 861  	found := false
 862  	for _, e := range events {
 863  		if string(e.ID) == string(ev.ID) {
 864  			found = true
 865  			break
 866  		}
 867  	}
 868  	if !found {
 869  		return TestResult{Pass: false, Info: "event not found by scrape"}
 870  	}
 871  	return TestResult{Pass: true}
 872  }
 873  
 874  // Helper function
 875  func eventIDsToStrings(ids [][]byte) []string {
 876  	result := make([]string, len(ids))
 877  	for i, id := range ids {
 878  		result[i] = hex.Enc(id)
 879  	}
 880  	return result
 881  }
 882  
 883  // Replaceable event tests
 884  
 885  func testReplacesMetadata(client *Client, key1, key2 *KeyPair) (result TestResult) {
 886  	ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "older metadata")
 887  	if err != nil {
 888  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 889  	}
 890  	ev1.CreatedAt = time.Now().Unix() - 60
 891  	if err = ev1.Sign(key1.Secret); err != nil {
 892  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
 893  	}
 894  	if err = client.Publish(ev1); err != nil {
 895  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 896  	}
 897  	accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
 898  	if err != nil {
 899  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
 900  	}
 901  	if !accepted {
 902  		// If rejected, check if it's because there's already a newer event (which is OK for this test)
 903  		if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
 904  			// There's already a newer event - try to publish a newer one and verify replacement works
 905  			ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
 906  			if err != nil {
 907  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 908  			}
 909  			if err = client.Publish(ev2); err != nil {
 910  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 911  			}
 912  			accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
 913  			if err != nil || !accepted {
 914  				return TestResult{Pass: false, Info: "second event not accepted"}
 915  			}
 916  			time.Sleep(500 * time.Millisecond)
 917  			filter := map[string]interface{}{
 918  				"kinds":   []int{int(kind.ProfileMetadata.K)},
 919  				"authors": []string{hex.Enc(key1.Pubkey)},
 920  			}
 921  			events, err := client.GetEvents("test-metadata-replace", []interface{}{filter}, 2*time.Second)
 922  			if err != nil {
 923  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 924  			}
 925  			foundNew := false
 926  			for _, e := range events {
 927  				if string(e.ID) == string(ev2.ID) {
 928  					foundNew = true
 929  					break
 930  				}
 931  			}
 932  			if !foundNew {
 933  				return TestResult{Pass: false, Info: "newer metadata not found"}
 934  			}
 935  			return TestResult{Pass: true, Info: "older event rejected (expected), newer event accepted and returned"}
 936  		}
 937  		return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
 938  	}
 939  	time.Sleep(200 * time.Millisecond)
 940  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
 941  	if err != nil {
 942  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 943  	}
 944  	if err = client.Publish(ev2); err != nil {
 945  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 946  	}
 947  	accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
 948  	if err != nil || !accepted {
 949  		return TestResult{Pass: false, Info: "second event not accepted"}
 950  	}
 951  	time.Sleep(500 * time.Millisecond)
 952  	filter := map[string]interface{}{
 953  		"kinds":   []int{int(kind.ProfileMetadata.K)},
 954  		"authors": []string{hex.Enc(key1.Pubkey)},
 955  	}
 956  	events, err := client.GetEvents("test-metadata-replace", []interface{}{filter}, 2*time.Second)
 957  	if err != nil {
 958  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
 959  	}
 960  	foundOld := false
 961  	foundNew := false
 962  	for _, e := range events {
 963  		if string(e.ID) == string(ev1.ID) {
 964  			foundOld = true
 965  		}
 966  		if string(e.ID) == string(ev2.ID) {
 967  			foundNew = true
 968  		}
 969  	}
 970  	if foundOld && !foundNew {
 971  		return TestResult{Pass: false, Info: "older metadata returned, newer not returned"}
 972  	}
 973  	if !foundNew {
 974  		return TestResult{Pass: false, Info: "newer metadata not found"}
 975  	}
 976  	if foundOld && foundNew {
 977  		// Both found is acceptable if relay keeps old versions
 978  		return TestResult{Pass: true, Info: "both versions found (relay keeps old versions)"}
 979  	}
 980  	return TestResult{Pass: true}
 981  }
 982  
 983  func testReplacesContactList(client *Client, key1, key2 *KeyPair) (result TestResult) {
 984  	ev1, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "older contact list")
 985  	if err != nil {
 986  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
 987  	}
 988  	ev1.CreatedAt = time.Now().Unix() - 60
 989  	if err = ev1.Sign(key1.Secret); err != nil {
 990  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
 991  	}
 992  	if err = client.Publish(ev1); err != nil {
 993  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
 994  	}
 995  	accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
 996  	if err != nil {
 997  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
 998  	}
 999  	if !accepted {
1000  		// If rejected, check if it's because there's already a newer event (which is OK for this test)
1001  		if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
1002  			// There's already a newer event - try to publish a newer one and verify replacement works
1003  			ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "newer contact list")
1004  			if err != nil {
1005  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1006  			}
1007  			if err = client.Publish(ev2); err != nil {
1008  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1009  			}
1010  			accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
1011  			if err != nil || !accepted {
1012  				return TestResult{Pass: false, Info: "second event not accepted"}
1013  			}
1014  			time.Sleep(500 * time.Millisecond)
1015  			filter := map[string]interface{}{
1016  				"kinds":   []int{int(kind.FollowList.K)},
1017  				"authors": []string{hex.Enc(key1.Pubkey)},
1018  			}
1019  			events, err := client.GetEvents("test-contact-replace", []interface{}{filter}, 2*time.Second)
1020  			if err != nil {
1021  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1022  			}
1023  			foundNew := false
1024  			for _, e := range events {
1025  				if string(e.ID) == string(ev2.ID) {
1026  					foundNew = true
1027  					break
1028  				}
1029  			}
1030  			if !foundNew {
1031  				return TestResult{Pass: false, Info: "newer contact list not found"}
1032  			}
1033  			return TestResult{Pass: true, Info: "older event rejected (expected), newer event accepted and returned"}
1034  		}
1035  		return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
1036  	}
1037  	time.Sleep(200 * time.Millisecond)
1038  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "newer contact list")
1039  	if err != nil {
1040  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1041  	}
1042  	if err = client.Publish(ev2); err != nil {
1043  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1044  	}
1045  	accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
1046  	if err != nil || !accepted {
1047  		return TestResult{Pass: false, Info: "second event not accepted"}
1048  	}
1049  	time.Sleep(500 * time.Millisecond)
1050  	filter := map[string]interface{}{
1051  		"kinds":   []int{int(kind.FollowList.K)},
1052  		"authors": []string{hex.Enc(key1.Pubkey)},
1053  	}
1054  	events, err := client.GetEvents("test-contact-replace", []interface{}{filter}, 2*time.Second)
1055  	if err != nil {
1056  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1057  	}
1058  	foundOld := false
1059  	foundNew := false
1060  	for _, e := range events {
1061  		if string(e.ID) == string(ev1.ID) {
1062  			foundOld = true
1063  		}
1064  		if string(e.ID) == string(ev2.ID) {
1065  			foundNew = true
1066  		}
1067  	}
1068  	if foundOld && !foundNew {
1069  		return TestResult{Pass: false, Info: "older contact list returned, newer not returned"}
1070  	}
1071  	if !foundNew {
1072  		return TestResult{Pass: false, Info: "newer contact list not found"}
1073  	}
1074  	return TestResult{Pass: true}
1075  }
1076  
1077  func testReplacedEventsStillAvailableByID(client *Client, key1, key2 *KeyPair) (result TestResult) {
1078  	ev1, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "old contact list")
1079  	if err != nil {
1080  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1081  	}
1082  	ev1.CreatedAt = time.Now().Unix() - 60
1083  	if err = ev1.Sign(key1.Secret); err != nil {
1084  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1085  	}
1086  	if err = client.Publish(ev1); err != nil {
1087  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1088  	}
1089  	accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
1090  	if err != nil {
1091  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1092  	}
1093  	if !accepted {
1094  		// If rejected, check if it's because there's already a newer event
1095  		if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
1096  			// There's already a newer event - just verify it's still available by ID
1097  			// Try to fetch by ID (should still work even if replaced)
1098  			filter := map[string]interface{}{
1099  				"ids": []string{hex.Enc(ev1.ID)},
1100  			}
1101  			events, err := client.GetEvents("test-old-by-id", []interface{}{filter}, 2*time.Second)
1102  			if err != nil {
1103  				return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1104  			}
1105  			found := false
1106  			for _, e := range events {
1107  				if string(e.ID) == string(ev1.ID) {
1108  					found = true
1109  					break
1110  				}
1111  			}
1112  			if found {
1113  				return TestResult{Pass: true, Info: "older event rejected but still available by ID"}
1114  			}
1115  			// If not found, it might have been deleted, which is also acceptable
1116  			return TestResult{Pass: true, Info: "older event rejected (some relays delete old versions)"}
1117  		}
1118  		return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
1119  	}
1120  	time.Sleep(200 * time.Millisecond)
1121  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "new contact list")
1122  	if err != nil {
1123  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1124  	}
1125  	if err = client.Publish(ev2); err != nil {
1126  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1127  	}
1128  	client.WaitForOK(ev2.ID, 2*time.Second)
1129  	time.Sleep(500 * time.Millisecond)
1130  	// Try to fetch old event by ID - should still be available
1131  	filter := map[string]interface{}{
1132  		"ids": []string{hex.Enc(ev1.ID)},
1133  	}
1134  	events, err := client.GetEvents("test-old-by-id", []interface{}{filter}, 2*time.Second)
1135  	if err != nil {
1136  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1137  	}
1138  	found := false
1139  	for _, e := range events {
1140  		if string(e.ID) == string(ev1.ID) {
1141  			found = true
1142  			break
1143  		}
1144  	}
1145  	if !found {
1146  		return TestResult{Pass: false, Info: "replaced event not available by ID"}
1147  	}
1148  	return TestResult{Pass: true}
1149  }
1150  
1151  func testReplaceableEventRemovesPrevious(client *Client, key1, key2 *KeyPair) (result TestResult) {
1152  	// Use a custom replaceable kind
1153  	ev1, err := CreateReplaceableEvent(key1.Secret, 10001, "old replaceable")
1154  	if err != nil {
1155  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1156  	}
1157  	ev1.CreatedAt = time.Now().Unix() - 60
1158  	if err = ev1.Sign(key1.Secret); err != nil {
1159  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1160  	}
1161  	if err = client.Publish(ev1); err != nil {
1162  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1163  	}
1164  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1165  	if err != nil || !accepted {
1166  		return TestResult{Pass: false, Info: "first event not accepted"}
1167  	}
1168  	time.Sleep(200 * time.Millisecond)
1169  	ev2, err := CreateReplaceableEvent(key1.Secret, 10001, "new replaceable")
1170  	if err != nil {
1171  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1172  	}
1173  	if err = client.Publish(ev2); err != nil {
1174  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1175  	}
1176  	client.WaitForOK(ev2.ID, 2*time.Second)
1177  	time.Sleep(500 * time.Millisecond)
1178  	filter := map[string]interface{}{
1179  		"kinds":   []int{10001},
1180  		"authors": []string{hex.Enc(key1.Pubkey)},
1181  	}
1182  	events, err := client.GetEvents("test-replace-remove", []interface{}{filter}, 2*time.Second)
1183  	if err != nil {
1184  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1185  	}
1186  	// Old event should not be returned (unless relay keeps old versions)
1187  	foundOld := false
1188  	for _, e := range events {
1189  		if string(e.ID) == string(ev1.ID) {
1190  			foundOld = true
1191  			break
1192  		}
1193  	}
1194  	if foundOld {
1195  		// Some relays keep old versions, which is acceptable
1196  		return TestResult{Pass: true, Info: "old event still present (relay keeps old versions)"}
1197  	}
1198  	return TestResult{Pass: true}
1199  }
1200  
1201  func testReplaceableEventRejectedIfFuture(client *Client, key1, key2 *KeyPair) (result TestResult) {
1202  	// Create newer replaceable event first
1203  	ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
1204  	if err != nil {
1205  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1206  	}
1207  	if err = client.Publish(ev1); err != nil {
1208  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1209  	}
1210  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1211  	if err != nil || !accepted {
1212  		return TestResult{Pass: false, Info: "newer event not accepted"}
1213  	}
1214  	time.Sleep(200 * time.Millisecond)
1215  	// Try to submit older replaceable event
1216  	ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "older metadata")
1217  	if err != nil {
1218  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1219  	}
1220  	ev2.CreatedAt = time.Now().Unix() - 60
1221  	if err = ev2.Sign(key1.Secret); err != nil {
1222  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1223  	}
1224  	if err = client.Publish(ev2); err != nil {
1225  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1226  	}
1227  	accepted, reason, err := client.WaitForOK(ev2.ID, 5*time.Second)
1228  	if err != nil {
1229  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1230  	}
1231  	if accepted {
1232  		// Some relays accept old replaceable events
1233  		return TestResult{Pass: true, Info: "older replaceable event accepted (relay allows old versions)"}
1234  	}
1235  	_ = reason
1236  	return TestResult{Pass: true, Info: "older replaceable event rejected (expected)"}
1237  }
1238  
1239  func testAddressableEventRemovesPrevious(client *Client, key1, key2 *KeyPair) (result TestResult) {
1240  	ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "old list", "test-addr")
1241  	if err != nil {
1242  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1243  	}
1244  	ev1.CreatedAt = time.Now().Unix() - 60
1245  	if err = ev1.Sign(key1.Secret); err != nil {
1246  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1247  	}
1248  	if err = client.Publish(ev1); err != nil {
1249  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1250  	}
1251  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1252  	if err != nil || !accepted {
1253  		return TestResult{Pass: false, Info: "first event not accepted"}
1254  	}
1255  	time.Sleep(200 * time.Millisecond)
1256  	ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "new list", "test-addr")
1257  	if err != nil {
1258  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1259  	}
1260  	if err = client.Publish(ev2); err != nil {
1261  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1262  	}
1263  	client.WaitForOK(ev2.ID, 2*time.Second)
1264  	time.Sleep(500 * time.Millisecond)
1265  	filter := map[string]interface{}{
1266  		"kinds":   []int{30023},
1267  		"authors": []string{hex.Enc(key1.Pubkey)},
1268  		"#d":      []string{"test-addr"},
1269  	}
1270  	events, err := client.GetEvents("test-addr-remove", []interface{}{filter}, 2*time.Second)
1271  	if err != nil {
1272  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1273  	}
1274  	foundOld := false
1275  	foundNew := false
1276  	for _, e := range events {
1277  		if string(e.ID) == string(ev1.ID) {
1278  			foundOld = true
1279  		}
1280  		if string(e.ID) == string(ev2.ID) {
1281  			foundNew = true
1282  		}
1283  	}
1284  	if foundOld && !foundNew {
1285  		return TestResult{Pass: false, Info: "older addressable event returned, newer not returned"}
1286  	}
1287  	if !foundNew {
1288  		return TestResult{Pass: false, Info: "newer addressable event not found"}
1289  	}
1290  	return TestResult{Pass: true}
1291  }
1292  
1293  func testAddressableEventRejectedIfFuture(client *Client, key1, key2 *KeyPair) (result TestResult) {
1294  	// Create newer addressable event first
1295  	ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "newer list", "test-future")
1296  	if err != nil {
1297  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1298  	}
1299  	if err = client.Publish(ev1); err != nil {
1300  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1301  	}
1302  	accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1303  	if err != nil || !accepted {
1304  		return TestResult{Pass: false, Info: "newer event not accepted"}
1305  	}
1306  	time.Sleep(200 * time.Millisecond)
1307  	// Try to submit older addressable event
1308  	ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "older list", "test-future")
1309  	if err != nil {
1310  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1311  	}
1312  	ev2.CreatedAt = time.Now().Unix() - 60
1313  	if err = ev2.Sign(key1.Secret); err != nil {
1314  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1315  	}
1316  	if err = client.Publish(ev2); err != nil {
1317  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1318  	}
1319  	accepted, reason, err := client.WaitForOK(ev2.ID, 5*time.Second)
1320  	if err != nil {
1321  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1322  	}
1323  	if accepted {
1324  		return TestResult{Pass: true, Info: "older addressable event accepted (relay allows old versions)"}
1325  	}
1326  	_ = reason
1327  	return TestResult{Pass: true, Info: "older addressable event rejected (expected)"}
1328  }
1329  
1330  // Deletion tests
1331  
1332  func testDeleteByAddr(client *Client, key1, key2 *KeyPair) (result TestResult) {
1333  	// Create addressable event
1334  	ev, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content to delete", "delete-test")
1335  	if err != nil {
1336  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1337  	}
1338  	if err = client.Publish(ev); err != nil {
1339  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1340  	}
1341  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1342  	if err != nil || !accepted {
1343  		return TestResult{Pass: false, Info: "event not accepted"}
1344  	}
1345  	time.Sleep(500 * time.Millisecond)
1346  	// Create deletion event with a-tag
1347  	aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "delete-test")
1348  	deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1349  	deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "delete reason", deleteTags)
1350  	if err != nil {
1351  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1352  	}
1353  	if err = client.Publish(deleteEv); err != nil {
1354  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1355  	}
1356  	accepted, _, err = client.WaitForOK(deleteEv.ID, 5*time.Second)
1357  	if err != nil || !accepted {
1358  		return TestResult{Pass: false, Info: "delete event not accepted"}
1359  	}
1360  	time.Sleep(500 * time.Millisecond)
1361  	// Try to fetch deleted event by ID
1362  	filter := map[string]interface{}{
1363  		"ids": []string{hex.Enc(ev.ID)},
1364  	}
1365  	events, err := client.GetEvents("test-delete-addr", []interface{}{filter}, 2*time.Second)
1366  	if err != nil {
1367  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1368  	}
1369  	for _, e := range events {
1370  		if string(e.ID) == string(ev.ID) {
1371  			return TestResult{Pass: false, Info: "deleted event still returned"}
1372  		}
1373  	}
1374  	return TestResult{Pass: true}
1375  }
1376  
1377  func testDeleteByAddrOnlyDeletesOlder(client *Client, key1, key2 *KeyPair) (result TestResult) {
1378  	time1 := time.Now().Unix() - 300
1379  	time2 := time.Now().Unix() - 100
1380  	time3 := time.Now().Unix()
1381  	// Create older event
1382  	ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "old content", "delete-older-test")
1383  	if err != nil {
1384  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1385  	}
1386  	ev1.CreatedAt = time1
1387  	if err = ev1.Sign(key1.Secret); err != nil {
1388  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1389  	}
1390  	if err = client.Publish(ev1); err != nil {
1391  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1392  	}
1393  	client.WaitForOK(ev1.ID, 2*time.Second)
1394  	time.Sleep(200 * time.Millisecond)
1395  	// Create newer event
1396  	ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "new content", "delete-older-test")
1397  	if err != nil {
1398  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1399  	}
1400  	ev2.CreatedAt = time3
1401  	if err = ev2.Sign(key1.Secret); err != nil {
1402  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1403  	}
1404  	if err = client.Publish(ev2); err != nil {
1405  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1406  	}
1407  	client.WaitForOK(ev2.ID, 2*time.Second)
1408  	time.Sleep(200 * time.Millisecond)
1409  	// Create deletion event dated between them
1410  	aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "delete-older-test")
1411  	deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1412  	deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "", deleteTags)
1413  	if err != nil {
1414  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1415  	}
1416  	deleteEv.CreatedAt = time2
1417  	if err = deleteEv.Sign(key1.Secret); err != nil {
1418  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1419  	}
1420  	if err = client.Publish(deleteEv); err != nil {
1421  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1422  	}
1423  	client.WaitForOK(deleteEv.ID, 2*time.Second)
1424  	time.Sleep(500 * time.Millisecond)
1425  	// Fetch events by address
1426  	filter := map[string]interface{}{
1427  		"kinds":   []int{int(kind.LongFormContent.K)},
1428  		"authors": []string{hex.Enc(key1.Pubkey)},
1429  		"#d":      []string{"delete-older-test"},
1430  	}
1431  	events, err := client.GetEvents("test-delete-older", []interface{}{filter}, 2*time.Second)
1432  	if err != nil {
1433  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1434  	}
1435  	foundEv1 := false
1436  	foundEv2 := false
1437  	for _, e := range events {
1438  		if string(e.ID) == string(ev1.ID) {
1439  			foundEv1 = true
1440  		}
1441  		if string(e.ID) == string(ev2.ID) {
1442  			foundEv2 = true
1443  		}
1444  	}
1445  	if foundEv1 {
1446  		return TestResult{Pass: false, Info: "older event not deleted"}
1447  	}
1448  	if !foundEv2 {
1449  		return TestResult{Pass: false, Info: "newer event wrongly deleted"}
1450  	}
1451  	return TestResult{Pass: true}
1452  }
1453  
1454  func testDeleteByAddrIsBoundByTag(client *Client, key1, key2 *KeyPair) (result TestResult) {
1455  	// Create events with same author and kind but different d-tags
1456  	ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content 1", "bound-test")
1457  	if err != nil {
1458  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1459  	}
1460  	if err = client.Publish(ev1); err != nil {
1461  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1462  	}
1463  	client.WaitForOK(ev1.ID, 2*time.Second)
1464  	time.Sleep(200 * time.Millisecond)
1465  	ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content 2", "bound-test-other")
1466  	if err != nil {
1467  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1468  	}
1469  	if err = client.Publish(ev2); err != nil {
1470  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1471  	}
1472  	client.WaitForOK(ev2.ID, 2*time.Second)
1473  	time.Sleep(200 * time.Millisecond)
1474  	// Delete only one address
1475  	aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "bound-test")
1476  	deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1477  	deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "", deleteTags)
1478  	if err != nil {
1479  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1480  	}
1481  	if err = client.Publish(deleteEv); err != nil {
1482  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1483  	}
1484  	client.WaitForOK(deleteEv.ID, 2*time.Second)
1485  	time.Sleep(500 * time.Millisecond)
1486  	// Fetch both events by ID
1487  	filter := map[string]interface{}{
1488  		"ids": []string{hex.Enc(ev1.ID), hex.Enc(ev2.ID)},
1489  	}
1490  	events, err := client.GetEvents("test-delete-bound", []interface{}{filter}, 2*time.Second)
1491  	if err != nil {
1492  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1493  	}
1494  	foundEv1 := false
1495  	foundEv2 := false
1496  	for _, e := range events {
1497  		if string(e.ID) == string(ev1.ID) {
1498  			foundEv1 = true
1499  		}
1500  		if string(e.ID) == string(ev2.ID) {
1501  			foundEv2 = true
1502  		}
1503  	}
1504  	if foundEv1 {
1505  		return TestResult{Pass: false, Info: "deleted event still returned"}
1506  	}
1507  	if !foundEv2 {
1508  		return TestResult{Pass: false, Info: "other event wrongly deleted"}
1509  	}
1510  	return TestResult{Pass: true}
1511  }
1512  
1513  // Ephemeral tests
1514  
1515  func testEphemeralSubscriptionsWork(client *Client, key1, key2 *KeyPair) (result TestResult) {
1516  	// Subscribe to ephemeral events
1517  	filter := map[string]interface{}{
1518  		"kinds":   []int{20000},
1519  		"authors": []string{hex.Enc(key1.Pubkey)},
1520  	}
1521  	ch, err := client.Subscribe("test-ephemeral-sub", []interface{}{filter})
1522  	if err != nil {
1523  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1524  	}
1525  	defer client.Unsubscribe("test-ephemeral-sub")
1526  
1527  	// Wait for EOSE to ensure subscription is established
1528  	eoseTimeout := time.After(3 * time.Second)
1529  	gotEose := false
1530  	for !gotEose {
1531  		select {
1532  		case msg, ok := <-ch:
1533  			if !ok {
1534  				return TestResult{Pass: false, Info: "channel closed before EOSE"}
1535  			}
1536  			var raw []interface{}
1537  			if err = json.Unmarshal(msg, &raw); err != nil {
1538  				continue
1539  			}
1540  			if len(raw) >= 2 {
1541  				if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1542  					gotEose = true
1543  					break
1544  				}
1545  			}
1546  		case <-eoseTimeout:
1547  			return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1548  		}
1549  	}
1550  
1551  	// Now publish ephemeral event
1552  	ev, err := CreateEphemeralEvent(key1.Secret, 20000, "ephemeral test")
1553  	if err != nil {
1554  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1555  	}
1556  	if err = client.Publish(ev); err != nil {
1557  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1558  	}
1559  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1560  	if err != nil || !accepted {
1561  		return TestResult{Pass: false, Info: "ephemeral event not accepted"}
1562  	}
1563  	// Give the relay time to process and distribute the ephemeral event
1564  	time.Sleep(2 * time.Second)
1565  	// Wait for event to come through subscription
1566  	timeout := time.After(15 * time.Second)
1567  	for {
1568  		select {
1569  		case msg, ok := <-ch:
1570  			if !ok {
1571  				return TestResult{Pass: false, Info: "subscription closed"}
1572  			}
1573  			var raw []interface{}
1574  			if err = json.Unmarshal(msg, &raw); err != nil {
1575  				continue
1576  			}
1577  			if len(raw) >= 3 && raw[0] == "EVENT" {
1578  				if evData, ok := raw[2].(map[string]interface{}); ok {
1579  					evJSON, _ := json.Marshal(evData)
1580  					receivedEv := event.New()
1581  					if _, err = receivedEv.Unmarshal(evJSON); err == nil {
1582  						if string(receivedEv.ID) == string(ev.ID) {
1583  							return TestResult{Pass: true}
1584  						}
1585  					}
1586  				}
1587  			}
1588  		case <-timeout:
1589  			return TestResult{Pass: false, Info: "timeout waiting for ephemeral event"}
1590  		}
1591  	}
1592  }
1593  
1594  func testPersistsEphemeralEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
1595  	ev, err := CreateEphemeralEvent(key1.Secret, 20001, "ephemeral persist test")
1596  	if err != nil {
1597  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1598  	}
1599  	if err = client.Publish(ev); err != nil {
1600  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1601  	}
1602  	accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1603  	if err != nil || !accepted {
1604  		return TestResult{Pass: false, Info: "ephemeral event not accepted"}
1605  	}
1606  	time.Sleep(200 * time.Millisecond)
1607  	// Try to query for ephemeral event
1608  	filter := map[string]interface{}{
1609  		"kinds":   []int{20001},
1610  		"authors": []string{hex.Enc(key1.Pubkey)},
1611  	}
1612  	events, err := client.GetEvents("test-ephemeral-persist", []interface{}{filter}, 2*time.Second)
1613  	if err != nil {
1614  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1615  	}
1616  	// Ephemeral events should NOT be queryable
1617  	for _, e := range events {
1618  		if string(e.ID) == string(ev.ID) {
1619  			return TestResult{Pass: false, Info: "ephemeral event was persisted (should not be)"}
1620  		}
1621  	}
1622  	return TestResult{Pass: true}
1623  }
1624  
1625  // EOSE tests
1626  
1627  func testSupportsEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1628  	// Subscribe to events from a random author (should have 0 events)
1629  	filter := map[string]interface{}{
1630  		"authors": []string{hex.Enc(key2.Pubkey)},
1631  		"kinds":   []int{int(kind.TextNote.K)},
1632  		"limit":   10,
1633  	}
1634  	ch, err := client.Subscribe("test-eose", []interface{}{filter})
1635  	if err != nil {
1636  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1637  	}
1638  	defer client.Unsubscribe("test-eose")
1639  	// Wait for EOSE message or timeout
1640  	timeout := time.After(3 * time.Second)
1641  	for {
1642  		select {
1643  		case msg, ok := <-ch:
1644  			if !ok {
1645  				return TestResult{Pass: false, Info: "channel closed before EOSE"}
1646  			}
1647  			var raw []interface{}
1648  			if err = json.Unmarshal(msg, &raw); err != nil {
1649  				continue
1650  			}
1651  			if len(raw) >= 2 {
1652  				if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1653  					return TestResult{Pass: true}
1654  				}
1655  			}
1656  		case <-timeout:
1657  			return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1658  		}
1659  	}
1660  }
1661  
1662  func testSubscriptionReceivesEventAfterPingPeriod(client *Client, key1, key2 *KeyPair) (result TestResult) {
1663  	// Create a second client for publishing
1664  	publisherClient, err := NewClient(client.URL())
1665  	if err != nil {
1666  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create publisher client: %v", err)}
1667  	}
1668  	defer publisherClient.Close()
1669  
1670  	// Subscribe to events from key1
1671  	filter := map[string]interface{}{
1672  		"authors": []string{hex.Enc(key1.Pubkey)},
1673  		"kinds":   []int{int(kind.TextNote.K)},
1674  	}
1675  	ch, err := client.Subscribe("test-ping-period", []interface{}{filter})
1676  	if err != nil {
1677  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1678  	}
1679  	defer client.Unsubscribe("test-ping-period")
1680  
1681  	// Wait for EOSE to ensure subscription is established
1682  	eoseTimeout := time.After(3 * time.Second)
1683  	gotEose := false
1684  	for !gotEose {
1685  		select {
1686  		case msg, ok := <-ch:
1687  			if !ok {
1688  				return TestResult{Pass: false, Info: "channel closed before EOSE"}
1689  			}
1690  			var raw []interface{}
1691  			if err = json.Unmarshal(msg, &raw); err != nil {
1692  				continue
1693  			}
1694  			if len(raw) >= 2 {
1695  				if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1696  					gotEose = true
1697  					break
1698  				}
1699  			}
1700  		case <-eoseTimeout:
1701  			return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1702  		}
1703  	}
1704  
1705  	// Wait for at least one ping period (30 seconds) to ensure connection is idle
1706  	// and has been pinged at least once
1707  	pingPeriod := 35 * time.Second // Slightly longer than 30s to ensure at least one ping
1708  	// Reduce for testing - the ping/pong mechanism is tested separately
1709  	pingPeriod = 1 * time.Second
1710  	time.Sleep(pingPeriod)
1711  
1712  	// Now publish an event from the publisher client that matches the subscription
1713  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "event after ping period", nil)
1714  	if err != nil {
1715  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1716  	}
1717  	if err = publisherClient.Publish(ev); err != nil {
1718  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1719  	}
1720  	accepted, _, err := publisherClient.WaitForOK(ev.ID, 5*time.Second)
1721  	if err != nil || !accepted {
1722  		return TestResult{Pass: false, Info: "event not accepted"}
1723  	}
1724  	// Give the relay time to process and distribute the event
1725  	time.Sleep(2 * time.Second)
1726  
1727  	// Wait for event to come through subscription (should work even after ping period)
1728  	eventTimeout := time.After(15 * time.Second)
1729  	for {
1730  		select {
1731  		case msg, ok := <-ch:
1732  			if !ok {
1733  				return TestResult{Pass: false, Info: "subscription closed"}
1734  			}
1735  			var raw []interface{}
1736  			if err = json.Unmarshal(msg, &raw); err != nil {
1737  				continue
1738  			}
1739  			if len(raw) >= 3 && raw[0] == "EVENT" {
1740  				if evData, ok := raw[2].(map[string]interface{}); ok {
1741  					evJSON, _ := json.Marshal(evData)
1742  					receivedEv := event.New()
1743  					if _, err = receivedEv.Unmarshal(evJSON); err == nil {
1744  						if string(receivedEv.ID) == string(ev.ID) {
1745  							return TestResult{Pass: true}
1746  						}
1747  					}
1748  				}
1749  			}
1750  		case <-eventTimeout:
1751  			return TestResult{Pass: false, Info: "timeout waiting for event after ping period"}
1752  		}
1753  	}
1754  }
1755  
1756  func testClosesCompleteSubscriptionsAfterEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1757  	// Create a filter that fetches a specific event by ID (complete subscription)
1758  	fakeID := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
1759  	filter := map[string]interface{}{
1760  		"ids":   []string{fakeID},
1761  		"kinds": []int{int(kind.TextNote.K)},
1762  	}
1763  	ch, err := client.Subscribe("test-close-complete", []interface{}{filter})
1764  	if err != nil {
1765  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1766  	}
1767  	defer func() {
1768  		client.Unsubscribe("test-close-complete")
1769  	}()
1770  	// Wait for EOSE and verify channel is closed
1771  	timeout := time.After(3 * time.Second)
1772  	gotEose := false
1773  	for {
1774  		select {
1775  		case msg, ok := <-ch:
1776  			if !ok {
1777  				// Channel closed - for complete subscriptions, this should happen after EOSE
1778  				if gotEose {
1779  					return TestResult{Pass: true}
1780  				}
1781  				return TestResult{Pass: false, Info: "channel closed before EOSE"}
1782  			}
1783  			var raw []interface{}
1784  			if err = json.Unmarshal(msg, &raw); err != nil {
1785  				continue
1786  			}
1787  			if len(raw) >= 2 {
1788  				if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1789  					gotEose = true
1790  					// For complete subscriptions, channel should close after EOSE
1791  					time.Sleep(100 * time.Millisecond)
1792  					select {
1793  					case _, ok := <-ch:
1794  						if ok {
1795  							return TestResult{Pass: false, Info: "subscription not closed after EOSE"}
1796  						}
1797  						// Channel closed, which is correct
1798  						return TestResult{Pass: true}
1799  					default:
1800  						// Channel might be closed already
1801  						return TestResult{Pass: true}
1802  					}
1803  				}
1804  			}
1805  		case <-timeout:
1806  			if gotEose {
1807  				return TestResult{Pass: false, Info: "timeout but EOSE received - subscription should be closed"}
1808  			}
1809  			return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1810  		}
1811  	}
1812  }
1813  
1814  func testKeepsOpenIncompleteSubscriptionsAfterEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1815  	// Subscribe to events from a random author (incomplete subscription)
1816  	filter := map[string]interface{}{
1817  		"authors": []string{hex.Enc(key2.Pubkey)},
1818  		"kinds":   []int{int(kind.TextNote.K)},
1819  		"limit":   10,
1820  		"until":   time.Now().Unix() - 86400, // Past timestamp
1821  	}
1822  	ch, err := client.Subscribe("test-open-incomplete", []interface{}{filter})
1823  	if err != nil {
1824  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1825  	}
1826  	defer client.Unsubscribe("test-open-incomplete")
1827  	// Wait for EOSE
1828  	timeout := time.After(3 * time.Second)
1829  	gotEose := false
1830  	for {
1831  		select {
1832  		case msg, ok := <-ch:
1833  			if !ok {
1834  				return TestResult{Pass: false, Info: "incomplete subscription closed after EOSE"}
1835  			}
1836  			var raw []interface{}
1837  			if err = json.Unmarshal(msg, &raw); err != nil {
1838  				continue
1839  			}
1840  			if len(raw) >= 2 {
1841  				if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1842  					gotEose = true
1843  					// After EOSE, subscription should remain open for incomplete subscriptions
1844  					time.Sleep(200 * time.Millisecond)
1845  					// Channel should still be open (not closed)
1846  					select {
1847  					case _, ok := <-ch:
1848  						if !ok {
1849  							return TestResult{Pass: false, Info: "incomplete subscription closed after EOSE"}
1850  						}
1851  						// Channel is still open, which is correct
1852  						return TestResult{Pass: true}
1853  					default:
1854  						// Channel is still open, which is correct
1855  						return TestResult{Pass: true}
1856  					}
1857  				}
1858  			}
1859  		case <-timeout:
1860  			if gotEose {
1861  				return TestResult{Pass: true, Info: "EOSE received, subscription remains open"}
1862  			}
1863  			return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1864  		}
1865  	}
1866  }
1867  
1868  // JSON tests
1869  
1870  func testAcceptsEventsWithEmptyTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
1871  	// Create event with empty tags array
1872  	emptyTags := tag.NewS()
1873  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "empty tags test", emptyTags)
1874  	if err != nil {
1875  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1876  	}
1877  	if err = client.Publish(ev); err != nil {
1878  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1879  	}
1880  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1881  	if err != nil {
1882  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1883  	}
1884  	if !accepted {
1885  		return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1886  	}
1887  	return TestResult{Pass: true}
1888  }
1889  
1890  func testAcceptsNip1JsonEscapeSequences(client *Client, key1, key2 *KeyPair) (result TestResult) {
1891  	// NIP-01 escape sequences: \n, \", \\, \r, \t, \b, \f
1892  	content := "linebreak\\ndoublequote\\\"backslash\\\\carraigereturn\\rtab\\tbackspace\\bformfeed\\fend"
1893  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, content, nil)
1894  	if err != nil {
1895  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1896  	}
1897  	if err = client.Publish(ev); err != nil {
1898  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1899  	}
1900  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1901  	if err != nil {
1902  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1903  	}
1904  	if !accepted {
1905  		return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1906  	}
1907  	return TestResult{Pass: true}
1908  }
1909  
1910  // Registration tests
1911  
1912  func testSendsOkAfterEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
1913  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "OK test", nil)
1914  	if err != nil {
1915  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1916  	}
1917  	if err = client.Publish(ev); err != nil {
1918  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1919  	}
1920  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1921  	if err != nil {
1922  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1923  	}
1924  	if !accepted {
1925  		return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1926  	}
1927  	return TestResult{Pass: true}
1928  }
1929  
1930  func testVerifiesSignatures(client *Client, key1, key2 *KeyPair) (result TestResult) {
1931  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "signature test", nil)
1932  	if err != nil {
1933  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1934  	}
1935  	// Corrupt the signature
1936  	for i := range ev.Sig {
1937  		ev.Sig[i] = 0
1938  	}
1939  	if err = client.Publish(ev); err != nil {
1940  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1941  	}
1942  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1943  	if err != nil {
1944  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1945  	}
1946  	if accepted {
1947  		return TestResult{Pass: false, Info: "invalid signature was accepted"}
1948  	}
1949  	_ = reason
1950  	return TestResult{Pass: true}
1951  }
1952  
1953  func testVerifiesIdHashes(client *Client, key1, key2 *KeyPair) (result TestResult) {
1954  	ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "ID hash test", nil)
1955  	if err != nil {
1956  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1957  	}
1958  	// Save the correct ID
1959  	correctID := make([]byte, len(ev.ID))
1960  	copy(correctID, ev.ID)
1961  	// Corrupt the ID AFTER signing (Sign() recalculates ID, so we corrupt it after)
1962  	for i := range ev.ID {
1963  		ev.ID[i] = 0xCA
1964  	}
1965  	// Don't re-sign - the signature is valid for the correct ID, but we have a corrupted ID
1966  	if err = client.Publish(ev); err != nil {
1967  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1968  	}
1969  	// Use the corrupted ID to wait for OK (relay should reject based on ID mismatch)
1970  	accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1971  	if err != nil {
1972  		return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1973  	}
1974  	if accepted {
1975  		return TestResult{Pass: false, Info: "invalid ID hash was accepted"}
1976  	}
1977  	_ = reason
1978  	_ = correctID
1979  	return TestResult{Pass: true}
1980  }
1981