clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/standalone-relay
tinyrelay / internal / daemon / git_outbox_test.go
git_outbox_test.go 6.2K
1package daemon23import (4 "context"5 "fmt"6 "testing"78 "github.com/FelineStateMachine/tinyrelay/internal/event"9)1011type gitOutboxTransport struct {12 queries map[string][]event.Event13 errors map[string]error14}1516func (t gitOutboxTransport) Query(_ context.Context, target string, _ event.Filter) ([]event.Event, error) {17 if err := t.errors[target]; err != nil {18 return nil, err19 }20 if items := t.queries[target]; items != nil {21 return items, nil22 }23 return nil, nil24}2526func TestGitOutboxFilterIncludesProfilesRelayListsAndRepositoryRelayLists(t *testing.T) {27 key := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"28 filter := gitOutboxFilter(key, 10002)29 if len(filter.Authors) != 1 || filter.Authors[0] != key {30 t.Fatalf("authors = %#v", filter.Authors)31 }32 if !containsGitKind(filter.Kinds, 10002) || len(filter.Kinds) != 1 {33 t.Fatalf("kinds = %#v", filter.Kinds)34 }35 if filter.Limit == nil || *filter.Limit != 1 {36 t.Fatalf("limit = %#v", filter.Limit)37 }38}3940func TestGitOutboxAuthorsAndRelaysAreDeduplicatedAndBounded(t *testing.T) {41 key := "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"42 authors := make([]string, 0, gitOutboxAuthorLimit+2)43 for i := 0; i < gitOutboxAuthorLimit+2; i++ {44 authors = append(authors, key)45 }46 if got := boundedGitAuthors(authors); len(got) != 1 {47 t.Fatalf("authors = %#v", got)48 }49 relays := boundedGitRelays([]string{"https://Relay.EXAMPLE/", "wss://relay.example"}, false, nil)50 if len(relays) != 1 || relays[0] != "wss://relay.example" {51 t.Fatalf("relays = %#v", relays)52 }53}5455func TestGitOutboxPrivateDiscoveryNeverUsesUnconfiguredRelay(t *testing.T) {56 if gitOutboxAllowedRelay("wss://other.example", true, []string{"https://peer.example/git"}) {57 t.Fatal("unconfigured private relay was allowed")58 }59 if !gitOutboxAllowedRelay("wss://peer.example/git", true, []string{"https://peer.example/git"}) {60 t.Fatal("configured private relay was rejected")61 }62}6364func TestGitOutboxRelayTagsUseWriteCapableNIP65Entries(t *testing.T) {65 list := event.Event{Kind: 10002, Tags: [][]string{{"r", "wss://read.example", "read"}, {"r", "wss://write.example", "write"}, {"r", "wss://both.example"}}}66 got := gitOutboxRelayTags(list)67 if len(got) != 2 || got[0] != "wss://write.example" || got[1] != "wss://both.example" {68 t.Fatalf("relays = %#v", got)69 }70}7172func TestGitOutboxRelayTagsUseGroupEntriesForKind10317(t *testing.T) {73 list := event.Event{Kind: 10317, Tags: [][]string{{"r", "wss://wrong.example"}, {"g", "wss://group.example"}}}74 got := gitOutboxRelayTags(list)75 if len(got) != 1 || got[0] != "wss://group.example" {76 t.Fatalf("relays = %#v", got)77 }78}7980func TestGitConversationAuthorsAreLimitedToAdmittedParticipants(t *testing.T) {81 owner := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"82 maintainer := "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"83 participant := "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"84 authors := gitConversationAuthors([]event.Event{{Kind: event.KIND_GIT_ISSUE, PubKey: participant}, {Kind: event.KIND_PROFILE, PubKey: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"}}, owner, []string{maintainer})85 if len(authors) != 3 || authors[0] != owner || authors[1] != maintainer || authors[2] != participant {86 t.Fatalf("authors = %#v", authors)87 }88}8990func TestGitConversationAuthorsDoesNotDropParticipantsBeforeBatching(t *testing.T) {91 events := make([]event.Event, 40)92 for i := range events {93 events[i] = event.Event{Kind: event.KIND_GIT_ISSUE, PubKey: fmt.Sprintf("%064x", i+1)}94 }95 if got := gitConversationAuthors(events, "", nil); len(got) != len(events) {96 t.Fatalf("authors = %d, want %d", len(got), len(events))97 }98}99100func TestGitOutboxAuthorBatchRotatesPastLimit(t *testing.T) {101 events := make([]event.Event, 40)102 for i := range events {103 events[i] = event.Event{Kind: event.KIND_GIT_ISSUE, PubKey: fmt.Sprintf("%064x", i+1)}104 }105 authors := gitConversationAuthors(events, "", nil)106 first := boundedGitAuthorsFrom(authors, 0)107 second := boundedGitAuthorsFrom(authors, gitOutboxAuthorLimit)108 if len(first) != gitOutboxAuthorLimit || len(second) != len(authors)-gitOutboxAuthorLimit {109 t.Fatalf("batch sizes = %d and %d", len(first), len(second))110 }111 if first[0] == second[0] {112 t.Fatal("rotated batch repeated its first author")113 }114}115116func TestRotateGitRelaysChangesStartingPoint(t *testing.T) {117 input := []string{"wss://one.example", "wss://two.example", "wss://three.example"}118 got := rotateGitRelays(input, 1)119 if len(got) != 3 || got[0] != input[1] || got[2] != input[0] {120 t.Fatalf("rotated relays = %#v", got)121 }122 if input[0] != "wss://one.example" {123 t.Fatal("rotation modified input")124 }125}126127func TestDiscoverGitOutboxesRequiresTransport(t *testing.T) {128 _, err := discoverGitOutboxes(context.Background(), gitOutboxOptions{}, []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"})129 if err == nil || err.Error() != "GRASP-03: outbox transport is required" {130 t.Fatalf("error = %v", err)131 }132}133134func TestDiscoverGitOutboxesIsolatesUnavailableSeeds(t *testing.T) {135 secret, err := event.GenerateKey()136 if err != nil {137 t.Fatal(err)138 }139 item := event.Event{Kind: 10002, CreatedAt: 1, Tags: [][]string{{"r", "wss://outbox.example"}}}140 if err := event.Sign(&item, secret); err != nil {141 t.Fatal(err)142 }143 if err := event.Validate(item); err != nil {144 t.Fatal(err)145 }146 if got := gitOutboxRelayTags(item); len(got) != 1 {147 t.Fatalf("item relays = %#v", got)148 }149 transport := gitOutboxTransport{150 queries: map[string][]event.Event{"wss://healthy.example": {item}},151 errors: map[string]error{"wss://down.example": fmt.Errorf("connection refused")},152 }153 authors := []string{item.PubKey}154 if got := boundedGitRelays([]string{"wss://down.example", "wss://healthy.example"}, false, nil); len(got) != 2 {155 t.Fatalf("seed relays = %#v", got)156 }157 got, err := discoverGitOutboxes(context.Background(), gitOutboxOptions{Transport: transport, Seeds: []string{"wss://down.example", "wss://healthy.example"}}, authors)158 if err == nil {159 t.Fatal("expected isolated seed error to be reported")160 }161 if len(got) != 1 || got[0] != "wss://outbox.example" {162 t.Fatalf("relays = %#v, err = %v", got, err)163 }164}165166func containsGitKind(kinds []int, wanted int) bool {167 for _, kind := range kinds {168 if kind == wanted {169 return true170 }171 }172 return false173}