clone

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

feat/standalone-relay
tinyrelay / internal / policy / policy_test.go
policy_test.go 4.4K
1package policy23import (4 "encoding/json"5 "strings"6 "testing"78 "github.com/FelineStateMachine/tinyrelay/internal/event"9)1011func TestPatchOmittedAndEmptySections(t *testing.T) {12 cur := Defaults("owner")13 cur.Tags = []string{"one"}14 cur.Features.Files = true15 got, err := Patch(cur, map[string]json.RawMessage{16 "tags": json.RawMessage(`[]`),17 "features": json.RawMessage(`{"search":"off"}`),18 })19 if err != nil {20 t.Fatal(err)21 }22 if len(got.Tags) != 0 || got.Features.Search != "off" || !got.Features.Files {23 t.Fatalf("patch did not preserve or clear fields: %#v", got)24 }25}2627func TestJobsDefaultOnUntilSwitchedOff(t *testing.T) {28 if !Defaults("owner").Features.Jobs {29 t.Fatal("long tasks are off by default")30 }31 var stored Policy32 if err := json.Unmarshal([]byte(`{"owner":"owner","writes":"open","reads":"open","features":{"search":"prose"}}`), &stored); err != nil {33 t.Fatal(err)34 }35 if !stored.Features.Jobs {36 t.Fatal("a stored policy without the field lost long tasks")37 }38 patched, err := Patch(Defaults("owner"), map[string]json.RawMessage{"features": json.RawMessage(`{"jobs":false}`)})39 if err != nil {40 t.Fatal(err)41 }42 if patched.Features.Jobs || patched.Features.Search != "prose" {43 t.Fatalf("patch did not switch long tasks off: %#v", patched.Features)44 }45}4647func TestPrivateReadRequiresRecipient(t *testing.T) {48 p := Defaults("owner")49 p.Reads = "members"50 e := event.Event{Kind: 1059, Tags: [][]string{{"p", "recipient"}}}51 if CanRead(p, e, Access{Member: true, PubKeys: []string{"other"}}) {52 t.Fatal("member without recipient identity can read private event")53 }54 if !CanRead(p, e, Access{Member: true, PubKeys: []string{"recipient"}}) {55 t.Fatal("recipient cannot read private event")56 }57}5859func TestPolicyJSONHasNoHostedEconomy(t *testing.T) {60 b, err := json.Marshal(Defaults(strings.Repeat("a", 64)))61 if err != nil {62 t.Fatal(err)63 }64 for _, forbidden := range []string{"lease", "fuel", "maxBlobMB", "eventsPerMinute", "reqsPerMinute"} {65 if strings.Contains(string(b), forbidden) {66 t.Fatalf("policy contains hosted field %q: %s", forbidden, b)67 }68 }69}7071func TestGRASP08RequiresPrivateMembershipReads(t *testing.T) {72 p := Defaults(strings.Repeat("a", 64))73 p.Features.Grasp08 = true74 p.Features.Grasp = true75 if err := Validate(p); err == nil || !strings.Contains(err.Error(), "reads") {76 t.Fatalf("GRASP-08 with open reads accepted: %v", err)77 }78 p.Reads = "members"79 if err := Validate(p); err != nil {80 t.Fatalf("valid private GRASP-08 policy rejected: %v", err)81 }82}8384func TestGRASP08RequiresBaseGRASP(t *testing.T) {85 p := Defaults(strings.Repeat("a", 64))86 p.Features.Grasp08 = true87 p.Reads = "members"88 p.Features.Grasp = false89 if err := Validate(p); err == nil || !strings.Contains(err.Error(), "subfeatures") {90 t.Fatalf("GRASP-08 without base GRASP accepted: %v", err)91 }92}9394func TestPrivateRepositoryRequiresGRASP08PrivateTenant(t *testing.T) {95 e := event.Event{Kind: 30617, Tags: [][]string{{"private", "true"}}}96 if CanRead(Policy{Reads: "open"}, e, Access{}) {97 t.Fatal("private repository was readable on an open tenant")98 }99 p := Policy{Reads: "members"}100 if !CanRead(p, event.Event{Kind: 1}, Access{Member: true}) {101 t.Fatal("ordinary event became unreadable on a member tenant")102 }103 p.Features.Grasp08 = true104 if !CanRead(p, e, Access{Member: true}) {105 t.Fatal("private repository was unreadable on a GRASP-08 member tenant")106 }107}108109func TestGRASPHistoryExtensionsRequireGRASP02(t *testing.T) {110 cases := []struct {111 name string112 set func(*Features)113 }{114 {name: "grasp03", set: func(f *Features) { f.Grasp03 = true }},115 {name: "grasp05", set: func(f *Features) { f.Grasp05 = true }},116 }117 for _, tc := range cases {118 t.Run(tc.name, func(t *testing.T) {119 p := Defaults(strings.Repeat("a", 64))120 p.Features.Grasp = true121 tc.set(&p.Features)122 if err := Validate(p); err == nil || !strings.Contains(err.Error(), "grasp02") {123 t.Fatalf("accepted %s without GRASP-02: %v", tc.name, err)124 }125 p.Features.Grasp02 = true126 if err := Validate(p); err != nil {127 t.Fatalf("rejected %s with GRASP-02: %v", tc.name, err)128 }129 })130 }131}132133func TestValidatePrivatePeers(t *testing.T) {134 p := Defaults(strings.Repeat("a", 64))135 p.PrivatePeers = []string{"wss://peer.example/relay/"}136 if err := Validate(p); err != nil {137 t.Fatal(err)138 }139 p.PrivatePeers = []string{"https://peer.example/?token=bad"}140 if err := Validate(p); err == nil {141 t.Fatal("accepted private peer URL with query credentials")142 }143}