tinyrelayfile
tinyrelay/internal/daemon/social_browse_test.go/

internal/daemon/social_browse_test.go raw
1package daemon
2
3import (
4 "context"
5 "encoding/json"
6 "strings"
7 "testing"
8
9 "github.com/FelineStateMachine/tinyrelay/internal/event"
10 "github.com/FelineStateMachine/tinyrelay/internal/storage"
11)
12
13func TestSocialBrowseChronologicalAndThread(t *testing.T) {
14 _, tenant := testTenant(t)
15 owner := tenant.Policy().Owner
16 secret := strings.Repeat("7", 63) + "8"
17 note := signedEvent(t, secret, 1, 200, [][]string{{"subject", "A note"}}, "hello markdown")
18 article := signedEvent(t, secret, 30023, 100, [][]string{{"d", "essay"}, {"title", "An essay"}, {"summary", "reference"}}, "long form")
19 comment := signedEvent(t, secret, 1, 300, [][]string{{"e", note.ID, "", "root", note.PubKey}}, "reply")
20 for _, row := range []event.Event{note, article, comment} {
21 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
22 t.Fatal(err)
23 }
24 }
25 raw, err := json.Marshal(map[string]any{"limit": 2})
26 if err != nil {
27 t.Fatal(err)
28 }
29 value, err := tenant.Execute(context.Background(), owner, "browsesocial", []json.RawMessage{raw})
30 if err != nil {
31 t.Fatal(err)
32 }
33 feed := value.(map[string]any)
34 items := feed["items"].([]socialItem)
35 if len(items) != 2 || items[0].ID != note.ID || items[1].ID != article.ID {
36 t.Fatalf("items = %#v", items)
37 }
38 check, _ := tenant.store.Query(context.Background(), event.Filter{Kinds: []int{1}, Tags: map[string][]string{"e": []string{note.ID}}}, storage.QueryOptions{Now: 999, Access: storage.Access{All: true}})
39 if len(check.Events) != 1 {
40 t.Fatalf("stored comments = %d", len(check.Events))
41 }
42 if items[0].ReplyCount != 1 {
43 t.Fatalf("reply count = %d", items[0].ReplyCount)
44 }
45 detail, err := tenant.Execute(context.Background(), owner, "browsesocialthread", []json.RawMessage{json.RawMessage(`{"id":"` + note.ID + `"}`)})
46 if err != nil {
47 t.Fatal(err)
48 }
49 if len(detail.(map[string]any)["comments"].([]socialItem)) != 1 {
50 t.Fatal("comment missing")
51 }
52}
53
54func TestSocialBrowseDoesNotExposeUnrelatedComments(t *testing.T) {
55 _, tenant := testTenant(t)
56 owner := tenant.Policy().Owner
57 secret := strings.Repeat("8", 63) + "9"
58 note := signedEvent(t, secret, 1, 100, nil, "note")
59 comment := signedEvent(t, secret, 1111, 200, [][]string{{"e", strings.Repeat("a", 64)}, {"k", "1"}}, "private-looking")
60 for _, row := range []event.Event{note, comment} {
61 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
62 t.Fatal(err)
63 }
64 }
65 value, err := tenant.Execute(context.Background(), owner, "browsesocial", nil)
66 if err != nil {
67 t.Fatal(err)
68 }
69 if len(value.(map[string]any)["items"].([]socialItem)) != 1 {
70 t.Fatal("unrelated comment leaked into feed")
71 }
72}
73
74func TestSocialBrowseNIP10NIP22AndRevisionReactions(t *testing.T) {
75 _, tenant := testTenant(t)
76 owner := tenant.Policy().Owner
77 authorSecret := strings.Repeat("1", 63) + "2"
78 otherSecret := strings.Repeat("3", 63) + "4"
79 note := signedEvent(t, authorSecret, 1, 500, nil, "root note")
80 legacyReply := signedEvent(t, otherSecret, 1, 501, [][]string{{"e", note.ID, ""}, {"e", note.ID, ""}}, "legacy reply")
81 nestedReply := signedEvent(t, authorSecret, 1, 502, [][]string{{"e", note.ID, "", "root", note.PubKey}, {"e", legacyReply.ID, "", "reply", legacyReply.PubKey}}, "nested reply")
82 articleOld := signedEvent(t, authorSecret, 30023, 400, [][]string{{"d", "essay:edition"}, {"title", "Old"}}, "old article")
83 article := signedEvent(t, authorSecret, 30023, 600, [][]string{{"d", "essay:edition"}, {"title", "Current"}}, "current article")
84 articleAddress := "30023:" + article.PubKey + ":essay:edition"
85 articleComment := signedEvent(t, otherSecret, 1111, 601, [][]string{
86 {"A", articleAddress}, {"E", articleOld.ID, "", articleOld.PubKey}, {"K", "30023"}, {"P", article.PubKey},
87 {"a", articleAddress}, {"e", articleOld.ID, "", articleOld.PubKey}, {"k", "30023"}, {"p", articleOld.PubKey},
88 }, "article comment")
89 reactionOld := signedEvent(t, otherSecret, 7, 602, [][]string{{"e", articleOld.ID}}, "")
90 reactionNew := signedEvent(t, otherSecret, 7, 603, [][]string{{"e", article.ID}, {"a", articleAddress}}, "")
91 strangerComment := signedEvent(t, otherSecret, 1111, 604, [][]string{{"E", strings.Repeat("f", 64)}, {"K", "30023"}}, "unrelated")
92 for _, row := range []event.Event{note, legacyReply, nestedReply, articleOld, article, articleComment, reactionOld, reactionNew, strangerComment} {
93 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
94 t.Fatal(err)
95 }
96 }
97 if check, queryErr := tenant.store.Query(context.Background(), event.Filter{Kinds: []int{1111}, Tags: map[string][]string{"A": {articleAddress}}}, storage.QueryOptions{Now: 999, Access: storage.Access{All: true}}); queryErr != nil || len(check.Events) != 1 {
98 t.Fatalf("article comment query = %d, %v", len(check.Events), queryErr)
99 }
100 value, err := tenant.Execute(context.Background(), owner, "browsesocial", []json.RawMessage{rawJSON(map[string]any{"limit": 20})})
101 if err != nil {
102 t.Fatal(err)
103 }
104 feed := value.(map[string]any)
105 items := feed["items"].([]socialItem)
106 if len(items) != 2 || items[0].Kind != 30023 || items[1].Kind != 1 {
107 t.Fatalf("feed roots = %#v", items)
108 }
109 articleItem := items[0]
110 if articleItem.Title != "Current" || articleItem.Address != articleAddress || articleItem.ReplyCount != 1 {
111 t.Fatalf("article item = %#v", articleItem)
112 }
113 if len(articleItem.Reactions) != 1 || articleItem.Reactions[0].Content != "+" || articleItem.Reactions[0].Count != 1 {
114 t.Fatalf("article reactions = %#v", articleItem.Reactions)
115 }
116 if items[1].ReplyCount != 2 {
117 t.Fatalf("note replies = %#v", items[1])
118 }
119 if items[1].RootID != items[1].ID || items[1].ParentID != "" || items[1].RootKind != 1 {
120 t.Fatalf("root metadata = %#v", items[1])
121 }
122 detail, err := tenant.Execute(context.Background(), owner, "browsesocialthread", []json.RawMessage{rawJSON(map[string]any{"id": nestedReply.ID, "limit": 20})})
123 if err != nil {
124 t.Fatal(err)
125 }
126 selected := detail.(map[string]any)["post"].(socialItem)
127 if selected.ParentID != legacyReply.ID || selected.RootID != note.ID || selected.ParentKind != 1 {
128 t.Fatalf("nested metadata = %#v", selected)
129 }
130 articleDetail, err := tenant.Execute(context.Background(), owner, "browsesocialthread", []json.RawMessage{rawJSON(map[string]any{"address": articleAddress, "limit": 20})})
131 if err != nil {
132 t.Fatal(err)
133 }
134 if len(articleDetail.(map[string]any)["comments"].([]socialItem)) != 1 {
135 t.Fatal("article comment missing")
136 }
137}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs