clone

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

feat/ui-updates
tinyrelay / internal / daemon / notifications_test.go
notifications_test.go 3.1K
1package daemon23import (4 "context"5 "path/filepath"6 "testing"78 "github.com/FelineStateMachine/tinyrelay/internal/catalog"9 "github.com/FelineStateMachine/tinyrelay/internal/event"10 "github.com/FelineStateMachine/tinyrelay/internal/storage"11 "github.com/FelineStateMachine/tinyrelay/internal/work"12)1314func TestDeliverNotificationQueuesPrivateLocalAndRecipientWorkAtomically(t *testing.T) {15 ctx := context.Background()16 store, err := storage.Open(ctx, filepath.Join(t.TempDir(), "tenant.db"))17 if err != nil {18 t.Fatal(err)19 }20 defer store.Close()21 secret, err := event.GenerateKey()22 if err != nil {23 t.Fatal(err)24 }25 recipient, err := event.PublicKey(secret)26 if err != nil {27 t.Fatal(err)28 }29 wrap := event.Event{CreatedAt: 10, Kind: event.KIND_WRAP, Tags: [][]string{{"p", recipient}}, Content: "cipher"}30 if err := event.Sign(&wrap, secret); err != nil {31 t.Fatal(err)32 }33 tenant := &Tenant{store: store, meta: catalog.Tenant{ID: "tenant-1"}, publicURL: "http://relay.example"}34 if err := tenant.deliverNotification(ctx, wrap, recipient); err != nil {35 t.Fatal(err)36 }37 var count int38 if err := store.DB().QueryRowContext(ctx, `SELECT count(*) FROM work_intents WHERE event_id=?`, wrap.ID).Scan(&count); err != nil {39 t.Fatal(err)40 }41 if count != 2 {42 t.Fatalf("queued intents = %d, want local broadcast and recipient delivery", count)43 }44 var target string45 if err := store.DB().QueryRowContext(ctx, `SELECT target FROM work_intents WHERE kind=?`, notificationDelivery).Scan(&target); err != nil {46 t.Fatal(err)47 }48 if target != recipient {49 t.Fatalf("delivery target = %q, want recipient %q", target, recipient)50 }51 if err := store.DB().QueryRowContext(ctx, `SELECT count(*) FROM events WHERE kind=10050`).Scan(&count); err != nil {52 t.Fatal(err)53 }54 if count != 0 {55 t.Fatal("stored a recipient's kind-10050 list locally")56 }57}5859func TestNotificationRelayValidation(t *testing.T) {60 if !validRelayURL("wss://inbox.example") || !validRelayURL("ws://localhost") {61 t.Fatal("valid relay URL rejected")62 }63 if validRelayURL("https://inbox.example") || validRelayURL("relay.example") {64 t.Fatal("non-relay URL accepted")65 }66 if !sameRelay("wss://relay.example/", "wss://relay.example") {67 t.Fatal("trailing slash not normalized")68 }69}7071func TestNotificationDeliveryRequiresRecipientInboxList(t *testing.T) {72 ctx := context.Background()73 store, err := storage.Open(ctx, filepath.Join(t.TempDir(), "tenant.db"))74 if err != nil {75 t.Fatal(err)76 }77 defer store.Close()78 secret, err := event.GenerateKey()79 if err != nil {80 t.Fatal(err)81 }82 recipient, err := event.PublicKey(secret)83 if err != nil {84 t.Fatal(err)85 }86 wrap := event.Event{CreatedAt: 10, Kind: event.KIND_WRAP, Tags: [][]string{{"p", recipient}}, Content: "cipher"}87 if err := event.Sign(&wrap, secret); err != nil {88 t.Fatal(err)89 }90 tenant := &Tenant{store: store}91 if err := tenant.deliverNotification(ctx, wrap, recipient); err != nil {92 t.Fatal(err)93 }94 handler := tenant.notificationHandlers()[notificationDelivery]95 err = handler(ctx, work.Intent{Kind: notificationDelivery, EventID: wrap.ID, Target: recipient})96 if err == nil {97 t.Fatal("delivery without kind-10050 inbox should remain retryable")98 }99}