Blob


1 package apub
3 import (
4 "bytes"
5 "errors"
6 "net/mail"
7 "os"
8 "reflect"
9 "sort"
10 "strings"
11 "testing"
12 )
14 func TestMailAddress(t *testing.T) {
15 tests := []struct {
16 name string
17 from string
18 followers string
19 }{
20 {
21 "testdata/actor/mastodon.json",
22 `"Oliver Lowe" <otl@hachyderm.io>`,
23 `"Oliver Lowe (followers)" <otl+followers@hachyderm.io>`,
24 },
25 {
26 "testdata/actor/akkoma.json",
27 `"Kari'boka" <kariboka@social.harpia.red>`,
28 `"Kari'boka (followers)" <kariboka+followers@social.harpia.red>`,
29 },
30 {
31 "testdata/actor/lemmy.json",
32 "<Spotlight7573@lemmy.world>",
33 "<@>", // zero mail.Address
34 },
35 }
36 for _, tt := range tests {
37 f, err := os.Open(tt.name)
38 if err != nil {
39 t.Error(err)
40 continue
41 }
42 defer f.Close()
43 actor, err := DecodeActor(f)
44 if err != nil {
45 t.Errorf("%s: decode actor: %v", tt.name, err)
46 continue
47 }
48 if actor.Address().String() != tt.from {
49 t.Errorf("%s: from address: want %s, got %s", tt.name, tt.from, actor.Address().String())
50 }
51 got := actor.FollowersAddress().String()
52 if got != tt.followers {
53 t.Errorf("%s: followers address: want %s, got %s", tt.name, tt.followers, got)
54 }
55 }
56 }
58 func TestMarshalMail(t *testing.T) {
59 tests := []struct {
60 name string
61 recipients []string
62 }{
63 {
64 "testdata/note/akkoma.json",
65 []string{
66 "kariboka+followers@social.harpia.red",
67 "otl@apubtest2.srcbeat.com",
68 },
69 },
70 {
71 "testdata/note/lemmy.json",
72 []string{
73 "Feathercrown@lemmy.world",
74 "technology@lemmy.world",
75 },
76 },
77 {
78 "testdata/note/mastodon.json",
79 []string{
80 "otl+followers@hachyderm.io",
81 "selfhosted+followers@lemmy.world",
82 "selfhosted@lemmy.world",
83 },
84 },
85 {
86 "testdata/page.json",
87 []string{
88 "technology@lemmy.world",
89 },
90 },
91 }
92 for _, tt := range tests {
93 f, err := os.Open(tt.name)
94 if err != nil {
95 t.Error(err)
96 continue
97 }
98 defer f.Close()
99 a, err := Decode(f)
100 if err != nil {
101 t.Errorf("%s: decode activity: %v", tt.name, err)
102 continue
104 b, err := MarshalMail(a, nil)
105 if err != nil {
106 t.Errorf("%s: marshal to mail message: %v", tt.name, err)
107 continue
109 msg, err := mail.ReadMessage(bytes.NewReader(b))
110 if err != nil {
111 t.Errorf("%s: read back message from marshalled activity: %v", tt.name, err)
112 continue
114 rcptto, err := msg.Header.AddressList("To")
115 if errors.Is(err, mail.ErrHeaderNotPresent) {
116 // whatever; sometimes the Activity has an empty slice.
117 } else if err != nil {
118 t.Errorf("%s: parse To addresses: %v", tt.name, err)
119 t.Log("raw value:", msg.Header.Get("To"))
120 continue
122 rcptcc, err := msg.Header.AddressList("CC")
123 if errors.Is(err, mail.ErrHeaderNotPresent) {
124 // whatever; sometimes the Activity has an empty slice.
125 } else if err != nil {
126 t.Errorf("%s: parse CC addresses: %v", tt.name, err)
127 t.Log("raw value:", msg.Header.Get("CC"))
128 continue
130 t.Log(rcptto)
131 t.Log(rcptcc)
132 rcpts := make([]string, len(rcptto)+len(rcptcc))
133 for i, rcpt := range append(rcptto, rcptcc...) {
134 rcpts[i] = rcpt.Address
136 sort.Strings(rcpts)
137 if !reflect.DeepEqual(rcpts, tt.recipients) {
138 t.Errorf("%s: unexpected recipients, want %s got %s", tt.name, tt.recipients, rcpts)
141 p := make([]byte, 8)
142 if _, err := msg.Body.Read(p); err != nil {
143 // Pages have no content, so skip this case
144 if a.Type == "Page" {
145 continue
147 t.Errorf("%s: read message body: %v", tt.name, err)
152 func TestUnmarshalMail(t *testing.T) {
153 f, err := os.Open("testdata/outbound.eml")
154 if err != nil {
155 t.Fatal(err)
157 defer f.Close()
158 msg, err := mail.ReadMessage(f)
159 if err != nil {
160 t.Fatal(err)
162 if testing.Short() {
163 t.Skip("skipping network calls to unmarshal mail message to Activity")
165 a, err := UnmarshalMail(msg, nil)
166 if err != nil {
167 t.Fatal(err)
169 if len(a.Tag) != 1 {
170 t.Fatalf("wanted 1 tag in unmarshalled activity, got %d", len(a.Tag))
172 want := "@henfredemars@infosec.pub"
173 if a.Tag[0].Name != want {
174 t.Errorf("wanted tag name %s, got %s", want, a.Tag[0].Name)
176 if a.MediaType != "text/markdown" {
177 t.Errorf("wrong media type: wanted %s, got %s", "text/markdown", a.MediaType)
179 wantCC := []string{
180 "https://programming.dev/c/programming",
181 "https://programming.dev/u/starman",
182 "https://hachyderm.io/users/otl/followers",
184 if !reflect.DeepEqual(wantCC, a.CC) {
185 t.Errorf("wanted recipients %s, got %s", wantCC, a.CC)
187 if strings.Contains(a.Content, "\r") {
188 t.Errorf("activity content contains carriage returns")
190 t.Log(a)