clone

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

feat/ui-updates
tinyrelay / internal / relayapp / relayapp_test.go
relayapp_test.go 4.9K
1package relayapp23import (4 "context"5 "path/filepath"6 "strings"7 "testing"8 "time"910 "github.com/FelineStateMachine/tinyrelay/internal/event"11 "github.com/FelineStateMachine/tinyrelay/internal/relay"12)1314func TestStandaloneBackendPublishesAndQueriesSignedEvents(t *testing.T) {15 ctx := context.Background()16 b, err := Open(ctx, Config{DataDir: t.TempDir(), Owner: strings.Repeat("a", 64)})17 if err != nil {18 t.Fatal(err)19 }20 defer b.Close()21 secret := strings.Repeat("1", 64)22 e := event.Event{CreatedAt: time.Now().Unix(), Kind: 1, Tags: [][]string{}, Content: "hello"}23 if err := event.Sign(&e, secret); err != nil {24 t.Fatal(err)25 }26 if _, err := b.Publish(ctx, e, relay.Session{}); err != nil {27 t.Fatal(err)28 }29 rows, err := b.Query(ctx, []event.Filter{{IDs: []string{e.ID}}}, relay.Session{})30 if err != nil || len(rows) != 1 || rows[0].ID != e.ID {31 t.Fatalf("query = %#v, %v", rows, err)32 }33 if reason, err := b.Publish(ctx, e, relay.Session{}); err != nil || !strings.Contains(reason, "duplicate") {34 t.Fatalf("duplicate = %q, %v", reason, err)35 }36}3738func TestStandaloneAcceptsGenericNostrKinds(t *testing.T) {39 ctx := context.Background()40 b, err := Open(ctx, Config{DataDir: t.TempDir()})41 if err != nil {42 t.Fatal(err)43 }44 defer b.Close()45 secret := strings.Repeat("1", 64)46 pubkey, err := event.PublicKey(secret)47 if err != nil {48 t.Fatal(err)49 }50 ids := make([]string, 0, 3)51 for i, kind := range []int{24133, 5000, 4242, 30390} {52 tags := [][]string{}53 if kind == 24133 {54 tags = [][]string{{"p", pubkey}}55 }56 if kind == 30390 {57 tags = [][]string{{"d", "opaque"}}58 }59 e := event.Event{CreatedAt: time.Now().Unix() + int64(i), Kind: kind, Tags: tags, Content: "generic"}60 if err := event.Sign(&e, secret); err != nil {61 t.Fatal(err)62 }63 if _, err := b.Publish(ctx, e, relay.Session{}); err != nil {64 t.Fatalf("kind %d: %v", kind, err)65 }66 if kind == 24133 && !b.CanRead(e, relay.Session{}) {67 t.Fatal("anonymous NIP-46 event was blocked")68 }69 if kind != 24133 {70 ids = append(ids, e.ID)71 }72 }73 rows, err := b.Query(ctx, []event.Filter{{IDs: ids}}, relay.Session{PubKeys: []string{pubkey}})74 if err != nil || len(rows) != len(ids) {75 got := make([]string, len(rows))76 for i, row := range rows {77 got[i] = row.ID78 }79 t.Fatalf("generic query = %d/%d ids=%v want=%v, %v", len(rows), len(ids), got, ids, err)80 }81}8283func TestStandaloneNIP70AuthorOnlyPublication(t *testing.T) {84 ctx := context.Background()85 b, err := Open(ctx, Config{DataDir: t.TempDir()})86 if err != nil {87 t.Fatal(err)88 }89 defer b.Close()90 secret := strings.Repeat("1", 64)91 e := event.Event{CreatedAt: time.Now().Unix(), Kind: 1, Tags: [][]string{{"-"}}, Content: "protected"}92 if err := event.Sign(&e, secret); err != nil {93 t.Fatal(err)94 }95 if _, err := b.Publish(ctx, e, relay.Session{}); err == nil {96 t.Fatal("unauthenticated NIP-70 publication accepted")97 }98 pubkey, err := event.PublicKey(secret)99 if err != nil {100 t.Fatal(err)101 }102 if _, err := b.Publish(ctx, e, relay.Session{PubKeys: []string{pubkey}}); err != nil {103 t.Fatal(err)104 }105}106107func TestStandaloneAuthRequiredAppliesToReadsAndWrites(t *testing.T) {108 ctx := context.Background()109 b, err := Open(ctx, Config{DataDir: t.TempDir(), Owner: strings.Repeat("a", 64), AuthRequired: true})110 if err != nil {111 t.Fatal(err)112 }113 defer b.Close()114 secret := strings.Repeat("1", 64)115 e := event.Event{CreatedAt: time.Now().Unix(), Kind: 1, Tags: [][]string{}, Content: "hello"}116 if err := event.Sign(&e, secret); err != nil {117 t.Fatal(err)118 }119 if _, err := b.Publish(ctx, e, relay.Session{}); err == nil {120 t.Fatal("unauthenticated publish accepted")121 }122 owner, err := event.PublicKey(secret)123 if err != nil {124 t.Fatal(err)125 }126 if _, err := b.Publish(ctx, e, relay.Session{PubKeys: []string{owner}}); err != nil {127 t.Fatal(err)128 }129 if _, err := b.Query(ctx, []event.Filter{{IDs: []string{e.ID}}}, relay.Session{}); err == nil {130 t.Fatal("unauthenticated query accepted")131 }132}133134func TestInformationDoesNotAdvertiseHostedExtensions(t *testing.T) {135 b, err := Open(context.Background(), Config{DataDir: t.TempDir()})136 if err != nil {137 t.Fatal(err)138 }139 defer b.Close()140 info := b.Information()141 for _, n := range info["supported_nips"].([]int) {142 if n == 86 {143 t.Fatal("standalone relay advertises NIP-86")144 }145 }146}147148func TestOpenResolvesDataDirectoryAndReopens(t *testing.T) {149 base := t.TempDir()150 t.Chdir(base)151 relative := "relay"152 b, err := Open(context.Background(), Config{DataDir: relative})153 if err != nil {154 t.Fatal(err)155 }156 if !filepath.IsAbs(b.cfg.DataDir) {157 t.Fatalf("data directory is not absolute: %q", b.cfg.DataDir)158 }159 if _, err := Open(context.Background(), Config{DataDir: relative}); err == nil {160 t.Fatal("second backend opened the same data directory")161 }162 if err := b.Close(); err != nil {163 t.Fatal(err)164 }165 reopened, err := Open(context.Background(), Config{DataDir: relative})166 if err != nil {167 t.Fatal(err)168 }169 defer reopened.Close()170 if err := reopened.Ready(context.Background()); err != nil {171 t.Fatal(err)172 }173}