tinyrelayfile
tinyrelay/internal/records/rooms_test.go/

internal/records/rooms_test.go raw
1package records
2
3import (
4 "context"
5 "database/sql"
6 "encoding/json"
7 "strings"
8 "testing"
9 "time"
10
11 "github.com/FelineStateMachine/tinyrelay/internal/community"
12 "github.com/FelineStateMachine/tinyrelay/internal/event"
13 "github.com/FelineStateMachine/tinyrelay/internal/policy"
14 "github.com/FelineStateMachine/tinyrelay/internal/storage"
15)
16
17func TestRoomRecordsAndNotices(t *testing.T) {
18 s, ctx := testStore(t)
19 secrets := map[string]string{"owner": strings.Repeat("0", 63) + "1", "alice": strings.Repeat("0", 63) + "2", "bob": strings.Repeat("0", 63) + "3", "carol": strings.Repeat("0", 63) + "4"}
20 keys := map[string]string{}
21 for name, secret := range secrets {
22 keys[name], _ = event.PublicKey(secret)
23 }
24 p := policy.Defaults(keys["owner"])
25 svc, err := community.New(ctx, s, keys["owner"])
26 if err != nil {
27 t.Fatal(err)
28 }
29 svc.ConfigurePolicy(func() policy.Policy { return p })
30 if err := svc.EnsureSlugRoom(ctx, "main", community.RoomOpen); err != nil {
31 t.Fatal(err)
32 }
33 raw := func(v any) json.RawMessage { b, _ := json.Marshal(v); return b }
34 for _, name := range []string{"alice", "bob", "carol"} {
35 if _, err := svc.Execute(ctx, keys["owner"], "setmember", []json.RawMessage{raw(keys[name]), raw(map[string]any{"role": "member"})}); err != nil {
36 t.Fatal(err)
37 }
38 }
39 send := func(who string, kind int, tags [][]string) {
40 t.Helper()
41 e := event.Event{Kind: kind, CreatedAt: time.Now().Unix(), Tags: tags}
42 if err := event.Sign(&e, secrets[who]); err != nil {
43 t.Fatal(err)
44 }
45 if _, err := svc.HandleRoomEventTx(ctx, e, func(tx *sql.Tx) error {
46 _, err := storage.SaveTx(ctx, tx, e, storage.SaveOptions{Now: time.Now().Unix()})
47 return err
48 }); err != nil {
49 t.Fatal(err)
50 }
51 }
52 send("alice", event.KIND_CREATE_GROUP, [][]string{{"h", "team"}, {"name", "Team"}, {"about", "Ours"}, {"picture", "https://img.example/t.png"}, {"closed"}})
53 send("alice", event.KIND_PUT_USER, [][]string{{"h", "team"}, {"p", keys["bob"], "admin"}})
54 send("alice", event.KIND_PUT_USER, [][]string{{"h", "team"}, {"p", keys["carol"]}})
55
56 var generated []event.Event
57 r, err := New(ctx, Config{Community: emptyCommunityReader{}, Store: s, Policy: func() policy.Policy { return p }, RelayURL: "wss://relay.example", GroupID: "main", OnGenerated: func(ctx context.Context, e event.Event) error {
58 generated = append(generated, e)
59 _, err := s.Save(ctx, e, storage.SaveOptions{Now: e.CreatedAt})
60 return err
61 }})
62 if err != nil {
63 t.Fatal(err)
64 }
65 records, err := r.PublishRoom(ctx, "team", 100)
66 if err != nil {
67 t.Fatal(err)
68 }
69 if len(records) != 3 || records[0].Kind != event.KIND_GROUP_METADATA || records[1].Kind != event.KIND_GROUP_ADMINS || records[2].Kind != event.KIND_GROUP_MEMBERS {
70 t.Fatalf("records = %+v", records)
71 }
72 meta := records[0]
73 if event.Tag(meta, "d") != "team" || event.Tag(meta, "name") != "Team" || event.Tag(meta, "about") != "Ours" || event.Tag(meta, "picture") != "https://img.example/t.png" {
74 t.Fatalf("metadata tags = %v", meta.Tags)
75 }
76 if !hasBareTag(meta, "closed") || hasBareTag(meta, "private") || !hasBareTag(meta, "public") {
77 t.Fatalf("access tags = %v", meta.Tags)
78 }
79 admins := map[string]string{}
80 for _, tag := range records[1].Tags {
81 if tag[0] == "p" {
82 admins[tag[1]] = tag[2]
83 }
84 }
85 if len(admins) != 2 || admins[keys["alice"]] != "owner" || admins[keys["bob"]] != "admin" {
86 t.Fatalf("admins = %v", admins)
87 }
88 if got := event.TagValues(records[2], "p"); len(got) != 3 {
89 t.Fatalf("members = %v", got)
90 }
91 for _, rec := range records {
92 if rec.PubKey != r.PublicKey() || event.Validate(rec) != nil {
93 t.Fatalf("record not relay-signed: %+v", rec)
94 }
95 }
96 p.Reads = "members"
97 records, err = r.PublishRoom(ctx, "team", 101)
98 if err != nil || !hasBareTag(records[0], "private") {
99 t.Fatalf("private marker missing: %v %v", records[0].Tags, err)
100 }
101 notice, err := r.RoomNotice(ctx, "team", keys["carol"], true, 102)
102 if err != nil || notice.Kind != event.KIND_ROOM_MEMBER_ADDED || event.Tag(notice, "h") != "team" || event.Tag(notice, "p") != keys["carol"] {
103 t.Fatalf("added notice = %+v %v", notice, err)
104 }
105 notice, err = r.RoomNotice(ctx, "team", keys["carol"], false, 103)
106 if err != nil || notice.Kind != event.KIND_ROOM_MEMBER_REMOVED {
107 t.Fatalf("removed notice = %+v %v", notice, err)
108 }
109 stored, err := s.Query(ctx, event.Filter{Kinds: []int{event.KIND_GROUP_METADATA, event.KIND_GROUP_ADMINS, event.KIND_GROUP_MEMBERS}, Authors: []string{r.PublicKey()}, Tags: map[string][]string{"d": {"team"}}}, storage.QueryOptions{Access: storage.Access{All: true}})
110 if err != nil || len(stored.Events) != 3 {
111 t.Fatalf("stored records = %d %v", len(stored.Events), err)
112 }
113 if err := r.RetireRoom(ctx, "team"); err != nil {
114 t.Fatal(err)
115 }
116 stored, err = s.Query(ctx, event.Filter{Kinds: []int{event.KIND_GROUP_METADATA}, Authors: []string{r.PublicKey()}, Tags: map[string][]string{"d": {"team"}}}, storage.QueryOptions{Access: storage.Access{All: true}})
117 if err != nil || len(stored.Events) != 0 {
118 t.Fatalf("retired records remain: %d %v", len(stored.Events), err)
119 }
120 if len(generated) < 8 {
121 t.Fatalf("generated %d events", len(generated))
122 }
123}
124
125func hasBareTag(e event.Event, name string) bool {
126 for _, tag := range e.Tags {
127 if len(tag) == 1 && tag[0] == name {
128 return true
129 }
130 }
131 return false
132}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs