You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
550 lines
19 KiB
550 lines
19 KiB
package relaytester |
|
|
|
import ( |
|
"fmt" |
|
"time" |
|
|
|
"next.orly.dev/pkg/encoders/hex" |
|
"next.orly.dev/pkg/encoders/kind" |
|
"next.orly.dev/pkg/encoders/tag" |
|
) |
|
|
|
// Test implementations - these are referenced by test.go |
|
|
|
func testPublishBasicEvent(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "test content", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)} |
|
} |
|
if !accepted { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByID(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by id test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"ids": []string{hex.Enc(ev.ID)}, |
|
} |
|
events, err := client.GetEvents("test-sub", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by ID"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByAuthor(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by author test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"authors": []string{hex.Enc(key1.Pubkey)}, |
|
} |
|
events, err := client.GetEvents("test-author", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by author"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByKind(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by kind test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"kinds": []int{int(kind.TextNote.K)}, |
|
} |
|
events, err := client.GetEvents("test-kind", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by kind"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByTags(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
tags := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("test-tag"))) |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by tags test", tags) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"#t": []string{"test-tag"}, |
|
} |
|
events, err := client.GetEvents("test-tags", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by tags"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByMultipleTags(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
tags := tag.NewS( |
|
tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-1")), |
|
tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-2")), |
|
) |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by multiple tags test", tags) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"#t": []string{"multi-tag-1", "multi-tag-2"}, |
|
} |
|
events, err := client.GetEvents("test-multi-tags", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by multiple tags"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testFindByTimeRange(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by time range test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
now := time.Now().Unix() |
|
filter := map[string]interface{}{ |
|
"since": now - 3600, |
|
"until": now + 3600, |
|
} |
|
events, err := client.GetEvents("test-time", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
found := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
found = true |
|
break |
|
} |
|
} |
|
if !found { |
|
return TestResult{Pass: false, Info: "event not found by time range"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testRejectInvalidSignature(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "invalid sig test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
// Corrupt the signature |
|
ev.Sig[0] ^= 0xFF |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)} |
|
} |
|
if accepted { |
|
return TestResult{Pass: false, Info: "invalid signature was accepted"} |
|
} |
|
_ = reason |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testRejectFutureEvent(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "future event test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
ev.CreatedAt = time.Now().Unix() + 3601 // More than 1 hour in the future (should be rejected) |
|
// Re-sign with new timestamp |
|
if err = ev.Sign(key1.Secret); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)} |
|
} |
|
if accepted { |
|
return TestResult{Pass: false, Info: "future event was accepted"} |
|
} |
|
_ = reason |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testRejectExpiredEvent(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "expired event test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
ev.CreatedAt = time.Now().Unix() - 86400*365 // 1 year ago |
|
// Re-sign with new timestamp |
|
if err = ev.Sign(key1.Secret); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)} |
|
} |
|
// Some relays may accept old events, so this is optional |
|
if !accepted { |
|
return TestResult{Pass: true, Info: "expired event rejected (expected)"} |
|
} |
|
return TestResult{Pass: true, Info: "expired event accepted (relay allows old events)"} |
|
} |
|
|
|
func testReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "first version") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev1); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "first event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "second version") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev2); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "second event not accepted"} |
|
} |
|
// Wait longer for replacement to complete |
|
time.Sleep(500 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"kinds": []int{int(kind.ProfileMetadata.K)}, |
|
"authors": []string{hex.Enc(key1.Pubkey)}, |
|
"limit": 2, // Set limit > 1 to get multiple versions of replaceable events |
|
} |
|
events, err := client.GetEvents("test-replaceable", []interface{}{filter}, 3*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
foundSecond := false |
|
for _, e := range events { |
|
if string(e.ID) == string(ev2.ID) { |
|
foundSecond = true |
|
break |
|
} |
|
} |
|
if !foundSecond { |
|
return TestResult{Pass: false, Info: "second replaceable event not found"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testEphemeralEvents(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEphemeralEvent(key1.Secret, 20000, "ephemeral test") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "ephemeral event not accepted"} |
|
} |
|
// Ephemeral events should not be stored, so query should not find them |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"kinds": []int{20000}, |
|
} |
|
events, err := client.GetEvents("test-ephemeral", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
// Ephemeral events should not be queryable |
|
for _, e := range events { |
|
if string(e.ID) == string(ev.ID) { |
|
return TestResult{Pass: false, Info: "ephemeral event was stored (should not be)"} |
|
} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testParameterizedReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "first list", "test-list") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev1); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "first event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "second list", "test-list") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev2); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "second event not accepted"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testDeletionEvents(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
// First create an event to delete |
|
targetEv, err := CreateEvent(key1.Secret, kind.TextNote.K, "event to delete", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(targetEv); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(targetEv.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "target event not accepted"} |
|
} |
|
// Wait longer for event to be indexed |
|
time.Sleep(500 * time.Millisecond) |
|
// Now create deletion event |
|
deleteEv, err := CreateDeleteEvent(key1.Secret, [][]byte{targetEv.ID}, "deletion reason") |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)} |
|
} |
|
if err = client.Publish(deleteEv); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err = client.WaitForOK(deleteEv.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "delete event not accepted"} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testCountRequest(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "count test", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event not accepted"} |
|
} |
|
time.Sleep(200 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"kinds": []int{int(kind.TextNote.K)}, |
|
} |
|
count, err := client.Count([]interface{}{filter}) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("COUNT failed: %v", err)} |
|
} |
|
if count < 1 { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("COUNT returned %d, expected at least 1", count)} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testLimitParameter(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
// Publish multiple events |
|
for i := 0; i < 5; i++ { |
|
ev, err := CreateEvent(key1.Secret, kind.TextNote.K, fmt.Sprintf("limit test %d", i), nil) |
|
if err != nil { |
|
continue |
|
} |
|
client.Publish(ev) |
|
client.WaitForOK(ev.ID, 2*time.Second) |
|
} |
|
time.Sleep(500 * time.Millisecond) |
|
filter := map[string]interface{}{ |
|
"limit": 2, |
|
} |
|
events, err := client.GetEvents("test-limit", []interface{}{filter}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
// Limit should be respected (though exact count may vary) |
|
if len(events) > 10 { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, limit may not be working", len(events))} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testMultipleFilters(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "filter 1", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
ev2, err := CreateEvent(key2.Secret, kind.TextNote.K, "filter 2", nil) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)} |
|
} |
|
if err = client.Publish(ev1); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
if err = client.Publish(ev2); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)} |
|
} |
|
accepted, _, err := client.WaitForOK(ev1.ID, 2*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event 1 not accepted"} |
|
} |
|
accepted, _, err = client.WaitForOK(ev2.ID, 2*time.Second) |
|
if err != nil || !accepted { |
|
return TestResult{Pass: false, Info: "event 2 not accepted"} |
|
} |
|
time.Sleep(300 * time.Millisecond) |
|
filter1 := map[string]interface{}{ |
|
"authors": []string{hex.Enc(key1.Pubkey)}, |
|
} |
|
filter2 := map[string]interface{}{ |
|
"authors": []string{hex.Enc(key2.Pubkey)}, |
|
} |
|
events, err := client.GetEvents("test-multi-filter", []interface{}{filter1, filter2}, 2*time.Second) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)} |
|
} |
|
if len(events) < 2 { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected at least 2", len(events))} |
|
} |
|
return TestResult{Pass: true} |
|
} |
|
|
|
func testSubscriptionClose(client *Client, key1, key2 *KeyPair) (result TestResult) { |
|
ch, err := client.Subscribe("close-test", []interface{}{map[string]interface{}{}}) |
|
if err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)} |
|
} |
|
if err = client.Unsubscribe("close-test"); err != nil { |
|
return TestResult{Pass: false, Info: fmt.Sprintf("failed to unsubscribe: %v", err)} |
|
} |
|
// Channel should be closed |
|
select { |
|
case _, ok := <-ch: |
|
if ok { |
|
return TestResult{Pass: false, Info: "subscription channel not closed"} |
|
} |
|
default: |
|
// Channel already closed, which is fine |
|
} |
|
return TestResult{Pass: true} |
|
}
|
|
|