tinyrelayfile
tinyrelay/internal/daemon/direct_messages_test.go/

internal/daemon/direct_messages_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 TestBrowseDirectMessagesRequiresRecipientAndPaginates(t *testing.T) {
14 _, tenant := testTenant(t)
15 actorSecret := strings.Repeat("a", 63) + "1"
16 otherSecret := strings.Repeat("b", 63) + "2"
17 actor := signedEvent(t, actorSecret, event.KIND_WRAP, 100, [][]string{{"p", strings.Repeat("c", 64)}}, "ciphertext").Tags[0][1]
18 recipient := actor
19 first := signedEvent(t, otherSecret, event.KIND_WRAP, 200, [][]string{{"p", recipient}}, "first")
20 second := signedEvent(t, otherSecret, event.KIND_WRAP, 200, [][]string{{"p", recipient}}, "second")
21 third := signedEvent(t, otherSecret, event.KIND_WRAP, 199, [][]string{{"p", recipient}}, "third")
22 for _, row := range []event.Event{first, second, third} {
23 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
24 t.Fatal(err)
25 }
26 }
27 value, err := tenant.Execute(context.Background(), recipient, "browsedirectmessages", []json.RawMessage{rawJSON(map[string]any{"limit": 2})})
28 if err != nil {
29 t.Fatal(err)
30 }
31 page := value.(map[string]any)
32 events := page["events"].([]event.Event)
33 if len(events) != 2 || events[0].ID != first.ID || events[1].ID != second.ID {
34 t.Fatalf("first page = %#v", events)
35 }
36 next := page["next_cursor"].(string)
37 value, err = tenant.Execute(context.Background(), recipient, "browsedirectmessages", []json.RawMessage{rawJSON(map[string]any{"limit": 2, "cursor": next})})
38 if err != nil {
39 t.Fatal(err)
40 }
41 page = value.(map[string]any)
42 events = page["events"].([]event.Event)
43 if len(events) != 1 || events[0].ID != third.ID || page["next_cursor"].(string) != "" {
44 t.Fatalf("second page = %#v", page)
45 }
46 if events[0].Content != "third" {
47 t.Fatalf("message content changed: %q", events[0].Content)
48 }
49}
50
51func TestBrowseDirectMessagesDoesNotUseOwnerBypass(t *testing.T) {
52 _, tenant := testTenant(t)
53 owner := tenant.Policy().Owner
54 other := strings.Repeat("d", 64)
55 row := signedEvent(t, strings.Repeat("e", 63)+"f", event.KIND_WRAP, 100, [][]string{{"p", other}}, "secret")
56 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
57 t.Fatal(err)
58 }
59 if _, err := tenant.Execute(context.Background(), owner, "browsedirectmessages", []json.RawMessage{rawJSON(map[string]any{"limit": 10})}); err != nil {
60 t.Fatal(err)
61 }
62 page := mustDirectMessagePage(t, tenant, owner)
63 if len(page["events"].([]event.Event)) != 0 {
64 t.Fatal("owner saw a message addressed to another user")
65 }
66}
67
68func TestBrowseDirectMessagesRequiresValidActor(t *testing.T) {
69 _, tenant := testTenant(t)
70 for _, actor := range []string{"", "not-a-pubkey"} {
71 if _, err := tenant.Execute(context.Background(), actor, "browsedirectmessages", nil); err == nil || !strings.HasPrefix(err.Error(), "auth-required:") {
72 t.Fatalf("actor %q error = %v", actor, err)
73 }
74 }
75}
76
77func TestBrowseDirectMessagesFiltersRecipientAndKind(t *testing.T) {
78 _, tenant := testTenant(t)
79 actorSecret := strings.Repeat("1", 63) + "2"
80 actor := signedEvent(t, actorSecret, event.KIND_WRAP, 99, nil, "").PubKey
81 actorMessage := signedEvent(t, actorSecret, event.KIND_WRAP, 100, [][]string{{"p", actor}}, "recipient")
82 otherRecipient := signedEvent(t, strings.Repeat("3", 63)+"4", event.KIND_WRAP, 101, [][]string{{"p", strings.Repeat("f", 64)}}, "other")
83 wrongKind := signedEvent(t, strings.Repeat("5", 63)+"6", event.KIND_DM, 102, [][]string{{"p", actor}}, "legacy")
84 for _, row := range []event.Event{actorMessage, otherRecipient, wrongKind} {
85 if _, err := tenant.store.Save(context.Background(), row, storage.SaveOptions{Now: row.CreatedAt, SearchMode: tenant.Policy().Features.Search}); err != nil {
86 t.Fatal(err)
87 }
88 }
89 value, err := tenant.Execute(context.Background(), actor, "browsedirectmessages", nil)
90 if err != nil {
91 t.Fatal(err)
92 }
93 events := value.(map[string]any)["events"].([]event.Event)
94 if len(events) != 1 || events[0].ID != actorMessage.ID {
95 t.Fatalf("filtered events = %#v", events)
96 }
97}
98
99func TestBrowseDirectMessagesRejectsInvalidCursor(t *testing.T) {
100 _, tenant := testTenant(t)
101 actor := strings.Repeat("a", 64)
102 _, err := tenant.Execute(context.Background(), actor, "browsedirectmessages", []json.RawMessage{rawJSON(map[string]any{"cursor": "broken"})})
103 if err == nil || !strings.HasPrefix(err.Error(), "invalid: direct message cursor") {
104 t.Fatalf("cursor error = %v", err)
105 }
106}
107
108func mustDirectMessagePage(t *testing.T, tenant *Tenant, actor string) map[string]any {
109 t.Helper()
110 value, err := tenant.Execute(context.Background(), actor, "browsedirectmessages", nil)
111 if err != nil {
112 t.Fatal(err)
113 }
114 return value.(map[string]any)
115}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs