Adding upstream version 0.0~git20250501.71edba4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c6ff472a6d
commit
c8085bda34
87 changed files with 24009 additions and 0 deletions
67
tests/integration_test.go
Normal file
67
tests/integration_test.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
j "github.com/go-ap/jsonld"
|
||||
|
||||
pub "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func TestAcceptSerialization(t *testing.T) {
|
||||
obj := pub.AcceptNew("https://localhost/myactivity", nil)
|
||||
obj.Name = make(pub.NaturalLanguageValues, 1)
|
||||
obj.Name.Set("en", pub.Content("test"))
|
||||
obj.Name.Set("fr", pub.Content("teste"))
|
||||
|
||||
uri := "https://www.w3.org/ns/activitystreams"
|
||||
p := j.WithContext(j.IRI(uri))
|
||||
|
||||
data, err := p.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(data), uri) {
|
||||
t.Errorf("Could not find context url %#v in output %s", p.Context, data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.ID)) {
|
||||
t.Errorf("Could not find id %#v in output %s", string(obj.ID), data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.Name.Get("en"))) {
|
||||
t.Errorf("Could not find name %#v in output %s", string(obj.Name.Get("en")), data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.Name.Get("fr"))) {
|
||||
t.Errorf("Could not find name %#v in output %s", string(obj.Name.Get("fr")), data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.Type)) {
|
||||
t.Errorf("Could not find activity type %#v in output %s", obj.Type, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateActivityHTTPSerialization(t *testing.T) {
|
||||
id := pub.ID("test_object")
|
||||
obj := pub.AcceptNew(id, nil)
|
||||
obj.Name.Set("en", pub.Content("Accept New"))
|
||||
|
||||
uri := string(pub.ActivityBaseURI)
|
||||
|
||||
data, err := j.WithContext(j.IRI(uri)).Marshal(obj)
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(data), uri) {
|
||||
t.Errorf("Could not find context url %#v in output %s", j.GetContext(), data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.ID)) {
|
||||
t.Errorf("Could not find id %#v in output %s", string(obj.ID), data)
|
||||
}
|
||||
if !strings.Contains(string(data), obj.Name.Get("en").String()) {
|
||||
t.Errorf("Could not find name %s in output %s", obj.Name.Get("en"), data)
|
||||
}
|
||||
if !strings.Contains(string(data), string(obj.Type)) {
|
||||
t.Errorf("Could not find activity type %#v in output %s", obj.Type, data)
|
||||
}
|
||||
}
|
22
tests/mocks/activity_create_multiple_objects.json
Normal file
22
tests/mocks/activity_create_multiple_objects.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"type": "Create",
|
||||
"actor": "https://littr.git/api/accounts/anonymous",
|
||||
"object": [
|
||||
{
|
||||
"type": "Note",
|
||||
"attributedTo": "https://littr.git/api/accounts/anonymous",
|
||||
"inReplyTo": "https://littr.git/api/accounts/system/outbox/7ca154ff",
|
||||
"content": "<p>Hello world</p>",
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
{
|
||||
"type": "Article",
|
||||
"id": "http://www.test.example/article/1",
|
||||
"name": "This someday will grow up to be an article",
|
||||
"inReplyTo": [
|
||||
"http://www.test.example/object/1",
|
||||
"http://www.test.example/object/778"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
12
tests/mocks/activity_create_simple.json
Normal file
12
tests/mocks/activity_create_simple.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "Create",
|
||||
"actor": "https://littr.git/api/accounts/anonymous",
|
||||
|
||||
"object": {
|
||||
"type": "Note",
|
||||
"attributedTo": "https://littr.git/api/accounts/anonymous",
|
||||
"inReplyTo": "https://littr.git/api/accounts/system/outbox/7ca154ff",
|
||||
"content": "<p>Hello world</p>",
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
}
|
13
tests/mocks/activity_simple.json
Normal file
13
tests/mocks/activity_simple.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Activity",
|
||||
"summary": "Sally did something to a note",
|
||||
"actor": {
|
||||
"type": "Person",
|
||||
"name": "Sally"
|
||||
},
|
||||
"object": {
|
||||
"type": "Note",
|
||||
"name": "A Note"
|
||||
}
|
||||
}
|
10
tests/mocks/article_with_multiple_inreplyto.json
Normal file
10
tests/mocks/article_with_multiple_inreplyto.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Article",
|
||||
"id": "http://www.test.example/article/1",
|
||||
"name": "This someday will grow up to be an article",
|
||||
"inReplyTo": [
|
||||
"http://www.test.example/object/1",
|
||||
"http://www.test.example/object/778"
|
||||
]
|
||||
}
|
1
tests/mocks/empty.json
Normal file
1
tests/mocks/empty.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
11
tests/mocks/like_activity_with_iri_actor.json
Normal file
11
tests/mocks/like_activity_with_iri_actor.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "Like",
|
||||
"actor": "https://littr.git/api/accounts/24d4b96f",
|
||||
"object": {
|
||||
"id": "https://littr.git/api/accounts/ana/liked/7ca154ff",
|
||||
"type": "Article"
|
||||
},
|
||||
"published": "2018-09-06T15:15:09Z",
|
||||
"to": null,
|
||||
"cc": null
|
||||
}
|
8
tests/mocks/link_simple.json
Normal file
8
tests/mocks/link_simple.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Link",
|
||||
"href": "http://example.org/abc",
|
||||
"hrefLang": "en",
|
||||
"mediaType": "text/html",
|
||||
"name": "An example link"
|
||||
}
|
6
tests/mocks/natural_language_values.json
Normal file
6
tests/mocks/natural_language_values.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"-": "\n\t\t\n",
|
||||
"en": "Ana got apples ⓐ",
|
||||
"fr": "Aná a des pommes ⒜",
|
||||
"ro": "Ana are mere"
|
||||
}
|
5
tests/mocks/object_no_type.json
Normal file
5
tests/mocks/object_no_type.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://www.test.example/object/1",
|
||||
"name": "A Simple, non-specific object without a type"
|
||||
}
|
6
tests/mocks/object_simple.json
Normal file
6
tests/mocks/object_simple.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Object",
|
||||
"id": "http://www.test.example/object/1",
|
||||
"name": "A Simple, non-specific object"
|
||||
}
|
18
tests/mocks/object_with_audience.json
Normal file
18
tests/mocks/object_with_audience.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Object",
|
||||
"id": "http://www.test.example/object/1",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"bto": [
|
||||
"http://example.com/sharedInbox"
|
||||
],
|
||||
"cc": [
|
||||
"https://example.com/actors/ana",
|
||||
"https://example.com/actors/bob"
|
||||
],
|
||||
"bcc": [
|
||||
"https://darkside.cookie/actors/darthvader"
|
||||
]
|
||||
}
|
17
tests/mocks/object_with_replies.json
Normal file
17
tests/mocks/object_with_replies.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Object",
|
||||
"id": "http://www.test.example/object/1",
|
||||
"replies": {
|
||||
"id": "http://www.test.example/object/1/replies",
|
||||
"type": "Collection",
|
||||
"totalItems": 1,
|
||||
"items": [
|
||||
{
|
||||
"id": "http://www.test.example/object/1/replies/2",
|
||||
"type": "Article",
|
||||
"name": "Example title"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
18
tests/mocks/object_with_tags.json
Normal file
18
tests/mocks/object_with_tags.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Object",
|
||||
"id": "http://www.test.example/object/1",
|
||||
"name": "A Simple, non-specific object",
|
||||
"tag": [
|
||||
{
|
||||
"name": "#my_tag",
|
||||
"id": "http://example.com/tag/my_tag",
|
||||
"type": "Mention"
|
||||
},
|
||||
{
|
||||
"name": "@ana",
|
||||
"type": "Mention",
|
||||
"id": "http://example.com/users/ana"
|
||||
}
|
||||
]
|
||||
}
|
4
tests/mocks/object_with_url.json
Normal file
4
tests/mocks/object_with_url.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"@context":"https://www.w3.org/ns/activitystreams",
|
||||
"url":"http://littr.git/api/accounts/system"
|
||||
}
|
7
tests/mocks/object_with_url_collection.json
Normal file
7
tests/mocks/object_with_url_collection.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"@context":"https://www.w3.org/ns/activitystreams",
|
||||
"url": [
|
||||
"http://littr.git/api/accounts/system",
|
||||
"http://littr.git/~system"
|
||||
]
|
||||
}
|
20
tests/mocks/ordered_collection.json
Normal file
20
tests/mocks/ordered_collection.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://example.com/outbox",
|
||||
"type": "OrderedCollection",
|
||||
"url": "http://example.com/outbox",
|
||||
"totalItems": 1,
|
||||
"orderedItems": [
|
||||
{
|
||||
"id": "http://example.com/outbox/53c6fb47",
|
||||
"type": "Article",
|
||||
"name": "Example title",
|
||||
"content": "Example content!",
|
||||
"url": "http://example.com/53c6fb47",
|
||||
"mediaType": "text/markdown",
|
||||
"published": "2018-07-05T16:46:44.00000Z",
|
||||
"generator": "http://example.com",
|
||||
"attributedTo": "http://example.com/accounts/alice"
|
||||
}
|
||||
]
|
||||
}
|
25
tests/mocks/ordered_collection_page.json
Normal file
25
tests/mocks/ordered_collection_page.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://example.com/outbox?page=2",
|
||||
"type": "OrderedCollectionPage",
|
||||
"url": "http://example.com/outbox?page=2",
|
||||
"totalItems": 1,
|
||||
"partOf": "http://example.com/outbox",
|
||||
"current": "http://example.com/outbox?page=2",
|
||||
"next": "http://example.com/outbox?page=3",
|
||||
"prev" : "http://example.com/outbox?page=1",
|
||||
"startIndex": 100,
|
||||
"orderedItems": [
|
||||
{
|
||||
"id": "http://example.com/outbox/53c6fb47",
|
||||
"type": "Article",
|
||||
"name": "Example title",
|
||||
"content": "Example content!",
|
||||
"url": "http://example.com/53c6fb47",
|
||||
"mediaType": "text/markdown",
|
||||
"published": "2018-07-05T16:46:44.00000Z",
|
||||
"generator": "http://example.com",
|
||||
"attributedTo": "http://example.com/accounts/alice"
|
||||
}
|
||||
]
|
||||
}
|
13
tests/mocks/person_with_outbox.json
Normal file
13
tests/mocks/person_with_outbox.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "http://example.com/accounts/ana",
|
||||
"type": "Person",
|
||||
"name": "ana",
|
||||
"url": "http://example.com/accounts/ana",
|
||||
"outbox": {
|
||||
"id": "http://example.com/accounts/ana/outbox",
|
||||
"type": "OrderedCollection",
|
||||
"url": "http://example.com/outbox"
|
||||
},
|
||||
"preferredUsername": "Ana"
|
||||
}
|
13
tests/mocks/travel_simple.json
Normal file
13
tests/mocks/travel_simple.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Travel",
|
||||
"summary": "Sally went to work",
|
||||
"actor": {
|
||||
"type": "Person",
|
||||
"name": "Sally"
|
||||
},
|
||||
"target": {
|
||||
"type": "Place",
|
||||
"name": "Work"
|
||||
}
|
||||
}
|
54
tests/server_common_test.go
Normal file
54
tests/server_common_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package tests
|
||||
|
||||
// Common server tests...
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Server: Fetching the inbox
|
||||
// Try retrieving the actor's inbox of an actor.
|
||||
// Server responds to GET request at inbox URL
|
||||
func TestInboxGETRequest(t *testing.T) {
|
||||
desc := `
|
||||
Server: Fetching the inbox
|
||||
Try retrieving the actor's inbox of an actor.
|
||||
|
||||
Server responds to GET request at inbox URL
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// Server: Fetching the inbox
|
||||
// Try retrieving the actor's inbox of an actor.
|
||||
// inbox is an OrderedCollection
|
||||
func TestInboxIsOrderedCollection(t *testing.T) {
|
||||
desc := `
|
||||
Server: Fetching the inbox
|
||||
Try retrieving the actor's inbox of an actor.
|
||||
|
||||
inbox is an OrderedCollection
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// Server: Fetching the inbox
|
||||
// Try retrieving the actor's inbox of an actor.
|
||||
// Server filters inbox content according to the requester's permission
|
||||
func TestInboxFilteringBasedOnPermissions(t *testing.T) {
|
||||
desc := `
|
||||
Server: Fetching the inbox
|
||||
Try retrieving the actor's inbox of an actor.
|
||||
|
||||
Server filters inbox content according to the requester's permission
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
/*
|
||||
func Test_(t *testing.T) {
|
||||
desc := `
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
*/
|
569
tests/server_to_server_test.go
Normal file
569
tests/server_to_server_test.go
Normal file
|
@ -0,0 +1,569 @@
|
|||
package tests
|
||||
|
||||
// Server to Server tests from: https://test.activitypub.rocks/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
pub "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
// S2S Server: Activities requiring the object property
|
||||
// The distribution of the following activities require that they contain the object property:
|
||||
// Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.
|
||||
// Implementation always includes object property for each of the above supported activities
|
||||
func TestObjectPropertyExists(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activities requiring the object property
|
||||
The distribution of the following activities require that they contain the object property:
|
||||
Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.
|
||||
|
||||
Implementation always includes object property for each of the above supported activities
|
||||
`
|
||||
t.Log(desc)
|
||||
|
||||
obj := pub.MentionNew("gigel")
|
||||
|
||||
add := pub.AddNew("https://localhost/myactivity", obj, nil)
|
||||
if pub.IsNil(add.Object) {
|
||||
t.Errorf("Missing GetID in Add activity %#v", add.Object)
|
||||
}
|
||||
if add.Object != obj {
|
||||
t.Errorf("Add.GetID different than what we initialized %#v %#v", add.Object, obj)
|
||||
}
|
||||
|
||||
block := pub.BlockNew("https://localhost/myactivity", obj)
|
||||
if pub.IsNil(block.Object) {
|
||||
t.Errorf("Missing GetID in Add activity %#v", block.Object)
|
||||
}
|
||||
if block.Object != obj {
|
||||
t.Errorf("Block.GetID different than what we initialized %#v %#v", block.Object, obj)
|
||||
}
|
||||
|
||||
create := pub.CreateNew("https://localhost/myactivity", obj)
|
||||
if create.Object == nil {
|
||||
t.Errorf("Missing GetID in Add activity %#v", create.Object)
|
||||
}
|
||||
if create.Object != obj {
|
||||
t.Errorf("Create.GetID different than what we initialized %#v %#v", create.Object, obj)
|
||||
}
|
||||
|
||||
delete := pub.DeleteNew("https://localhost/myactivity", obj)
|
||||
if pub.IsNil(delete.Object) {
|
||||
t.Errorf("Missing GetID in Delete activity %#v", delete.Object)
|
||||
}
|
||||
if delete.Object != obj {
|
||||
t.Errorf("Delete.GetID different than what we initialized %#v %#v", delete.Object, obj)
|
||||
}
|
||||
|
||||
follow := pub.FollowNew("https://localhost/myactivity", obj)
|
||||
if pub.IsNil(follow.Object) {
|
||||
t.Errorf("Missing GetID in Follow activity %#v", follow.Object)
|
||||
}
|
||||
if follow.Object != obj {
|
||||
t.Errorf("Follow.GetID different than what we initialized %#v %#v", follow.Object, obj)
|
||||
}
|
||||
|
||||
like := pub.LikeNew("https://localhost/myactivity", obj)
|
||||
if pub.IsNil(like.Object) {
|
||||
t.Errorf("Missing GetID in Like activity %#v", like.Object)
|
||||
}
|
||||
if like.Object != obj {
|
||||
t.Errorf("Like.GetID different than what we initialized %#v %#v", add.Object, obj)
|
||||
}
|
||||
|
||||
update := pub.UpdateNew("https://localhost/myactivity", obj)
|
||||
if pub.IsNil(update.Object) {
|
||||
t.Errorf("Missing GetID in Update activity %#v", update.Object)
|
||||
}
|
||||
if update.Object != obj {
|
||||
t.Errorf("Update.GetID different than what we initialized %#v %#v", update.Object, obj)
|
||||
}
|
||||
|
||||
undo := pub.UndoNew("https://localhost/myactivity", obj)
|
||||
if undo.Object == nil {
|
||||
t.Errorf("Missing GetID in Undo activity %#v", undo.Object)
|
||||
}
|
||||
if undo.Object != obj {
|
||||
t.Errorf("Undo.GetID different than what we initialized %#v %#v", undo.Object, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// S2S Server: Activities requiring the target property
|
||||
// The distribution of the following activities require that they contain the target property:
|
||||
// Add, Remove.
|
||||
// Implementation always includes target property for each of the above supported activities.
|
||||
func TestTargetPropertyExists(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activities requiring the target property
|
||||
The distribution of the following activities require that they contain the target
|
||||
property: Add, Remove.
|
||||
|
||||
Implementation always includes target property for each of the above supported activities.
|
||||
`
|
||||
t.Log(desc)
|
||||
|
||||
obj := pub.MentionNew("foo")
|
||||
target := pub.MentionNew("bar")
|
||||
|
||||
add := pub.AddNew("https://localhost/myactivity", obj, target)
|
||||
if pub.IsNil(add.Target) {
|
||||
t.Errorf("Missing Target in Add activity %#v", add.Target)
|
||||
}
|
||||
if add.Target != target {
|
||||
t.Errorf("Add.Target different than what we initialized %#v %#v", add.Target, target)
|
||||
}
|
||||
|
||||
remove := pub.RemoveNew("https://localhost/myactivity", obj, target)
|
||||
if pub.IsNil(remove.Target) {
|
||||
t.Errorf("Missing Target in Remove activity %#v", remove.Target)
|
||||
}
|
||||
if remove.Target != target {
|
||||
t.Errorf("Remove.Target different than what we initialized %#v %#v", remove.Target, target)
|
||||
}
|
||||
}
|
||||
|
||||
// S2S Server: Deduplication of recipient list
|
||||
// Attempt to submit for delivery an activity that addresses the same actor
|
||||
// (ie an actor with the same id) twice.
|
||||
// (For example, the same actor could appear on both the to and cc fields,
|
||||
// or the actor could be explicitly addressed
|
||||
// in to but could also be a member of the addressed followers collection of the sending actor.)
|
||||
// The server should deduplicate the list of inboxes to deliver to before delivering.
|
||||
// The final recipient list is deduplicated before delivery.
|
||||
func TestDeduplication(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Deduplication of recipient list
|
||||
Attempt to submit for delivery an activity that addresses the same actor
|
||||
(ie an actor with the same id) twice.
|
||||
|
||||
The final recipient list is deduplicated before delivery.
|
||||
`
|
||||
t.Log(desc)
|
||||
|
||||
to := pub.PersonNew("bob")
|
||||
o := pub.ObjectNew(pub.ArticleType)
|
||||
cc := pub.PersonNew("alice")
|
||||
|
||||
o.ID = "something"
|
||||
c := pub.CreateNew("create", o)
|
||||
c.To.Append(to)
|
||||
c.CC.Append(cc)
|
||||
c.BCC.Append(cc)
|
||||
|
||||
c.Recipients()
|
||||
|
||||
checkDedup := func(list pub.ItemCollection, recIds *[]pub.ID) error {
|
||||
for _, rec := range list {
|
||||
for _, id := range *recIds {
|
||||
if rec.GetID() == id {
|
||||
return fmt.Errorf("%T[%s] already stored in recipients list, Deduplication faild", rec, id)
|
||||
}
|
||||
}
|
||||
*recIds = append(*recIds, rec.GetID())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
recIds := make([]pub.ID, 0)
|
||||
err = checkDedup(c.To, &recIds)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkDedup(c.Bto, &recIds)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkDedup(c.CC, &recIds)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkDedup(c.BCC, &recIds)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// S2S Server: Do-not-deliver considerations
|
||||
// Server does not deliver to recipients which are the same as the actor of the
|
||||
// Activity being notified about
|
||||
func TestDoNotDeliverToActor(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Do-not-deliver considerations
|
||||
|
||||
Server does not deliver to recipients which are the same as the actor of the
|
||||
Activity being notified about
|
||||
`
|
||||
t.Log(desc)
|
||||
|
||||
p := pub.PersonNew("main actor")
|
||||
|
||||
to := pub.PersonNew("bob")
|
||||
o := pub.ObjectNew(pub.ArticleType)
|
||||
cc := pub.PersonNew("alice")
|
||||
|
||||
o.ID = "something"
|
||||
c := pub.CreateNew("create", o)
|
||||
c.Actor = *p
|
||||
|
||||
c.To.Append(p)
|
||||
c.To.Append(to)
|
||||
c.CC.Append(cc)
|
||||
c.CC.Append(p)
|
||||
c.BCC.Append(cc)
|
||||
c.BCC.Append(p)
|
||||
|
||||
c.Recipients()
|
||||
|
||||
checkActor := func(list pub.ItemCollection, actor pub.Item) error {
|
||||
for _, rec := range list {
|
||||
if rec.GetID() == actor.GetID() {
|
||||
return fmt.Errorf("%T[%s] Actor of activity should not be in the recipients list", rec, actor.GetID())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
err = checkActor(c.To, c.Actor)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkActor(c.Bto, c.Actor)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkActor(c.CC, c.Actor)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkActor(c.BCC, c.Actor)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// S2S Server: Do-not-deliver considerations
|
||||
// Server does not deliver Block activities to their object.
|
||||
func TestDoNotDeliverBlockToObject(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Do-not-deliver considerations
|
||||
|
||||
Server does not deliver Block activities to their object.
|
||||
`
|
||||
t.Log(desc)
|
||||
|
||||
p := pub.PersonNew("blocked")
|
||||
|
||||
bob := pub.PersonNew("bob")
|
||||
jane := pub.PersonNew("jane doe")
|
||||
|
||||
b := pub.BlockNew("block actor", p)
|
||||
b.Actor = *bob
|
||||
|
||||
b.To.Append(jane)
|
||||
b.To.Append(p)
|
||||
b.To.Append(bob)
|
||||
|
||||
b.Recipients()
|
||||
|
||||
checkActor := func(list pub.ItemCollection, ob pub.Item) error {
|
||||
for _, rec := range list {
|
||||
if rec.GetID() == ob.GetID() {
|
||||
return fmt.Errorf("%T[%s] of activity should not be in the recipients list", rec, ob.GetID())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
err = checkActor(b.To, b.Object)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = checkActor(b.To, b.Actor)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// S2S Sever: Support for sharedInbox
|
||||
// Delivers to sharedInbox endpoints to reduce the number of receiving actors delivered
|
||||
// to by identifying all followers which share the same sharedInbox who would otherwise be
|
||||
// individual recipients and instead deliver objects to said sharedInbox.
|
||||
func TestSharedInboxIdentifySharedInbox(t *testing.T) {
|
||||
desc := `
|
||||
S2S Sever: Support for sharedInbox
|
||||
|
||||
Delivers to sharedInbox endpoints to reduce the number of receiving actors delivered
|
||||
to by identifying all followers which share the same sharedInbox who would otherwise be
|
||||
individual recipients and instead deliver objects to said sharedInbox.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Sever: Support for sharedInbox
|
||||
// Deliver to actor inboxes and collections otherwise addressed which do not have a sharedInbox.
|
||||
func TestSharedInboxActorsWOSharedInbox(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Do-not-deliver considerations
|
||||
|
||||
Server does not deliver Block activities to their object.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Deduplicating received activities
|
||||
// Server deduplicates activities received in inbox by comparing activity ids
|
||||
func TestInboxDeduplication(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Deduplicating received activities
|
||||
|
||||
Server deduplicates activities received in inbox by comparing activity ids
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Special forwarding mechanism
|
||||
// ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
// Forwards incoming activities to the values of to, bto, cc, bcc, audience if and only if criteria are met.
|
||||
func TestForwardingMechanismsToRecipients(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Special forwarding mechanism
|
||||
ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
|
||||
Forwards incoming activities to the values of to, bto, cc, bcc, audience if and only if criteria are met.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Special forwarding mechanism
|
||||
// ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
// Recurse through to, bto, cc, bcc, audience object values to determine whether/where
|
||||
// to forward according to criteria in 7.1.2
|
||||
func TestForwardingMechanismsRecurseRecipients(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Special forwarding mechanism
|
||||
ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
|
||||
Recurse through to, bto, cc, bcc, audience object values to determine whether/where
|
||||
to forward according to criteria in 7.1.2
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Special forwarding mechanism
|
||||
// ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
// Limits depth of this recursion.
|
||||
func TestForwardingMechanismsLimitsRecursion(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Special forwarding mechanism
|
||||
ActivityPub contains a special mechanism for forwarding replies to avoid "ghost replies".
|
||||
|
||||
Limits depth of this recursion.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Verification of content authorship
|
||||
// Before accepting activities delivered to an actor's inbox some sort of verification
|
||||
// should be performed. (For example, if the delivering actor has a public key on their profile,
|
||||
// the request delivering the activity may be signed with HTTP Signatures.)
|
||||
// Don't trust content received from a server other than the content's origin without some form of verification.
|
||||
func TestVerification(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Verification of content authorship
|
||||
Before accepting activities delivered to an actor's inbox some sort of verification
|
||||
should be performed. (For example, if the delivering actor has a public key on their profile,
|
||||
the request delivering the activity may be signed with HTTP Signatures.)
|
||||
|
||||
Don't trust content received from a server other than the content's origin without some form of verification.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Update activity
|
||||
// On receiving an Update activity to an actor's inbox, the server:
|
||||
// Takes care to be sure that the Update is authorized to modify its object
|
||||
func TestUpdateIsAuthorized(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Update activity
|
||||
On receiving an Update activity to an actor's inbox, the server:
|
||||
|
||||
Takes care to be sure that the Update is authorized to modify its object
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Update activity
|
||||
// On receiving an Update activity to an actor's inbox, the server:
|
||||
// Completely replaces its copy of the activity with the newly received value
|
||||
func TestUpdateReplacesActivity(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Update activity
|
||||
On receiving an Update activity to an actor's inbox, the server:
|
||||
|
||||
Completely replaces its copy of the activity with the newly received value
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Delete activity
|
||||
// Delete removes object's representation, assuming object is owned by sending actor/server
|
||||
func TestDeleteRemoves(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Delete activity
|
||||
|
||||
Delete removes object's representation, assuming object is owned by sending actor/server
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Delete activity
|
||||
// Replaces deleted object with a Tombstone object (optional)
|
||||
func TestDeleteReplacesWithTombstone(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Delete activity
|
||||
|
||||
Replaces deleted object with a Tombstone object (optional)
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Following, and handling accept/reject of follows
|
||||
// Follow should add the activity's actor to the receiving actor's Followers Collection.
|
||||
func TestFollowAddsToFollowers(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Following, and handling accept/reject of follows
|
||||
|
||||
Follow should add the activity's actor to the receiving actor's Followers Collection.
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Following, and handling accept/reject of follows
|
||||
// Generates either an Accept or Reject activity with Follow as object and deliver to actor of the Follow
|
||||
func TestGeneratesAcceptOrReject(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Following, and handling accept/reject of follows
|
||||
|
||||
Generates either an Accept or Reject activity with Follow as object and deliver to actor of the Follow
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Following, and handling accept/reject of follows
|
||||
// If receiving an Accept in reply to a Follow activity, adds actor to receiver's Following Collection
|
||||
func TestAddsFollowerIfAccept(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Following, and handling accept/reject of follows
|
||||
|
||||
If receiving an Accept in reply to a Follow activity, adds actor to receiver's Following Collection
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
// S2S Server: Following, and handling accept/reject of follows
|
||||
// If receiving a Reject in reply to a Follow activity, does not add actor to receiver's Following Collection
|
||||
func TestDoesntAddFollowerIfReject(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Following, and handling accept/reject of follows
|
||||
|
||||
If receiving a Reject in reply to a Follow activity, does not add actor to receiver's Following Collection
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Create makes record of the object existing
|
||||
func TestCreateMakesRecord(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Create makes record of the object existing
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Add should add the activity's object to the Collection specified in the target property,
|
||||
// unless not allowed per requirements
|
||||
func TestAddObjectToTarget(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Add should add the activity's object to the Collection specified in the target property,
|
||||
unless not allowed per requirements
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Remove should remove the object from the Collection specified in the target property,
|
||||
// unless not allowed per requirements
|
||||
func TestRemoveObjectFromTarget(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Remove should remove the object from the Collection specified in the target property,
|
||||
unless not allowed per requirements
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Like increments the object's count of likes by adding the received activity to the likes
|
||||
// collection if this collection is present
|
||||
func TestLikeIncrementsLikes(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Like increments the object's count of likes by adding the received activity to the likes
|
||||
collection if this collection is present
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Announce increments object's count of shares by adding the received activity to the
|
||||
// 'shares' collection if this collection is present
|
||||
func TestAnnounceIncrementsShares(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Announce increments object's count of shares by adding the received activity to the
|
||||
'shares' collection if this collection is present
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
||||
|
||||
//S2S Server: Activity acceptance side-effects
|
||||
// Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
//
|
||||
// Undo performs Undo of object in federated context
|
||||
func TestUndoPerformsUndoOnObject(t *testing.T) {
|
||||
desc := `
|
||||
S2S Server: Activity acceptance side-effects
|
||||
Test accepting the following activities to an actor's inbox and observe the side effects:
|
||||
|
||||
Undo performs Undo of object in federated context
|
||||
`
|
||||
t.Skip(desc)
|
||||
}
|
580
tests/unmarshal_test.go
Normal file
580
tests/unmarshal_test.go
Normal file
|
@ -0,0 +1,580 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
pub "github.com/go-ap/activitypub"
|
||||
|
||||
j "github.com/go-ap/jsonld"
|
||||
)
|
||||
|
||||
const dir = "./mocks"
|
||||
|
||||
var stopOnFailure = false
|
||||
|
||||
type testPair struct {
|
||||
expected bool
|
||||
blank interface{}
|
||||
result interface{}
|
||||
}
|
||||
|
||||
type testMaps map[string]testPair
|
||||
|
||||
type visit struct {
|
||||
a1 unsafe.Pointer
|
||||
a2 unsafe.Pointer
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
type canErrorFunc func(format string, args ...interface{})
|
||||
|
||||
// See reflect.DeepEqual
|
||||
func assertDeepEquals(t canErrorFunc, x, y interface{}) bool {
|
||||
if x == nil || y == nil {
|
||||
return x == y
|
||||
}
|
||||
v1 := reflect.ValueOf(x)
|
||||
v2 := reflect.ValueOf(y)
|
||||
if v1.Type() != v2.Type() {
|
||||
t("%T != %T", x, y)
|
||||
return false
|
||||
}
|
||||
return deepValueEqual(t, v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
||||
// See reflect.deepValueEqual
|
||||
func deepValueEqual(t canErrorFunc, v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
t("types differ %s != %s", v1.Type().Name(), v2.Type().Name())
|
||||
return false
|
||||
}
|
||||
|
||||
hard := func(v1, v2 reflect.Value) bool {
|
||||
switch v1.Kind() {
|
||||
case reflect.Ptr:
|
||||
return false
|
||||
case reflect.Map, reflect.Slice, reflect.Interface:
|
||||
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
|
||||
return !v1.IsNil() && !v2.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if hard(v1, v2) {
|
||||
var addr1, addr2 unsafe.Pointer
|
||||
if v1.CanAddr() {
|
||||
addr1 = unsafe.Pointer(v1.UnsafeAddr())
|
||||
} else {
|
||||
addr1 = unsafe.Pointer(v1.Pointer())
|
||||
}
|
||||
if v2.CanAddr() {
|
||||
addr2 = unsafe.Pointer(v2.UnsafeAddr())
|
||||
} else {
|
||||
addr2 = unsafe.Pointer(v2.Pointer())
|
||||
}
|
||||
if uintptr(addr1) > uintptr(addr2) {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
// Assumes non-moving garbage collector.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
// Short circuit if references are already seen.
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !deepValueEqual(t, v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
t("Arrays not equal at index %d %s %s", i, v1.Index(i), v2.Index(i))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
t("One of the slices is not nil %s[%d] vs %s[%d]", v1.Type().Name(), v1.Len(), v2.Type().Name(), v2.Len())
|
||||
return false
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
t("Slices lengths are different %s[%d] vs %s[%d]", v1.Type().Name(), v1.Len(), v2.Type().Name(), v2.Len())
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !deepValueEqual(t, v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
t("Slices elements at pos %d are not equal %#v vs %#v", i, v1.Index(i), v2.Index(i))
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() || v2.IsNil() {
|
||||
if v1.IsNil() == v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
var isNil1, isNil2 string
|
||||
if v1.IsNil() {
|
||||
isNil1 = "is"
|
||||
} else {
|
||||
isNil1 = "is not"
|
||||
}
|
||||
if v2.IsNil() {
|
||||
isNil2 = "is"
|
||||
} else {
|
||||
isNil2 = "is not"
|
||||
}
|
||||
t("Interface '%s' %s nil and '%s' %s nil", v1.Type().Name(), isNil1, v2.Type().Name(), isNil2)
|
||||
return false
|
||||
}
|
||||
return deepValueEqual(t, v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
return deepValueEqual(t, v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
var (
|
||||
f1 = v1.Field(i)
|
||||
f2 = v2.Field(i)
|
||||
n1 = v1.Type().Field(i).Name
|
||||
n2 = v2.Type().Field(i).Name
|
||||
t1 = f1.Type().Name()
|
||||
t2 = f2.Type().Name()
|
||||
)
|
||||
if !deepValueEqual(t, v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
t("Struct fields at pos %d %s[%s] and %s[%s] are not deeply equal", i, n1, t1, n2, t2)
|
||||
if f1.CanInterface() && f2.CanInterface() {
|
||||
t(" Values: %#v - %#v", v1.Field(i).Interface(), v2.Field(i).Interface())
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
t("Maps are not nil", v1.Type().Name(), v2.Type().Name())
|
||||
return false
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
t("Maps don't have the same length %d vs %d", v1.Len(), v2.Len())
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
val1 := v1.MapIndex(k)
|
||||
val2 := v2.MapIndex(k)
|
||||
if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(t, v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
t("Maps values at index %s are not equal", k.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
case reflect.String:
|
||||
return v1.String() == v2.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v1.Int() == v2.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return v1.Uint() == v2.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v1.Float() == v2.Float()
|
||||
case reflect.Bool:
|
||||
return v1.Bool() == v2.Bool()
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return v1.Complex() == v2.Complex()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var zLoc, _ = time.LoadLocation("UTC")
|
||||
|
||||
var allTests = testMaps{
|
||||
//"empty": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{},
|
||||
//},
|
||||
//"link_simple": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Link{},
|
||||
// result: &pub.Link{
|
||||
// Type: pub.LinkType,
|
||||
// Href: pub.IRI("http://example.org/abc"),
|
||||
// HrefLang: pub.LangRef("en"),
|
||||
// MediaType: pub.MimeType("text/html"),
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("An example link"),
|
||||
// }},
|
||||
// },
|
||||
//},
|
||||
//"object_with_url": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// URL: pub.IRI("http://littr.git/api/accounts/system"),
|
||||
// },
|
||||
//},
|
||||
//"object_with_url_collection": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// URL: pub.ItemCollection{
|
||||
// pub.IRI("http://littr.git/api/accounts/system"),
|
||||
// pub.IRI("http://littr.git/~system"),
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"object_simple": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// Type: pub.ObjectType,
|
||||
// ID: pub.ID("http://www.test.example/object/1"),
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("A Simple, non-specific object"),
|
||||
// }},
|
||||
// },
|
||||
//},
|
||||
//"object_no_type": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// ID: pub.ID("http://www.test.example/object/1"),
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("A Simple, non-specific object without a type"),
|
||||
// }},
|
||||
// },
|
||||
//},
|
||||
//"object_with_tags": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// Type: pub.ObjectType,
|
||||
// ID: pub.ID("http://www.test.example/object/1"),
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("A Simple, non-specific object"),
|
||||
// }},
|
||||
// Tag: pub.ItemCollection{
|
||||
// &pub.Mention{
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("#my_tag"),
|
||||
// }},
|
||||
// Type: pub.MentionType,
|
||||
// ID: pub.ID("http://example.com/tag/my_tag"),
|
||||
// },
|
||||
// &pub.Mention{
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("@ana"),
|
||||
// }},
|
||||
// Type: pub.MentionType,
|
||||
// ID: pub.ID("http://example.com/users/ana"),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"object_with_replies": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// Type: pub.ObjectType,
|
||||
// ID: pub.ID("http://www.test.example/object/1"),
|
||||
// Replies: &pub.Collection{
|
||||
// ID: pub.ID("http://www.test.example/object/1/replies"),
|
||||
// Type: pub.CollectionType,
|
||||
// TotalItems: 1,
|
||||
// Items: pub.ItemCollection{
|
||||
// &pub.Object{
|
||||
// ID: pub.ID("http://www.test.example/object/1/replies/2"),
|
||||
// Type: pub.ArticleType,
|
||||
// Name: pub.NaturalLanguageValues{{
|
||||
// pub.NilLangRef, pub.Content("Example title"),
|
||||
// }},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"activity_simple": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Activity{
|
||||
// Actor: &pub.Person{},
|
||||
// },
|
||||
// result: &pub.Activity{
|
||||
// Type: pub.ActivityType,
|
||||
// Summary: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("Sally did something to a note")}},
|
||||
// Actor: &pub.Person{
|
||||
// Type: pub.PersonType,
|
||||
// Name: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("Sally")}},
|
||||
// },
|
||||
// Object: &pub.Object{
|
||||
// Type: pub.NoteType,
|
||||
// Name: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("A Note")}},
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"person_with_outbox": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Person{},
|
||||
// result: &pub.Person{
|
||||
// ID: pub.ID("http://example.com/accounts/ana"),
|
||||
// Type: pub.PersonType,
|
||||
// Name: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("ana")}},
|
||||
// PreferredUsername: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("Ana")}},
|
||||
// URL: pub.IRI("http://example.com/accounts/ana"),
|
||||
// Outbox: &pub.OrderedCollection{
|
||||
// ID: "http://example.com/accounts/ana/outbox",
|
||||
// Type: pub.OrderedCollectionType,
|
||||
// URL: pub.IRI("http://example.com/outbox"),
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"ordered_collection": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.OrderedCollection{},
|
||||
// result: &pub.OrderedCollection{
|
||||
// ID: pub.ID("http://example.com/outbox"),
|
||||
// Type: pub.OrderedCollectionType,
|
||||
// URL: pub.IRI("http://example.com/outbox"),
|
||||
// TotalItems: 1,
|
||||
// OrderedItems: pub.ItemCollection{
|
||||
// &pub.Object{
|
||||
// ID: pub.ID("http://example.com/outbox/53c6fb47"),
|
||||
// Type: pub.ArticleType,
|
||||
// Name: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("Example title")}},
|
||||
// Content: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("Example content!")}},
|
||||
// URL: pub.IRI("http://example.com/53c6fb47"),
|
||||
// MediaType: pub.MimeType("text/markdown"),
|
||||
// Published: time.Date(2018, time.July, 5, 16, 46, 44, 0, zLoc),
|
||||
// Generator: pub.IRI("http://example.com"),
|
||||
// AttributedTo: pub.IRI("http://example.com/accounts/alice"),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
"ordered_collection_page": testPair{
|
||||
expected: true,
|
||||
blank: &pub.OrderedCollectionPage{},
|
||||
result: &pub.OrderedCollectionPage{
|
||||
PartOf: pub.IRI("http://example.com/outbox"),
|
||||
Next: pub.IRI("http://example.com/outbox?page=3"),
|
||||
Prev: pub.IRI("http://example.com/outbox?page=1"),
|
||||
ID: pub.ID("http://example.com/outbox?page=2"),
|
||||
Type: pub.OrderedCollectionPageType,
|
||||
URL: pub.IRI("http://example.com/outbox?page=2"),
|
||||
Current: pub.IRI("http://example.com/outbox?page=2"),
|
||||
TotalItems: 1,
|
||||
StartIndex: 100,
|
||||
OrderedItems: pub.ItemCollection{
|
||||
&pub.Object{
|
||||
ID: pub.ID("http://example.com/outbox/53c6fb47"),
|
||||
Type: pub.ArticleType,
|
||||
Name: pub.NaturalLanguageValues{{Ref: pub.NilLangRef, Value: pub.Content("Example title")}},
|
||||
Content: pub.NaturalLanguageValues{{Ref: pub.NilLangRef, Value: pub.Content("Example content!")}},
|
||||
URL: pub.IRI("http://example.com/53c6fb47"),
|
||||
MediaType: pub.MimeType("text/markdown"),
|
||||
Published: time.Date(2018, time.July, 5, 16, 46, 44, 0, zLoc),
|
||||
Generator: pub.IRI("http://example.com"),
|
||||
AttributedTo: pub.IRI("http://example.com/accounts/alice"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//"natural_language_values": {
|
||||
// expected: true,
|
||||
// blank: &pub.NaturalLanguageValues{},
|
||||
// result: &pub.NaturalLanguageValues{
|
||||
// {
|
||||
// pub.NilLangRef, pub.Content([]byte{'\n','\t', '\t', '\n'}),
|
||||
// },
|
||||
// {pub.LangRef("en"), pub.Content("Ana got apples ⓐ")},
|
||||
// {pub.LangRef("fr"), pub.Content("Aná a des pommes ⒜")},
|
||||
// {pub.LangRef("ro"), pub.Content("Ana are mere")},
|
||||
// },
|
||||
//},
|
||||
//"activity_create_simple": {
|
||||
// expected: true,
|
||||
// blank: &pub.Create{},
|
||||
// result: &pub.Create{
|
||||
// Type: pub.CreateType,
|
||||
// Actor: pub.IRI("https://littr.git/api/accounts/anonymous"),
|
||||
// Object: &pub.Object{
|
||||
// Type: pub.NoteType,
|
||||
// AttributedTo: pub.IRI("https://littr.git/api/accounts/anonymous"),
|
||||
// InReplyTo: pub.IRI("https://littr.git/api/accounts/system/outbox/7ca154ff"),
|
||||
// Content: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("<p>Hello world</p>")}},
|
||||
// To: pub.ItemCollection{pub.IRI("https://www.w3.org/ns/activitystreams#Public")},
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"activity_create_multiple_objects": {
|
||||
// expected: true,
|
||||
// blank: &pub.Create{},
|
||||
// result: &pub.Create{
|
||||
// Type: pub.CreateType,
|
||||
// Actor: pub.IRI("https://littr.git/api/accounts/anonymous"),
|
||||
// Object: pub.ItemCollection{
|
||||
// &pub.Object{
|
||||
// Type: pub.NoteType,
|
||||
// AttributedTo: pub.IRI("https://littr.git/api/accounts/anonymous"),
|
||||
// InReplyTo: pub.IRI("https://littr.git/api/accounts/system/outbox/7ca154ff"),
|
||||
// Content: pub.NaturalLanguageValues{{pub.NilLangRef, pub.Content("<p>Hello world</p>")}},
|
||||
// To: pub.ItemCollection{pub.IRI("https://www.w3.org/ns/activitystreams#Public")},
|
||||
// },
|
||||
// &pub.Article{
|
||||
// Type: pub.ArticleType,
|
||||
// ID: pub.ID("http://www.test.example/article/1"),
|
||||
// Name: pub.NaturalLanguageValues{
|
||||
// {
|
||||
// pub.NilLangRef,
|
||||
// pub.Content("This someday will grow up to be an article"),
|
||||
// },
|
||||
// },
|
||||
// InReplyTo: pub.ItemCollection{
|
||||
// pub.IRI("http://www.test.example/object/1"),
|
||||
// pub.IRI("http://www.test.example/object/778"),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"object_with_audience": testPair{
|
||||
// expected: true,
|
||||
// blank: &pub.Object{},
|
||||
// result: &pub.Object{
|
||||
// Type: pub.ObjectType,
|
||||
// ID: pub.ID("http://www.test.example/object/1"),
|
||||
// To: pub.ItemCollection{
|
||||
// pub.IRI("https://www.w3.org/ns/activitystreams#Public"),
|
||||
// },
|
||||
// Bto: pub.ItemCollection{
|
||||
// pub.IRI("http://example.com/sharedInbox"),
|
||||
// },
|
||||
// CC: pub.ItemCollection{
|
||||
// pub.IRI("https://example.com/actors/ana"),
|
||||
// pub.IRI("https://example.com/actors/bob"),
|
||||
// },
|
||||
// BCC: pub.ItemCollection{
|
||||
// pub.IRI("https://darkside.cookie/actors/darthvader"),
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
//"article_with_multiple_inreplyto": {
|
||||
// expected: true,
|
||||
// blank: &pub.Article{},
|
||||
// result: &pub.Article{
|
||||
// Type: pub.ArticleType,
|
||||
// ID: pub.ID("http://www.test.example/article/1"),
|
||||
// Name: pub.NaturalLanguageValues{
|
||||
// {
|
||||
// pub.NilLangRef,
|
||||
// pub.Content("This someday will grow up to be an article"),
|
||||
// },
|
||||
// },
|
||||
// InReplyTo: pub.ItemCollection{
|
||||
// pub.IRI("http://www.test.example/object/1"),
|
||||
// pub.IRI("http://www.test.example/object/778"),
|
||||
// },
|
||||
// },
|
||||
//},
|
||||
}
|
||||
|
||||
func getFileContents(path string) ([]byte, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := make([]byte, st.Size())
|
||||
_, _ = io.ReadFull(f, data)
|
||||
data = bytes.Trim(data, "\x00")
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
var err error
|
||||
|
||||
f := t.Errorf
|
||||
if len(allTests) == 0 {
|
||||
t.Skip("No tests found")
|
||||
}
|
||||
|
||||
for k, pair := range allTests {
|
||||
path := filepath.Join(dir, fmt.Sprintf("%s.json", k))
|
||||
t.Run(path, func(t *testing.T) {
|
||||
var data []byte
|
||||
data, err = getFileContents(path)
|
||||
if err != nil {
|
||||
f("Error: %s for %s", err, path)
|
||||
return
|
||||
}
|
||||
object := pair.blank
|
||||
|
||||
err = j.Unmarshal(data, object)
|
||||
if err != nil {
|
||||
f("Error: %s for %s", err, data)
|
||||
return
|
||||
}
|
||||
expLbl := ""
|
||||
if !pair.expected {
|
||||
expLbl = "not be "
|
||||
}
|
||||
status := assertDeepEquals(f, object, pair.result)
|
||||
if pair.expected != status {
|
||||
if stopOnFailure {
|
||||
f = t.Fatalf
|
||||
}
|
||||
|
||||
f("Mock: %s: %s\n%#v\n should %sequal to expected\n%#v", k, path, object, expLbl, pair.result)
|
||||
return
|
||||
}
|
||||
if !status {
|
||||
oj, err := j.Marshal(object)
|
||||
if err != nil {
|
||||
f(err.Error())
|
||||
}
|
||||
tj, err := j.Marshal(pair.result)
|
||||
if err != nil {
|
||||
f(err.Error())
|
||||
}
|
||||
f("Mock: %s: %s\n%s\n should %sequal to expected\n%s", k, path, oj, expLbl, tj)
|
||||
}
|
||||
//if err == nil {
|
||||
// fmt.Printf(" --- %s: %s\n %s\n", "PASS", k, path)
|
||||
//}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue