clone

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

feat/standalone-relay
tinyrelay / internal / configport / configport_test.go
configport_test.go 4.8K
1package configport23import (4 "context"5 "strings"6 "testing"78 "github.com/FelineStateMachine/tinyrelay/internal/community"9 "github.com/FelineStateMachine/tinyrelay/internal/policy"10 "github.com/FelineStateMachine/tinyrelay/internal/storage"11)1213func TestParsePlanOmittedAndEmptySections(t *testing.T) {14 ctx := context.Background()15 st, err := storage.Open(ctx, t.TempDir()+"/relay.db")16 if err != nil {17 t.Fatal(err)18 }19 defer st.Close()20 owner := strings.Repeat("a", 64)21 c, err := community.New(ctx, st, owner)22 if err != nil {23 t.Fatal(err)24 }25 cur := policy.Defaults(owner)26 svc := New(ConfigStore{Store: st, Community: c, Policy: func() policy.Policy { return cur }})27 doc, err := Parse([]byte(`{"format":"bind.ws/relay-config/2","policy":{"reads":"members"}}`))28 if err != nil {29 t.Fatal(err)30 }31 plan, err := svc.Plan(ctx, doc)32 if err != nil {33 t.Fatal(err)34 }35 if len(plan.Policy) == 0 {36 t.Fatal("expected policy diff")37 }38 if len(plan.Members) != 0 {39 t.Fatal("omitted members changed plan")40 }41 doc, err = Parse([]byte(`{"format":"bind.ws/relay-config/2","members":[]}`))42 if err != nil {43 t.Fatal(err)44 }45 plan, err = svc.Plan(ctx, doc)46 if err != nil {47 t.Fatal(err)48 }49 if !plan.MembersCleared {50 t.Fatal("empty members must clear")51 }52}5354func TestApplyDryRunAndEconomyMigrationWarning(t *testing.T) {55 ctx := context.Background()56 st, err := storage.Open(ctx, t.TempDir()+"/relay.db")57 if err != nil {58 t.Fatal(err)59 }60 defer st.Close()61 owner := strings.Repeat("a", 64)62 _, err = community.New(ctx, st, owner)63 if err != nil {64 t.Fatal(err)65 }66 cur := policy.Defaults(owner)67 changed := false68 svc := New(ConfigStore{Store: st, Policy: func() policy.Policy { return cur }, OnApplied: func(policy.Policy) { changed = true }})69 doc, err := Parse([]byte(`{"format":"bind.ws/relay-config/2","policy":{"fuel":10,"reads":"auth"}}`))70 if err != nil {71 t.Fatal(err)72 }73 if len(doc.Warnings) == 0 {74 t.Fatal("expected removed economy warning")75 }76 if _, err := svc.Apply(ctx, doc, true); err != nil {77 t.Fatal(err)78 }79 if changed {80 t.Fatal("dry run mutated policy")81 }82}8384func TestApplyAtomicAcrossPolicyMetadataAndJobs(t *testing.T) {85 ctx := context.Background()86 st, err := storage.Open(ctx, t.TempDir()+"/relay.db")87 if err != nil {88 t.Fatal(err)89 }90 defer st.Close()91 owner := strings.Repeat("a", 64)92 _, err = community.New(ctx, st, owner)93 if err != nil {94 t.Fatal(err)95 }96 cur := policy.Defaults(owner)97 if err := st.PutSetting(ctx, "policy", cur); err != nil {98 t.Fatal(err)99 }100 svc := New(ConfigStore{Store: st, Policy: func() policy.Policy { return cur }})101 if _, err := st.DB().Exec(`INSERT INTO config_jobs(id,recipe) VALUES('old','{"source":"old"}')`); err != nil {102 t.Fatal(err)103 }104 if _, err := st.DB().Exec(`INSERT INTO community_members(pubkey,name,note,role,created_at) VALUES(?,?,?,?,?)`, strings.Repeat("b", 64), "old", "", "member", 1); err != nil {105 t.Fatal(err)106 }107 doc, err := Parse([]byte(`{"format":"bind.ws/relay-config/2","policy":{"reads":"auth"},"members":[{"pubkey":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","role":"member"}],"kinds":{"allow":[1],"block":[1]},"jobs":[{"id":"new","source":"new"}]}`))108 if err != nil {109 t.Fatal(err)110 }111 if _, err := svc.Apply(ctx, doc, false); err == nil {112 t.Fatal("expected duplicate kind rule failure")113 }114 var got policy.Policy115 if err := st.GetSetting(ctx, "policy", &got); err != nil {116 t.Fatal(err)117 }118 if got.Reads != cur.Reads {119 t.Fatalf("policy committed despite rollback: %q", got.Reads)120 }121 var jobs, members int122 if err := st.DB().QueryRow(`SELECT count(*) FROM config_jobs WHERE id='old'`).Scan(&jobs); err != nil {123 t.Fatal(err)124 }125 if err := st.DB().QueryRow(`SELECT count(*) FROM community_members WHERE pubkey=?`, strings.Repeat("b", 64)).Scan(&members); err != nil {126 t.Fatal(err)127 }128 if jobs != 1 || members != 1 {129 t.Fatalf("metadata committed despite rollback: jobs=%d members=%d", jobs, members)130 }131}132133func TestApplyJobsFeedsReplicationQueue(t *testing.T) {134 ctx := context.Background()135 st, err := storage.Open(ctx, t.TempDir()+"/relay.db")136 if err != nil {137 t.Fatal(err)138 }139 defer st.Close()140 owner := strings.Repeat("a", 64)141 if _, err := community.New(ctx, st, owner); err != nil {142 t.Fatal(err)143 }144 cur := policy.Defaults(owner)145 svc := New(ConfigStore{Store: st, Community: nil, Policy: func() policy.Policy { return cur }})146 doc, err := Parse([]byte(`{"format":"bind.ws/relay-config/2","jobs":[{"id":"pull-1","kind":"pull","relays":["wss://relay.example"]}]}`))147 if err != nil {148 t.Fatal(err)149 }150 if _, err := svc.Apply(ctx, doc, false); err != nil {151 t.Fatal(err)152 }153 var payload string154 if err := st.DB().QueryRow(`SELECT payload FROM replication_jobs WHERE id='pull-1'`).Scan(&payload); err != nil {155 t.Fatal(err)156 }157 if !strings.Contains(payload, `"kind":"pull"`) {158 t.Fatalf("replication payload: %s", payload)159 }160}