initial public relay build repo
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"fiatjaf.com/nostr"
|
||||
)
|
||||
|
||||
func TestParseConfig(t *testing.T) {
|
||||
t.Run("empty spec disables federation", func(t *testing.T) {
|
||||
cfg, warnings := ParseConfig("")
|
||||
if cfg.Enabled() {
|
||||
t.Fatal("expected federation disabled for empty spec")
|
||||
}
|
||||
if len(warnings) != 0 {
|
||||
t.Fatalf("unexpected warnings: %v", warnings)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bare url defaults to mirror all channels", func(t *testing.T) {
|
||||
cfg, _ := ParseConfig("wss://a.example")
|
||||
if len(cfg.Peers) != 1 {
|
||||
t.Fatalf("expected 1 peer, got %d", len(cfg.Peers))
|
||||
}
|
||||
p := cfg.Peers[0]
|
||||
if p.Mode != ModeMirror {
|
||||
t.Errorf("expected mirror mode, got %s", p.Mode)
|
||||
}
|
||||
if !p.AllChans {
|
||||
t.Error("expected AllChans=true for bare url")
|
||||
}
|
||||
if !p.wants("anything") {
|
||||
t.Error("bare url should federate every channel")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("explicit mode and channels", func(t *testing.T) {
|
||||
cfg, _ := ParseConfig("wss://a.example|ingest:general,dev")
|
||||
p := cfg.Peers[0]
|
||||
if p.Mode != ModeIngest {
|
||||
t.Errorf("expected ingest, got %s", p.Mode)
|
||||
}
|
||||
if p.AllChans {
|
||||
t.Error("expected scoped channels, got AllChans")
|
||||
}
|
||||
if !p.wants("general") || !p.wants("dev") {
|
||||
t.Error("should federate listed channels")
|
||||
}
|
||||
if p.wants("secret") {
|
||||
t.Error("should NOT federate unlisted channel")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multiple peers separated by semicolons", func(t *testing.T) {
|
||||
cfg, _ := ParseConfig("wss://a.example|mirror:general; wss://b.example|ingest:*")
|
||||
if len(cfg.Peers) != 2 {
|
||||
t.Fatalf("expected 2 peers, got %d", len(cfg.Peers))
|
||||
}
|
||||
if cfg.Peers[0].Mode != ModeMirror || cfg.Peers[1].Mode != ModeIngest {
|
||||
t.Error("modes parsed incorrectly")
|
||||
}
|
||||
if !cfg.Peers[1].AllChans {
|
||||
t.Error("ingest:* should mean all channels")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("channel names tolerate a leading #", func(t *testing.T) {
|
||||
cfg, _ := ParseConfig("wss://a.example|mirror:#general")
|
||||
if !cfg.Peers[0].wants("general") {
|
||||
t.Error("should strip leading # from channel names")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid url is skipped with a warning", func(t *testing.T) {
|
||||
cfg, warnings := ParseConfig("http://not-websocket.example")
|
||||
if cfg.Enabled() {
|
||||
t.Error("non-ws url should be skipped")
|
||||
}
|
||||
if len(warnings) == 0 {
|
||||
t.Error("expected a warning for invalid url")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("explicit but empty channel list is skipped", func(t *testing.T) {
|
||||
cfg, warnings := ParseConfig("wss://a.example|mirror:,")
|
||||
if cfg.Enabled() {
|
||||
t.Error("empty channel list should skip the peer")
|
||||
}
|
||||
if len(warnings) == 0 {
|
||||
t.Error("expected a warning for empty channel list")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unknown mode falls back to mirror with a warning", func(t *testing.T) {
|
||||
cfg, warnings := ParseConfig("wss://a.example|bogus:general")
|
||||
if cfg.Peers[0].Mode != ModeMirror {
|
||||
t.Error("unknown mode should fall back to mirror")
|
||||
}
|
||||
if len(warnings) == 0 {
|
||||
t.Error("expected a warning for unknown mode")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestFederatesChannel(t *testing.T) {
|
||||
cfg, _ := ParseConfig("wss://a.example|mirror:general;wss://b.example|ingest:dev")
|
||||
if !cfg.FederatesChannel("general") {
|
||||
t.Error("general federates via peer a")
|
||||
}
|
||||
if !cfg.FederatesChannel("dev") {
|
||||
t.Error("dev federates via peer b")
|
||||
}
|
||||
if cfg.FederatesChannel("private") {
|
||||
t.Error("private federates with nobody")
|
||||
}
|
||||
}
|
||||
|
||||
// TestReceiveLoopPrevention proves the loop guard: an event injected twice is
|
||||
// only stored (and thus only forwardable) once, because the injector reports the
|
||||
// second sight as not-newly-stored. This mirrors AddEvent's ErrDupEvent path.
|
||||
func TestReceiveLoopPrevention(t *testing.T) {
|
||||
seen := map[nostr.ID]bool{}
|
||||
var mu sync.Mutex
|
||||
injectCount := 0
|
||||
storedCount := 0
|
||||
|
||||
inject := func(_ context.Context, evt nostr.Event) bool {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
injectCount++
|
||||
if seen[evt.ID] {
|
||||
return false // duplicate: not newly stored (the loop breaker)
|
||||
}
|
||||
seen[evt.ID] = true
|
||||
storedCount++
|
||||
return true
|
||||
}
|
||||
|
||||
cfg, _ := ParseConfig("wss://peer.example|mirror:general")
|
||||
f := NewFederator(cfg, inject)
|
||||
peer := cfg.Peers[0]
|
||||
|
||||
sk := nostr.Generate()
|
||||
evt := nostr.Event{
|
||||
Kind: nostr.KindSimpleGroupChatMessage,
|
||||
CreatedAt: nostr.Now(),
|
||||
Content: "hello federation",
|
||||
Tags: nostr.Tags{{"h", "general"}},
|
||||
}
|
||||
if err := evt.Sign(sk); err != nil {
|
||||
t.Fatalf("sign: %v", err)
|
||||
}
|
||||
|
||||
// Same event received twice (e.g. A→B then echoed B→A→B).
|
||||
f.receive(peer, evt)
|
||||
f.receive(peer, evt)
|
||||
|
||||
if injectCount != 2 {
|
||||
t.Errorf("expected 2 inject attempts, got %d", injectCount)
|
||||
}
|
||||
if storedCount != 1 {
|
||||
t.Errorf("loop guard failed: event stored %d times, want 1", storedCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReceiveDropsBadSignature ensures a peer can't inject forged events.
|
||||
func TestReceiveDropsBadSignature(t *testing.T) {
|
||||
injected := false
|
||||
inject := func(_ context.Context, _ nostr.Event) bool {
|
||||
injected = true
|
||||
return true
|
||||
}
|
||||
cfg, _ := ParseConfig("wss://peer.example|mirror:general")
|
||||
f := NewFederator(cfg, inject)
|
||||
|
||||
// Unsigned (invalid) event tagged for a federated channel.
|
||||
evt := nostr.Event{
|
||||
Kind: nostr.KindSimpleGroupChatMessage,
|
||||
CreatedAt: nostr.Now(),
|
||||
Content: "forged",
|
||||
Tags: nostr.Tags{{"h", "general"}},
|
||||
}
|
||||
f.receive(cfg.Peers[0], evt)
|
||||
if injected {
|
||||
t.Error("event with an invalid signature must not be injected")
|
||||
}
|
||||
}
|
||||
|
||||
// TestReceiveIgnoresUnfederatedChannel ensures scope is enforced on ingress.
|
||||
func TestReceiveIgnoresUnfederatedChannel(t *testing.T) {
|
||||
injected := false
|
||||
inject := func(_ context.Context, _ nostr.Event) bool {
|
||||
injected = true
|
||||
return true
|
||||
}
|
||||
cfg, _ := ParseConfig("wss://peer.example|mirror:general")
|
||||
f := NewFederator(cfg, inject)
|
||||
|
||||
sk := nostr.Generate()
|
||||
evt := nostr.Event{
|
||||
Kind: nostr.KindSimpleGroupChatMessage,
|
||||
CreatedAt: nostr.Now(),
|
||||
Content: "off-topic",
|
||||
Tags: nostr.Tags{{"h", "other-channel"}},
|
||||
}
|
||||
if err := evt.Sign(sk); err != nil {
|
||||
t.Fatalf("sign: %v", err)
|
||||
}
|
||||
f.receive(cfg.Peers[0], evt)
|
||||
if injected {
|
||||
t.Error("chat for an unfederated channel must not be injected")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user