diff --git a/asvocab_base_types.go b/asvocab_base_types.go index 61bc971..3b756e4 100644 --- a/asvocab_base_types.go +++ b/asvocab_base_types.go @@ -55,7 +55,7 @@ type EndpointsOrString struct { // Object represents the base ActivityStreams Object and all of its properties // Most of the other types extend Object type Object struct { - ASContext *ObjectOrLinkOrString `json:"@context,omitempty"` + ASContext any `json:"@context,omitempty"` ASLanguage string `json:"@language,omitempty"` Schema string `json:"schema,omitempty"` ID string `json:"id,omitempty"` @@ -78,7 +78,6 @@ type Object struct { StartTime *time.Time `json:"startTime,omitempty"` EndTime *time.Time `json:"endTime,omitempty"` Generator *ObjectOrLinkOrString `json:"generator,omitempty"` - Featured *ObjectOrLinkOrString `json:"featured,omitempty"` Likes *ObjectOrLinkOrString `json:"likes,omitempty"` Shares *ObjectOrLinkOrString `json:"shares,omitempty"` Icon *ObjectOrLinkOrString `json:"icon,omitempty"` @@ -102,7 +101,7 @@ type Object struct { // Link represents the base ActivityStreams Link and all of its properties // Other Link types extend it type Link struct { - ASContext *ObjectOrLinkOrString `json:"@context,omitempty"` + ASContext any `json:"@context,omitempty"` ASLanguage string `json:"@language,omitempty"` Type string `json:"type"` Href string `json:"href,omitempty"` @@ -149,9 +148,15 @@ type Actor struct { Followers *StringWithOrderedCollectionPage `json:"followers,omitempty"` Following *StringWithOrderedCollectionPage `json:"following,omitempty"` Liked *StringWithOrderedCollectionPage `json:"liked,omitempty"` + Featured *StringWithOrderedCollection `json:"featured,omitempty"` + FeaturedTags *StringWithCollection `json:"featuredTags,omitempty"` Streams *ObjectOrLinkOrString `json:"streams,omitempty"` PreferredUsername string `json:"preferredUsername,omitempty"` - ManuallyApprovesFollowers bool `json:"manuallyApprovesFollowers,omitempty"` + ManuallyApprovesFollowers *bool `json:"manuallyApprovesFollowers,omitempty"` + Discoverable *bool `json:"discoverable,omitempty"` + Indexable *bool `json:"indexable,omitempty"` + Memorial *bool `json:"memorial,omitempty"` + Devices *StringWithCollection `json:"devices,omitempty"` EndpointsOrURI *EndpointsOrString `json:"endpoints,omitempty"` } diff --git a/asvocab_serialization.go b/asvocab_serialization.go index e0c5226..8d6f662 100644 --- a/asvocab_serialization.go +++ b/asvocab_serialization.go @@ -345,6 +345,11 @@ func (ols *ObjectOrLinkOrString) MarshalJSON() ([]byte, error) { } return uri, nil } + + if len(ols.Target) == 0 || len(ols.URL) == 0 { + return bytes.NewBufferString("[]").Bytes(), nil + } + return []byte{}, errors.New("unrecognised content, cannot Marshal ObjectOrLinkOrString, use nil for empty value") } diff --git a/asvocab_serialization_marshal_test.go b/asvocab_serialization_marshal_test.go index a6abc12..3c1db15 100644 --- a/asvocab_serialization_marshal_test.go +++ b/asvocab_serialization_marshal_test.go @@ -2,8 +2,10 @@ package astreams import ( "encoding/json" - "strings" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMarshalJSON_OrderedCollectionPage(t *testing.T) { @@ -36,16 +38,11 @@ func TestMarshalJSON_OrderedCollectionPage(t *testing.T) { // Prepare the object to marshal first orderedCollectionPage := OrderedCollectionPage{} err := json.Unmarshal([]byte(want[idx]), &orderedCollectionPage) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) got, err := json.MarshalIndent(&orderedCollectionPage, "", " ") - if err != nil { - t.Fatal(err) - } - if string(got) != strings.TrimSpace(want[idx]) { - t.Fatalf("marshaled OrderedCollectionPage JSON not as expected, wanted '%s', got '%s'", strings.TrimSpace(want[idx]), string(got)) - } + require.NoError(t, err) + + assert.JSONEq(t, want[idx], string(got)) } } @@ -124,121 +121,154 @@ func TestMarshalJSON_Collection(t *testing.T) { // Prepare the object to marshal first collection := Collection{} err := json.Unmarshal([]byte(want[idx]), &collection) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) got, err := json.MarshalIndent(&collection, "", " ") - if err != nil { - t.Fatal(err) - } - if string(got) != strings.TrimSpace(want[idx]) { - t.Fatalf("marshaled Collection JSON not as expected, wanted '%s', got '%s'", strings.TrimSpace(want[idx]), string(got)) - } + require.NoError(t, err) + + assert.JSONEq(t, want[idx], string(got)) } } func TestMarshalJSON_Actor(t *testing.T) { want := []string{ - ` -{ + `{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { - "schema": "http://schema.org#", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, "alsoKnownAs": { - "@type": "@id", - "@id": "as:alsoKnownAs" + "@id": "as:alsoKnownAs", + "@type": "@id" }, "movedTo": { + "@id": "as:movedTo", + "@type": "@id" + }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "discoverable": "toot:discoverable", + "Device": "toot:Device", + "Ed25519Signature": "toot:Ed25519Signature", + "Ed25519Key": "toot:Ed25519Key", + "Curve25519Key": "toot:Curve25519Key", + "EncryptedMessage": "toot:EncryptedMessage", + "publicKeyBase64": "toot:publicKeyBase64", + "deviceId": "toot:deviceId", + "claim": { + "@type": "@id", + "@id": "toot:claim" + }, + "fingerprintKey": { + "@type": "@id", + "@id": "toot:fingerprintKey" + }, + "identityKey": { "@type": "@id", - "@id": "as:movedTo" + "@id": "toot:identityKey" + }, + "devices": { + "@type": "@id", + "@id": "toot:devices" + }, + "messageFranking": "toot:messageFranking", + "messageType": "toot:messageType", + "cipherText": "toot:cipherText", + "suspended": "toot:suspended", + "memorial": "toot:memorial", + "indexable": "toot:indexable", + "Hashtag": "as:Hashtag", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" } } ], "id": "https://social.matej-lach.me/users/MatejLach", "type": "Person", - "summary": "\u003cp\u003eFree software enthusiast, \u003ca href=\"https://social.matej-lach.me/tags/golang\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003egolang\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"https://social.matej-lach.me/tags/rustlang\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003erustlang\u003c/span\u003e\u003c/a\u003e, \u003ca href=\"https://social.matej-lach.me/tags/jvm\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ejvm\u003c/span\u003e\u003c/a\u003e \u0026amp; \u003ca href=\"https://social.matej-lach.me/tags/swiftlang\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eswiftlang\u003c/span\u003e\u003c/a\u003e . Working on a question/answer \u003ca href=\"https://social.matej-lach.me/tags/ActivityPub\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eActivityPub\u003c/span\u003e\u003c/a\u003e server. \u003ca href=\"https://social.matej-lach.me/tags/systemd\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003esystemd\u003c/span\u003e\u003c/a\u003e aficionado :-)\u003c/p\u003e", - "name": "Matej Ľach ✅", - "attachment": { - "type": "PropertyValue", - "name": "Blog", - "value": "\u003ca href=\"https://blog.matej-lach.me\" rel=\"me nofollow noopener\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003eblog.matej-lach.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" - }, + "following": "https://social.matej-lach.me/users/MatejLach/following", + "followers": "https://social.matej-lach.me/users/MatejLach/followers", + "inbox": "https://social.matej-lach.me/users/MatejLach/inbox", + "outbox": "https://social.matej-lach.me/users/MatejLach/outbox", "featured": "https://social.matej-lach.me/users/MatejLach/collections/featured", - "icon": { - "type": "Image", - "url": "https://social.matej-lach.me/system/accounts/avatars/000/000/001/original/6e9242b03795bf80.png?1509060490", - "mediaType": "image/png" - }, - "image": { - "type": "Image", - "url": "https://social.matej-lach.me/system/accounts/headers/000/000/001/original/f18240c45b0ac254.png?1576335545", - "mediaType": "image/png" + "featuredTags": "https://social.matej-lach.me/users/MatejLach/collections/tags", + "preferredUsername": "MatejLach", + "name": "Matej Ľach ✅", + "summary": "

Free software enthusiast, #golang, #rustlang, #swiftlang . Working on a question/answer #ActivityPub server. #systemd aficionado :-)

", + "url": "https://social.matej-lach.me/@MatejLach", + "manuallyApprovesFollowers": false, + "discoverable": true, + "indexable": false, + "published": "2017-10-26T00:00:00Z", + "memorial": false, + "devices": "https://social.matej-lach.me/users/MatejLach/collections/devices", + "publicKey": { + "id": "https://social.matej-lach.me/users/MatejLach#main-key", + "owner": "https://social.matej-lach.me/users/MatejLach", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm9ir9e6zclOuWFN+mrtV\nqjz+qNYdgYYA7TGLWf4heNjdQRP0wOTjSY5mXST95aKY8h30LSyPAI01/GLrPsme\nm+uSgr59VX3dyvDHYiOSSuMl8lkFMGthxWlKXcUb7vFcJnHRr4q5TUZ+J4wjEksw\nWmqK5me2Lnt+wVQnWXplfnknVJaZvCPEfRWVVu53lgSfTkF+rO4Bl6osw2TrIj3T\n8MoOpnGKXSTGuL86cAQAkxbJcqkFeM/ksojVBqVpGn+xdQuOf62j6mFzZl4B9wfo\nKah+O7zbgvJEHhSmvSZlo8b9YJre0YbAJBCcQnAyj3m2oSEwIKz20jjTapsiAJFS\nTwIDAQAB\n-----END PUBLIC KEY-----\n" }, "tag": [ { "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/golang", + "href": "https://social.matej-lach.me/tags/golang", "name": "#golang" }, { "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/activitypub", + "href": "https://social.matej-lach.me/tags/activitypub", "name": "#activitypub" }, { "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/rustlang", + "href": "https://social.matej-lach.me/tags/rustlang", "name": "#rustlang" }, { "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/swiftlang", + "href": "https://social.matej-lach.me/tags/swiftlang", "name": "#swiftlang" }, { "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/jvm", - "name": "#jvm" - }, - { - "type": "Hashtag", - "href": "https://social.matej-lach.me/explore/systemd", + "href": "https://social.matej-lach.me/tags/systemd", "name": "#systemd" } ], - "url": "https://social.matej-lach.me/@MatejLach", - "publicKey": { - "id": "https://social.matej-lach.me/users/MatejLach#main-key", - "owner": "https://social.matej-lach.me/users/MatejLach", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm9ir9e6zclOuWFN+mrtV\nqjz+qNYdgYYA7TGLWf4heNjdQRP0wOTjSY5mXST95aKY8h30LSyPAI01/GLrPsme\nm+uSgr59VX3dyvDHYiOSSuMl8lkFMGthxWlKXcUb7vFcJnHRr4q5TUZ+J4wjEksw\nWmqK5me2Lnt+wVQnWXplfnknVJaZvCPEfRWVVu53lgSfTkF+rO4Bl6osw2TrIj3T\n8MoOpnGKXSTGuL86cAQAkxbJcqkFeM/ksojVBqVpGn+xdQuOf62j6mFzZl4B9wfo\nKah+O7zbgvJEHhSmvSZlo8b9YJre0YbAJBCcQnAyj3m2oSEwIKz20jjTapsiAJFS\nTwIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "inbox": "https://social.matej-lach.me/users/MatejLach/inbox", - "outbox": "https://social.matej-lach.me/users/MatejLach/outbox", - "followers": "https://social.matej-lach.me/users/MatejLach/followers", - "following": "https://social.matej-lach.me/users/MatejLach/following", - "preferredUsername": "MatejLach", + "attachment": [], "endpoints": { "sharedInbox": "https://social.matej-lach.me/inbox" + }, + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://social.matej-lach.me/system/accounts/avatars/000/000/001/original/6e9242b03795bf80.png" + }, + "image": { + "type": "Image", + "mediaType": "image/png", + "url": "https://social.matej-lach.me/system/accounts/headers/000/000/001/original/f18240c45b0ac254.png" } -} -`, +}`, } for idx := range want { // Prepare the object to marshal first person := Person{} err := json.Unmarshal([]byte(want[idx]), &person) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) got, err := json.MarshalIndent(&person, "", " ") - if err != nil { - t.Fatal(err) - } - if string(got) != strings.TrimSpace(want[idx]) { - t.Fatalf("marshaled Actor JSON not as expected, wanted '%s', got '%s'", strings.TrimSpace(want[idx]), string(got)) - } + require.NoError(t, err) + + assert.JSONEq(t, want[idx], string(got)) } } diff --git a/asvocab_serialization_unmarshal_test.go b/asvocab_serialization_unmarshal_test.go index 22b9aae..aa4e44b 100644 --- a/asvocab_serialization_unmarshal_test.go +++ b/asvocab_serialization_unmarshal_test.go @@ -6,6 +6,9 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestUnmarshalJSON_ObjectOrLink(t *testing.T) { @@ -51,9 +54,7 @@ func TestUnmarshalJSON_ObjectOrLink(t *testing.T) { for _, tc := range testCases { err = json.Unmarshal([]byte(tc), &obj) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) } } @@ -87,9 +88,7 @@ func TestUnmarshalJSON_OrderedCollectionPage(t *testing.T) { for _, tc := range testCases { err = json.Unmarshal([]byte(tc), &ordColPage) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) } } @@ -316,39 +315,26 @@ func TestUnmarshalJSON_ObjectOrLinkOrString(t *testing.T) { for _, tc := range testCases { payloadMeta, err := DecodePayloadObjectType(strings.NewReader(tc)) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) switch payloadMeta.Type { case "Note": var note Note err = json.Unmarshal([]byte(tc), ¬e) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) case "Offer": var offer Offer err = json.Unmarshal([]byte(tc), &offer) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) case "Person": var person Person err = json.Unmarshal([]byte(tc), &person) - if err != nil { - t.Fatal(err) - } - - if person.Name == "" { - t.Fatal(err) - } + require.NoError(t, err) + require.NotEmpty(t, person.Name) default: var obj ObjectOrLinkOrString err = json.Unmarshal([]byte(tc), &obj) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) } } } @@ -369,9 +355,7 @@ func TestUnmarshalJSON_Link(t *testing.T) { for _, tc := range testCases { err = json.Unmarshal([]byte(tc), &lnk) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) } } @@ -470,13 +454,9 @@ func TestOrderedCollection_Sorting(t *testing.T) { ` oc := OrderedCollection{} err := json.Unmarshal([]byte(payload), &oc) - if err != nil { - t.Fatalf("Failed Unmarshaling an OrderedCollection: %s", err) - } + require.NoError(t, err) sort.Sort(oc) mostRecentPostPublishedAt := oc.OrderedItems.Target[0].GetObject().Published.Format(time.RFC3339) - if mostRecentPostPublishedAt != "2024-08-15T09:23:47Z" { - t.Fatalf("Expected %s to be the first published date in a sorted collection, but got: %s", "2024-08-15 09:23:47 +0000 UTC", mostRecentPostPublishedAt) - } + assert.Equal(t, "2024-08-15T09:23:47Z", mostRecentPostPublishedAt) } diff --git a/go.mod b/go.mod index f0b4a9f..7bb244f 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,13 @@ module github.com/MatejLach/astreams -retract ( - [v0.8.0, v0.9.0] // experiments, not fit for consumption -) +retract [v0.8.0, v0.9.0] // experiments, not fit for consumption go 1.18 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 1d5c1d0..60ce688 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,10 @@ -github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= -github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=