1// Copyright 2023, 2024 The Forgejo Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package forgefed
5
6import (
7 "reflect"
8 "strings"
9 "testing"
10
11 "forgejo.org/modules/setting"
12 "forgejo.org/modules/validation"
13
14 ap "github.com/go-ap/activitypub"
15)
16
17func TestNewPersonId(t *testing.T) {
18 expected := PersonID{}
19 expected.ID = "1"
20 expected.Source = "forgejo"
21 expected.Schema = "https"
22 expected.Path = "api/v1/activitypub/user-id"
23 expected.Host = "an.other.host"
24 expected.Port = ""
25 expected.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/1"
26 sut, _ := NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
27 if sut != expected {
28 t.Errorf("expected: %v\n but was: %v\n", expected, sut)
29 }
30
31 expected = PersonID{}
32 expected.ID = "1"
33 expected.Source = "forgejo"
34 expected.Schema = "https"
35 expected.Path = "api/v1/activitypub/user-id"
36 expected.Host = "an.other.host"
37 expected.Port = "443"
38 expected.UnvalidatedInput = "https://an.other.host:443/api/v1/activitypub/user-id/1"
39 sut, _ = NewPersonID("https://an.other.host:443/api/v1/activitypub/user-id/1", "forgejo")
40 if sut != expected {
41 t.Errorf("expected: %v\n but was: %v\n", expected, sut)
42 }
43}
44
45func TestNewRepositoryId(t *testing.T) {
46 setting.AppURL = "http://localhost:3000/"
47 expected := RepositoryID{}
48 expected.ID = "1"
49 expected.Source = "forgejo"
50 expected.Schema = "http"
51 expected.Path = "api/activitypub/repository-id"
52 expected.Host = "localhost"
53 expected.Port = "3000"
54 expected.UnvalidatedInput = "http://localhost:3000/api/activitypub/repository-id/1"
55 sut, _ := NewRepositoryID("http://localhost:3000/api/activitypub/repository-id/1", "forgejo")
56 if sut != expected {
57 t.Errorf("expected: %v\n but was: %v\n", expected, sut)
58 }
59}
60
61func TestActorIdValidation(t *testing.T) {
62 sut := ActorID{}
63 sut.Source = "forgejo"
64 sut.Schema = "https"
65 sut.Path = "api/v1/activitypub/user-id"
66 sut.Host = "an.other.host"
67 sut.Port = ""
68 sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/"
69 if sut.Validate()[0] != "userId should not be empty" {
70 t.Errorf("validation error expected but was: %v\n", sut.Validate())
71 }
72
73 sut = ActorID{}
74 sut.ID = "1"
75 sut.Source = "forgejo"
76 sut.Schema = "https"
77 sut.Path = "api/v1/activitypub/user-id"
78 sut.Host = "an.other.host"
79 sut.Port = ""
80 sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/1?illegal=action"
81 if sut.Validate()[0] != "not all input was parsed, \nUnvalidated Input:\"https://an.other.host/api/v1/activitypub/user-id/1?illegal=action\" \nParsed URI: \"https://an.other.host/api/v1/activitypub/user-id/1\"" {
82 t.Errorf("validation error expected but was: %v\n", sut.Validate()[0])
83 }
84}
85
86func TestPersonIdValidation(t *testing.T) {
87 sut := PersonID{}
88 sut.ID = "1"
89 sut.Source = "forgejo"
90 sut.Schema = "https"
91 sut.Path = "path"
92 sut.Host = "an.other.host"
93 sut.Port = ""
94 sut.UnvalidatedInput = "https://an.other.host/path/1"
95
96 _, err := validation.IsValid(sut)
97 if validation.IsErrNotValid(err) && strings.Contains(err.Error(), "path: \"path\" has to be a person specific api path\n") {
98 t.Errorf("validation error expected but was: %v\n", err)
99 }
100
101 sut = PersonID{}
102 sut.ID = "1"
103 sut.Source = "forgejox"
104 sut.Schema = "https"
105 sut.Path = "api/v1/activitypub/user-id"
106 sut.Host = "an.other.host"
107 sut.Port = ""
108 sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/1"
109 if sut.Validate()[0] != "Value forgejox is not contained in allowed values [forgejo gitea]" {
110 t.Errorf("validation error expected but was: %v\n", sut.Validate()[0])
111 }
112}
113
114func TestWebfingerId(t *testing.T) {
115 sut, _ := NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
116 if sut.AsWebfinger() != "@12345@codeberg.org" {
117 t.Errorf("wrong webfinger: %v", sut.AsWebfinger())
118 }
119
120 sut, _ = NewPersonID("https://Codeberg.org/api/v1/activitypub/user-id/12345", "forgejo")
121 if sut.AsWebfinger() != "@12345@codeberg.org" {
122 t.Errorf("wrong webfinger: %v", sut.AsWebfinger())
123 }
124}
125
126func TestShouldThrowErrorOnInvalidInput(t *testing.T) {
127 var err any
128 // TODO: remove after test
129 //_, err = NewPersonId("", "forgejo")
130 //if err == nil {
131 // t.Errorf("empty input should be invalid.")
132 //}
133
134 _, err = NewPersonID("http://localhost:3000/api/v1/something", "forgejo")
135 if err == nil {
136 t.Errorf("localhost uris are not external")
137 }
138 _, err = NewPersonID("./api/v1/something", "forgejo")
139 if err == nil {
140 t.Errorf("relative uris are not allowed")
141 }
142 _, err = NewPersonID("http://1.2.3.4/api/v1/something", "forgejo")
143 if err == nil {
144 t.Errorf("uri may not be ip-4 based")
145 }
146 _, err = NewPersonID("http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo")
147 if err == nil {
148 t.Errorf("uri may not be ip-6 based")
149 }
150 _, err = NewPersonID("https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo")
151 if err == nil {
152 t.Errorf("uri may not contain relative path elements")
153 }
154 _, err = NewPersonID("https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo")
155 if err == nil {
156 t.Errorf("uri may not contain unparsed elements")
157 }
158
159 _, err = NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo")
160 if err != nil {
161 t.Errorf("this uri should be valid but was: %v", err)
162 }
163}
164
165func Test_PersonMarshalJSON(t *testing.T) {
166 sut := ForgePerson{}
167 sut.Type = "Person"
168 sut.PreferredUsername = ap.NaturalLanguageValuesNew()
169 sut.PreferredUsername.Set("en", ap.Content("MaxMuster"))
170 result, _ := sut.MarshalJSON()
171 if string(result) != "{\"type\":\"Person\",\"preferredUsername\":\"MaxMuster\"}" {
172 t.Errorf("MarshalJSON() was = %q", result)
173 }
174}
175
176func Test_PersonUnmarshalJSON(t *testing.T) {
177 expected := &ForgePerson{
178 Actor: ap.Actor{
179 Type: "Person",
180 PreferredUsername: ap.NaturalLanguageValues{
181 ap.LangRefValue{Ref: "en", Value: []byte("MaxMuster")},
182 },
183 },
184 }
185 sut := new(ForgePerson)
186 err := sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`))
187 if err != nil {
188 t.Errorf("UnmarshalJSON() unexpected error: %v", err)
189 }
190 x, _ := expected.MarshalJSON()
191 y, _ := sut.MarshalJSON()
192 if !reflect.DeepEqual(x, y) {
193 t.Errorf("UnmarshalJSON() expected: %q got: %q", x, y)
194 }
195
196 expectedStr := strings.ReplaceAll(strings.ReplaceAll(`{
197 "id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10",
198 "type":"Person",
199 "icon":{"type":"Image","mediaType":"image/png","url":"https://federated-repo.prod.meissa.de/avatar/fa7f9c4af2a64f41b1bef292bf872614"},
200 "url":"https://federated-repo.prod.meissa.de/stargoose9",
201 "inbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10/inbox",
202 "outbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10/outbox",
203 "preferredUsername":"stargoose9",
204 "publicKey":{"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10#main-key",
205 "owner":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10",
206 "publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBoj...XAgMBAAE=\n-----END PUBLIC KEY-----\n"}}`,
207 "\n", ""),
208 "\t", "")
209 err = sut.UnmarshalJSON([]byte(expectedStr))
210 if err != nil {
211 t.Errorf("UnmarshalJSON() unexpected error: %v", err)
212 }
213 result, _ := sut.MarshalJSON()
214 if expectedStr != string(result) {
215 t.Errorf("UnmarshalJSON() expected: %q got: %q", expectedStr, result)
216 }
217}
218
219func TestForgePersonValidation(t *testing.T) {
220 sut := new(ForgePerson)
221 sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`))
222 if res, _ := validation.IsValid(sut); !res {
223 t.Errorf("sut expected to be valid: %v\n", sut.Validate())
224 }
225}