ActivityPub Server Conformance Requirements

Servers MUST de-duplicate the final recipient list.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:83a907d3-3c5d-4e1b-99b5-0a8b6efb67fd",
  "uuid": "83a907d3-3c5d-4e1b-99b5-0a8b6efb67fd",
  "type": "Behavior",
  "content": "Servers MUST de-duplicate the final recipient list.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#delivery",
      "branch": [
        7,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.\n",
      "exact": "Servers MUST de-duplicate the final recipient list.\n",
      "suffix": "Servers MUST also exclude actors from the list which are the same as the actor of the Activity being notified about.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:7eb4f1aa-5779-4935-9840-f2909a5c6d70",
  "uuid": "7eb4f1aa-5779-4935-9840-f2909a5c6d70",
  "type": "Behavior",
  "content": "Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#delivery",
      "branch": [
        7,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection.\n",
      "exact": "Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.\n",
      "suffix": "Servers MUST de-duplicate the final recipient list.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server then MUST attach this object as the object of a Create Activity.

Tags
ActivityPubServer
Test Cases
  • Outbox Wraps Object With Create Activity
    JSON
    {
      "name": "Outbox Wraps Object With Create Activity",
      "description": "a test rule that submits an object to an outbox and checks for conformance with requirements for how the outbox server must wrap the object in a Create Activity.",
      "slug": "outbox-wraps-object-with-create-checked-using-get-location",
      "input": {
        "outbox": {
          "help": "URL of ActivityPub outbox that an object will be sent to",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value that will be sent with ActivityPub requests",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "applicability": "\nThis test applies to an ActivityPub Server handling POST requests to an ActivityPub Outbox.\n\nTest Targets\n* outbox: same value as input.outbox\n",
      "inapplicableCases": [],
      "passedCases": [],
      "failedCases": [],
      "uuid": "963a2313-788e-4bb7-b6ea-0d8bf7d255ef",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:e6d00349-6d07-4512-9a99-f698d3bc1dba",
          "url": "https://activitypub-testing.socialweb.coop/e6d00349-6d07-4512-9a99-f698d3bc1dba"
        },
        {
          "id": "urn:uuid:104e2cde-f072-43af-badd-bf9bd4129151",
          "url": "https://activitypub-testing.socialweb.coop/104e2cde-f072-43af-badd-bf9bd4129151"
        },
        {
          "id": "urn:uuid:f3725db1-1e6b-4d20-9cea-a49ef4291e9f",
          "url": "https://activitypub-testing.socialweb.coop/f3725db1-1e6b-4d20-9cea-a49ef4291e9f"
        }
      ],
      "testCases": [
        {
          "name": "outbox=https://socialweb.coop/outbox",
          "id": "urn:uuid:d7c8b377-2850-481b-992f-92ac437a9f3c",
          "input": {
            "outbox": "https://socialweb.coop/outbox"
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "outbox=https://example.com",
          "id": "urn:uuid:dde952d7-b9b0-488f-8b06-61a10ba134d4",
          "input": {
            "outbox": "https://example.com/"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "expectations": "\n* target `outboxPostLocationResponse` http request body must be a JSON string that parses to a JSON Object called `outboxPostResult`\n* `outboxPostResult` must have a `type` property whose value is either the string \"Create\" or an Array containing the string \"Create\"\n* `outboxPostResult` must have a `object` property whose value includes the object from test target `submission` (i.e. the object that was sent to the outbox, and should have been wrapped in a Create).\n* `outboxPostResult` must have a `id` property whose value is a URI\n* `outboxPostResult.object` must have a `id` property whose value is a URI\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult`\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult.object`\n"
    }
JSON
{
  "id": "urn:uuid:e6d00349-6d07-4512-9a99-f698d3bc1dba",
  "uuid": "e6d00349-6d07-4512-9a99-f698d3bc1dba",
  "type": "Behavior",
  "content": "The server then MUST attach this object as the object of a Create Activity.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6.2 Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#create-activity-outbox"
    },
    {
      "name": "6.2.1 Object creation without a Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#object-without-create"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#object-without-create",
      "name": "Object creation without a Create Activity",
      "branch": [
        6,
        2,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The server MUST accept a valid [ActivityStreams] object that isn't a subtype of Activity in the POST request to the outbox.\n",
      "exact": "The server then MUST attach this object as the object of a Create Activity.\n",
      "suffix": "For non-transient objects, the server MUST attach an id to both the wrapping Create and its wrapped Object.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server MUST accept a valid [ActivityStreams] object that isn't a subtype of Activity in the POST request to the outbox.

Tags
ActivityPubServer
Test Cases
  • Outbox Servers handling submissions MUST accept a valid object that isn't a subtype of Activity
    JSON
    {
      "description": "This test checks that an ActivityPub Outbox accepts submission of an ActivityPub Object that is not an Activity, e.g. a Note, which it should then wrap in a Create activity.",
      "failedCases": [],
      "inapplicableCases": [],
      "input": {
        "outbox": {
          "help": "the url to the Outbox handling an Activity POST request",
          "required": true,
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "proof of authorization. This will be included in the HTTP Authorization header for the POST outbox request",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        },
        "submission": {
          "help": "the object to send to the outbox.",
          "required": false,
          "default": "{ \"type\": \"Note\", \"@context\": [\"https://www.w3.org/ns/activitystreams\"] }"
        },
        "time": {
          "help": "amount of time allowed to run test, as IETF RFC3339 dur-time time duration. This is meant to configure the limit for how long this test will wait for network requests.",
          "required": true,
          "default": "T5S",
          "type": [
            "rfc3339:dur-time",
            "TimeLimit"
          ],
          "constraints": [
            {
              "content": " MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)",
              "mediaType": "test/markdown"
            }
          ]
        }
      },
      "name": "Outbox Servers handling submissions MUST accept a valid object that isn't a subtype of Activity",
      "passedCases": [],
      "slug": "outbox-post-must-accept-non-activity-object",
      "uuid": "77748b50-f58c-49e7-8986-98e520b0e890",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:b7b352f2-906b-492d-b64d-20bab5c2ea73",
          "url": "https://socialweb.coop/activitypub/behaviors/b7b352f2-906b-492d-b64d-20bab5c2ea73"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:b7b352f2-906b-492d-b64d-20bab5c2ea73",
  "uuid": "b7b352f2-906b-492d-b64d-20bab5c2ea73",
  "type": "Behavior",
  "content": "The server MUST accept a valid [ActivityStreams] object that isn't a subtype of Activity in the POST request to the outbox.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6.2 Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#create-activity-outbox"
    },
    {
      "name": "6.2.1 Object creation without a Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#object-without-create"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#object-without-create",
      "name": "Object creation without a Create Activity",
      "branch": [
        6,
        2,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "For client to server posting, it is possible to submit an object for creation without a surrounding activity.\n",
      "exact": "The server MUST accept a valid [ActivityStreams] object that isn't a subtype of Activity in the POST request to the outbox.\n",
      "suffix": "The server then MUST attach this object as the object of a Create Activity.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server MUST only target the values of to, cc, and/or audience on the original object being forwarded, and not pick up any new addressees whilst recursing through the linked objects (in case these addressees were purposefully amended by or via the client).

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:5e781a3a-4314-4ec6-b973-717456dc5da5",
  "uuid": "5e781a3a-4314-4ec6-b973-717456dc5da5",
  "type": "Behavior",
  "content": "The server MUST only target the values of to, cc, and/or audience on the original object being forwarded, and not pick up any new addressees whilst recursing through the linked objects (in case these addressees were purposefully amended by or via the client).\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    },
    {
      "name": "7.1.2 Forwarding from Inbox",
      "id": "https://www.w3.org/TR/activitypub/#inbox-forwarding"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#inbox-forwarding",
      "branch": [
        7,
        1,
        2
      ]
    },
    "selector": {
      "prefix": "The server SHOULD recurse through these values to look for linked objects owned by the server, and SHOULD set a maximum limit for recursion (ie. the point at which the thread is so deep the recipients followers may not mind if they are no longer getting updates that don't directly involve the recipient).\n",
      "exact": "The server MUST only target the values of to, cc, and/or audience on the original object being forwarded, and not pick up any new addressees whilst recursing through the linked objects (in case these addressees were purposefully amended by or via the client).\n",
      "suffix": "The server MAY filter its delivery targets according to implementation-specific rules (for example, spam filtering).\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Additionally, servers performing server to server delivery of the following activities MUST also provide the target property: Add, Remove.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:561fc978-61f4-4545-bac8-3b1349e5a515",
  "uuid": "561fc978-61f4-4545-bac8-3b1349e5a515",
  "type": "Behavior",
  "content": "Additionally, servers performing server to server delivery of the following activities MUST also provide the target property: Add, Remove.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions",
      "branch": [
        7
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Servers performing delivery to the inbox or sharedInbox properties of actors on other servers MUST provide the object property in the activity: Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.\n",
      "exact": "Additionally, servers performing server to server delivery of the following activities MUST also provide the target property: Add, Remove.\n",
      "suffix": "HTTP caching mechanisms [RFC7234] SHOULD be respected when appropriate, both when receiving responses from other servers as well as sending responses to other servers.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server MUST then add this new Activity to the outbox collection.

Tags
ActivityPubServer
Test Cases
  • Check that outbox post server added to outbox collection by getting the outbox collection and looking in the items
    JSON
    {
      "name": "Check that outbox post server added to outbox collection by getting the outbox collection and looking in the items",
      "input": {
        "outbox": {
          "help": "the url to the Outbox handling an Activity POST request",
          "required": true,
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value to include in outbox POST request",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "description": "a test rule that sends a submission to an outbox then gets the outbox and expects to find the submitted activity",
      "applicability": "\nHow to derive test targets from input:\n* let outboxPostResponse be the http response from using an activitypub client to post to input.outbox\n* if outboxPostResponse does not contain a 'location' header, result outcome is inapplicable.\n* let resultLocation be the result of [Reference Resolution (rfc3986)](https://www.rfc-editor.org/rfc/rfc3986#section-5) with Base URI set to input.outbox and reference set to the value of the 'location' header in outboxPostResponse.\n  * If the value of the location header is already an absolute url, resultLocation will be set to that value. If it's a relative url, it will be resolved relative to the outbox url.\n* let target.newActivityId equal resultLocation\n* let outboxGetResponse be the http response from using an activitypub client to fetch the ActivityPub object at input.outbox. If input.authorization is provided, it MUST be passed as the value of the http header named authorization.\n* if outboxGetResponse http status is not in range [200, 299], return early. The outcome is 'inapplicable'. This test only applies when the test client can get a successful response to the get request.\n* let target.outbox be the result of parsing outboxGetResponse body as JSON\n",
      "assumptions": "\n* we assume that the outbox post response location header includes the id of the newly submitted activity.\n  * another requirement is outbox post servers [\"MUST include the new id in the Location header\"](https://activitypub-testing-website.socialweb.coop/00330762-59a2-4072-8d93-87ee4c30411c), so it seems like this is a reasonable assumption for servers that satisfy that requirement\n",
      "expectations": "\n* let types be the result of\n  * if target.outbox.type is an Array, return it\n  * return an array containing only target.outbox.type\n* let items be the result of\n  * if types includes \"OrderedCollection\", return target.outbox.orderedItems\n  * else return target.outbox.items\n* at least one entry in items must have an id property equal to target.newActivityId\n",
      "slug": "outbox-post-server-adds-to-outbox-collection-checked-by-outbox-get",
      "uuid": "43d3465d-6f5a-47c2-99ad-44bd883472b3",
      "requirementReference": [
        {
          "id": "urn:uuid:58b55b12-550f-415d-9ce4-a5160c08676f",
          "url": "https://activitypub-testing-website.socialweb.coop/58b55b12-550f-415d-9ce4-a5160c08676f"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:58b55b12-550f-415d-9ce4-a5160c08676f",
  "uuid": "58b55b12-550f-415d-9ce4-a5160c08676f",
  "type": "Behavior",
  "content": "The server MUST then add this new Activity to the outbox collection.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "clients posting Activities to an actor's outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "The server MUST then add this new Activity to the outbox collection.\n",
      "prefix": "The server MUST remove the bto and/or bcc properties, if they exist, from the ActivityStreams object before delivery, but MUST utilize the addressing originally stored on the bto / bcc properties for determining recipients in delivery.\n",
      "suffix": "Depending on the type of Activity, servers may then be required to carry out further side effects. (However, there is no guarantee that time the Activity may appear in the outbox. The Activity might appear after a delay or disappear at any period). These are described per individual Activity below.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

actor objects must have an inbox property

Tags
ActivityPubServer
Test Cases
  • Actor Objects Must Have Properties inbox and outbox
    JSON
    {
      "description": "This rule checks whether a given Actor Object has the properties inbox and outbox.",
      "failedCases": [
        {
          "name": "actor without required properties",
          "input": {
            "actor": {
              "type": "Person"
            }
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "null actor",
          "input": {
            "actor": null
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "empty object actor",
          "input": {
            "actor": {}
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "actor with input actor not json",
          "input": {
            "actor": "2b292jb"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "actor": {
          "help": "the Actor Object to be tested for presence of required properties",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\ntype:\n- TestCase\nstatus: draft\nname: Actor Objects must have properties inbox and outbox\ndescription: |\n  This rule checks whether a given Actor Object has the properties inbox and outbox.\nuuid: acaacb5f-8f7e-4f28-8d81-c7955070a767\nattributedTo:\n- https://bengo.is\n\n\"@context\":\n- TestCase:\n    \"@id\": http://www.w3.org/ns/earl#:TestCase\n  type:\n    \"@type\": \"@id\"\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n    latestVersion: https://socialweb.coop/activitypub/test-cases/actor-objects-must-have-inbox-outbox-properties/\n\n---\n\n# Actor Objects Must Have Properties inbox and outbox\n\n## Background\n\n[ActivityPub][activitypub] [§4.1 Actor Objects](https://www.w3.org/TR/activitypub/#actor-objects):\n> Actor objects MUST have... the following properties: inbox... outbox...\n\n## About this Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* Actor Objects must have inbox property [cc3f730a-37a9-4af9-948f-7c8a0b7f6c41](https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/)\n* Actor Objects must have outbox property [b8647b47-defb-483c-b468-8602d1124169](https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/)\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:acaacb5f-8f7e-4f28-8d81-c7955070a767`.\n\n## Test Subject\n\nThe subject of this test is any data claiming to conform to the specification of an ActivityPub Actor Object.\n\nThis test is *not* directly applicable to an ActivityPub Server.\nAn ActivityPub Server serves 0 or more Actor Objects.\nAn ActivityPub Server for a big community might serve hundreds of ActivityPub Actor Objects.\nAn ActivityPub Server for a single human may serve only that person's ActivityPub Actor Object.\n\nThis test applies to Actor *Objects*, but *not* all Actor Object are addressable by an HTTPS URL.\nThe URI that addresses an Actor Object is not the same as the Actor Object.\nA given URL may resolve to different Actor Objects in different contexts, and a given Actor Object may not be universally addressable across context by any one URL.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n* `actor` - the actor object that should be tested\n    * type: binary data\n    * constraints\n        * will be interpreted as JSON. If not parseable as JSON, the test result MUST be `inapplicable`.\n\n## Applicability\n\nThis test applies directly to the `actor` input.\n\nIf `actor` is not an Actor Object, the outcome MUST be `inapplicable`.\n\n* see assumption \"How to Determine Whether Input is an Actor Object\" below\n\nIf `actor` JSON does not have a `type` property, the outcome MUST be `inapplicable`.\n\n### Test Targets\n\n* `inbox` - the inbox property (or lack thereof) in the input `actor` JSON object\n* `outbox` - the outbox property (or lack thereof) in the input `actor` JSON object\n\n## Expectations\n\n1. `actor` JSON has a property whose name is `inbox`.\n    * If there is no `inbox` property, the outcome is `failed` for test target `inbox`\n\n2. `actor` JSON has a property whose name is `outbox`\n    * If there is no `outbox` property, the outcome is `failed` for test target `outbox`\n\n## Assumptions\n\n### 1. How to Determine Whether Input is an Actor Object\n\nFor the purposes of determining whether the input `actor` is in fact an actor object and whether this test is applicable to that input,\nthis test assumes that a valid way of determining whether the input is an Actor Object is:\n\n* the input is valid JSON\n* the input, once parsed as JSON\n    * has a `type` property\n    * has a `type` property value that is either a string or array of strings\n    * one of those `type` value strings is one of `Application`, `Group`, `Organization`, `Person`, `Service`\n        * rationale:\n            * [ActivityPub 4.3 Actors](https://www.w3.org/TR/activitystreams-core/#actors):\n              > The Activity Vocabulary provides the normative definition of five specific types of Actors: Application | Group | Organization | Person | Service.\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n\n### 2. No property value expectations\n\nThis test does not assert any expectations about the *values* of these required properties. The requirement being tested only requires that they are present.\n\nFor test cases about the values of these properties, see:\n\n* [inbox-must-be-an-orderedcollection](https://socialweb.coop/activitypub/test-cases/inbox-must-be-an-orderedcollection/)\n* [outbox-must-be-an-orderedcollection](https://socialweb.coop/activitypub/test-cases/outbox-must-be-an-orderedcollection/)\n\n## Test Cases\n\nThese are test cases for this test case, and can be used to verify that an implementation of this test case specification will be [consistent](https://www.w3.org/WAI/standards-guidelines/act/implementations/#understanding-act-consistency) with other implementations.\n\n### simple valid actor\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"inbox\": \"https://bengo.is/inbox\",\n      \"outbox\": \"https://bengo.is/outbox\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `passed`\n* `outbox`: present\n    * outcome: `passed`\n\n### missing inbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"outbox\": \"https://bengo.is/outbox\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n* `outbox`: present\n    * outcome: `passed`\n\n### missing outbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"inbox\": \"https://bengo.is/inbox\",\n    }\n    ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `passed`\n* `outbox`: not present\n    * outcome: `failed`\n\n### actor type, but no inbox nor outbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n* `outbox`: not present\n    * outcome: `failed`\n\n### not json\n\ninputs\n\n* `actor`\n\n  ```text\n  abc\n  ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `inapplicable`\n* `outbox`: not present\n    * outcome: `inapplicable`\n\n### json with no properties\n\ninputs\n\n* `actor`\n\n    ```json\n    {}\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object\n* `outbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object\n\n### actor with no other properties\n\ninputs\n\n* `actor`\n\n  ```json\n  {\n    \"type\": \"Person\"\n  }\n  ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n        * rationale: the object is an Actor, but is missing the required property\n* `outbox`: not present\n    * outcome: `failed`\n        * rationale: the object is an Actor, but is missing the required property\n\n### object with `inbox` but no `type`\n\ninputs\n\n* `actor`\n\n  ```json\n  {\n    \"inbox\": \"https://bengo.is/inbox\"\n  }\n  ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object. There is no `type` property indicating this is an Actor Object.\n* `outbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object. There is no `type` property indicating this is an Actor Object.\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject.\nAn outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub requirement cc3f730a-37a9-4af9-948f-7c8a0b7f6c41](https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/) - actor objects must have an inbox property\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `inbox` has outcome `passed`, requirement is satisfied\n        * when test target `inbox` has outcome `failed`, requirement is not satisfied\n        * when test target `inbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n* [ActivityPub requirement b8647b47-defb-483c-b468-8602d1124169](https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/) - actor objects must have an outbox property\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping for test target `outbox`\n        * when test target `outbox` has outcome `passed`, requirement is satisfied\n        * when test target `outbox` has outcome `failed`, requirement is not satisfied\n        * when test target `outbox` has outcome inapplicable, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* 2023-11-03T03:41:20.725Z - first draft\n* 2023-12-29T20:48:45.838Z - update test cases with consistent formatting, headings, and fix some mistakes\n\n## Issues List\n",
      "applicability": "This test applies directly to the `actor` input.\nIf `actor` is not an Actor Object, the outcome MUST be `inapplicable`.\n* see assumption \"How to Determine Whether Input is an Actor Object\" below\nIf `actor` JSON does not have a `type` property, the outcome MUST be `inapplicable`.\n### Test Targets\n* `inbox` - the inbox property (or lack thereof) in the input `actor` JSON object\n* `outbox` - the outbox property (or lack thereof) in the input `actor` JSON object\n",
      "assumptions": "### 1. How to Determine Whether Input is an Actor Object\nFor the purposes of determining whether the input `actor` is in fact an actor object and whether this test is applicable to that input,\nthis test assumes that a valid way of determining whether the input is an Actor Object is:\n* the input is valid JSON\n* the input, once parsed as JSON\n    * has a `type` property\n    * has a `type` property value that is either a string or array of strings\n    * one of those `type` value strings is one of `Application`, `Group`, `Organization`, `Person`, `Service`\n        * rationale:\n",
      "expectations": "1. `actor` JSON has a property whose name is `inbox`.\n    * If there is no `inbox` property, the outcome is `failed` for test target `inbox`\n2. `actor` JSON has a property whose name is `outbox`\n    * If there is no `outbox` property, the outcome is `failed` for test target `outbox`\n",
      "name": "Actor Objects Must Have Properties inbox and outbox",
      "passedCases": [
        {
          "name": "simple valid actor",
          "input": {
            "actor": {
              "type": "Person",
              "inbox": "https://bengo.is/inbox",
              "outbox": "https://bengo.is/outbox"
            }
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "slug": "actor-objects-must-have-inbox-outbox-properties",
      "uuid": "acaacb5f-8f7e-4f28-8d81-c7955070a767",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:cc3f730a-37a9-4af9-948f-7c8a0b7f6c41",
          "url": "https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/"
        },
        {
          "id": "urn:uuid:b8647b47-defb-483c-b468-8602d1124169",
          "url": "https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:cc3f730a-37a9-4af9-948f-7c8a0b7f6c41",
  "type": "Behavior",
  "uuid": "cc3f730a-37a9-4af9-948f-7c8a0b7f6c41",
  "content": "actor objects must have an inbox property",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#actor-objects",
      "branch": [
        4,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "Actor objects MUST have, in addition to the properties mandated by 3.1 Object Identifiers, the following properties:\ninbox\n  A reference to an [ActivityStreams] OrderedCollection comprised of all the messages received by the actor; see 5.2 Inbox. \n",
      "prefix": "4.1 Actor objects\n",
      "suffix": "outbox\n  An [ActivityStreams] OrderedCollection comprised of all the messages produced by the actor; see 5.1 Outbox.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The following collection MUST be either an OrderedCollection or a Collection

Tags
ActivityPubServer
Test Cases
  • An ActivityPub Actor Object's `following` Collection Must be a Collection
    JSON
    {
      "input": {
        "object": {
          "help": "object with a `following` property",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\n\ntype:\n- TestCase\ntags:\n- tests-activitypub-actor\nuuid:\n- 018c3e17-a1bd-7040-8007-4cd3b9063288\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# An ActivityPub Object `following` Collection Must be a Collection\n\n## Background [<sup>?</sup>][test-background]\n\n[ActivityPub][activitypub] [§ 5.4 Following Collection](https://www.w3.org/TR/activitypub/#following):\n\n> Every actor SHOULD have a following collection. This is a list of everybody that the actor has followed, added as a side effect. The following collection MUST be either an OrderedCollection or a Collection\n\n## About This Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* [requirement a4876ff4-7751-4bc6-91e0-9275382d4a85](https://socialweb.coop/activitypub/behaviors/a4876ff4-7751-4bc6-91e0-9275382d4a85/) - The following collection MUST be either an OrderedCollection or a Collection\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:018c3e17-a1bd-7040-8007-4cd3b9063288`.\n\n## Test Subject [<sup>?</sup>][test-subject]\n\nThe subject of this test is an ActivityPub Actor Object.\n\n## Input [<sup>?</sup>][test-input]\n\nThis test requires the following [inputs](act-rules-input) indicating parts of the test subject:\n\n1. `object` - the object whose `following` property will be tested\n    * type: binary\n    * constraints\n        * should be JSON\n\n## Applicability [<sup>?</sup>][test-applicability]\n\nThis test applies to each of the following collections derived from the values of the property on `object` named `following`.\n\n### Prerequisites\n\n* `object` is a JSON object\n* `object` JSON object has a property named `following`\n\n### How to Derive Test Targets from Input\n\n* let `targets` be a set\n* let `followingValues` be the result of\n    * if `object.following` is an Array, return it\n    * else return an Array whose only item is the value of `object.following`\n* for each item in `followingValues`\n    * if item is a string\n        * let itemFetched be the result of interpreting the string as an ActivityPub Object Identifier and fetching the corresponding ActivityPub Object.\n        * add itemFetched to `targets`\n    * else add item to `targets`\n* return `targets`\n\n## Expectations [<sup>?</sup>][test-expectations]\n\nFor every target `target`\n\n* `target` is a JSON object\n* `target` has a property named `type`\n* the values of the `target`'s `type` property must be one of\n    * an array containing the string \"Collection\"\n    * an array containing the string \"OrderedCollection\"\n    * the string \"Collection\"\n    * the string \"OrderedCollection\"\n\n## Assumptions [<sup>?</sup>][test-assumptions]\n\n## Test Cases [<sup>?</sup>][test-test-cases]\n\nWhat follows are some specific cases of applything this test.\n\n### Passed Example 1 - following has type Collection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": {\n      \"type\": \"Collection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"Collection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 2 - following has type OrderedCollection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": {\n      \"type\": \"OrderedCollection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"OrderedCollection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 3 - following type includes OrderedCollection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": {\n      \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n  }\n  ```\n\n    * outcome: `passed`\n        * rationale\n            * `\"FancyCollection\"` is not well-defined here,\nbut this object still passes the test because at least one of the `type`\nproperty values is `OrderedCollection` and thus satisfies the requirement\nas interpreted.\n\n### Failed Example 1 - following is a number\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": 1\n  }\n  ```\n\ntest targets\n\n* ```json\n  1\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 2 - following is an empty object\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": {}\n  }\n  ```\n\ntest targets\n\n* ```json\n  {}\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 3 - following object type is array including only `\"Link\"`\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"following\": {\n      \"type\": [\"Link\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"Link\"]\n  }\n  ```\n\n    * outcome: `failed`\n\n### Inapplicable Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  1\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n### Inapplicable Example 2 - object has no following property\n\ninputs\n\n* `object`:\n\n  ```json\n  { \"foo\": \"bar\" }\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/#glossary)\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping [<sup>?</sup>][act-rules-requirements-mapping]\n\n* [ActivityPub requirement a4876ff4-7751-4bc6-91e0-9275382d4a85](https://socialweb.coop/activitypub/behaviors/a4876ff4-7751-4bc6-91e0-9275382d4a85/) - The following collection MUST be either an OrderedCollection or a Collection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when every test target has outcome `passed`, the requirement is satisfied by the test subject\n        * when any test target has outcome `failed`, the requirement is not satisfied by the test subject\n        * when any test target has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Issues [<sup>?</sup>][test-issues-list]\n\n## Change Log\n\n* 2023-12-06T07:43:27.890Z - init\n\n[act-rules-requirements-mapping]: https://www.w3.org/TR/act-rules-format/#accessibility-requirements-mapping\n[activitypub]: https://www.w3.org/TR/activitypub/\n[test-subject]: https://www.w3.org/TR/act-rules-format/#test-subject\n[test-applicability]: https://www.w3.org/TR/act-rules-format/#applicability\n[test-assumptions]: https://www.w3.org/TR/act-rules-format/#assumptions\n[test-background]: https://www.w3.org/TR/act-rules-format/#background\n[test-expectations]: https://www.w3.org/TR/act-rules-format/#expectations\n[test-input]: https://www.w3.org/TR/act-rules-format/#input\n[test-issues-list]: https://www.w3.org/TR/act-rules-format/#issues-list\n[test-test-cases]: https://www.w3.org/TR/act-rules-format/#test-cases\n",
      "name": "An ActivityPub Actor Object's `following` Collection Must be a Collection",
      "description": "tests whether an ActivityPub Object has a `following`` collection with an appropriate Collection type",
      "slug": "following-collection-must-be-a-collection",
      "inapplicableCases": [
        {
          "name": "Inapplicable Example 1 - object is a number",
          "result": {
            "outcome": "inapplicable"
          },
          "input": {
            "object": "42"
          }
        },
        {
          "name": "Inapplicable Example 2 - object has no following property",
          "result": {
            "outcome": "inapplicable"
          },
          "input": {
            "object": "{ \"foo\": \"bar\" }"
          }
        },
        {
          "name": "inapplicable example 3 - html object",
          "input": {
            "object": "<!doctype html>hi"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "passedCases": [
        {
          "name": "Passed Example 1",
          "result": {
            "outcome": "passed"
          },
          "input": {
            "object": "\n        {\n          \"following\": {\n            \"type\": \"Collection\"\n          }\n        }\n      "
          }
        },
        {
          "name": "Passed Example 2",
          "result": {
            "outcome": "passed"
          },
          "input": {
            "object": "\n        {\n          \"following\": {\n            \"type\": \"OrderedCollection\"\n          }\n        }\n      "
          }
        }
      ],
      "failedCases": [
        {
          "name": "Failed Example 1 - following is a number",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"following\": 42\n        }\n      "
          }
        },
        {
          "name": "Failed Example 2 - following is an empty object",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"following\": {}\n        }\n      "
          }
        },
        {
          "name": "Failed Example 3 - following object type is array including only `\"Link\"`",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"following\": {\n            \"type\": [\"Link\"]\n          }\n        }\n      "
          }
        }
      ],
      "uuid": "018c3e17-a1bd-7040-8007-4cd3b9063288",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:a4876ff4-7751-4bc6-91e0-9275382d4a85",
          "url": "https://socialweb.coop/activitypub/behaviors/a4876ff4-7751-4bc6-91e0-9275382d4a85/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:a4876ff4-7751-4bc6-91e0-9275382d4a85",
  "type": "Behavior",
  "uuid": "a4876ff4-7751-4bc6-91e0-9275382d4a85",
  "content": "The following collection MUST be either an OrderedCollection or a Collection",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#following",
      "branch": [
        5,
        4
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Every actor SHOULD have a following collection. This is a list of everybody that the actor has followed, added as a side effect.\n",
      "exact": "The following collection MUST be either an OrderedCollection or a Collection\n",
      "suffix": "and MAY be filtered on privileges of an authenticated user or as appropriate when no authentication is given.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Identifiers MUST be provided for intransient activities posted in server to server communication

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:723f3629-b7ac-4c6e-8292-0b4d28c60e9d",
  "type": "Behavior",
  "uuid": "723f3629-b7ac-4c6e-8292-0b4d28c60e9d",
  "name": "Identifiers MUST be provided for intransient activities posted in server to server communication",
  "content": "Identifiers MUST be provided for activities posted in server to server communication, unless the activity is intentionally transient.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#obj-id",
      "branch": [
        3,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "Identifiers MUST be provided for activities posted in server to server communication, unless the activity is intentionally transient.\n",
      "prefix": "An ID explicitly specified as the JSON null object, which implies an anonymous object (a part of its parent context)\n",
      "suffix": "However, for client to server communication, a server receiving an object posted to the outbox with no specified id SHOULD allocate an object ID in the actor's namespace and attach it to the posted object. \n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The receiving server MUST take care to be sure that the Update is authorized to modify its object.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:3b43c69e-cd6b-4ffd-9ccd-183c5323d212",
  "uuid": "3b43c69e-cd6b-4ffd-9ccd-183c5323d212",
  "type": "Behavior",
  "content": "The receiving server MUST take care to be sure that the Update is authorized to modify its object.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.3 Update Activity",
      "id": "https://www.w3.org/TR/activitypub/#update-activity-inbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#update-activity-inbox",
      "branch": [
        7,
        3
      ]
    },
    "selector": {
      "prefix": "Unlike the client to server handling of the Update activity, this is not a partial update but a complete replacement of the object.\n",
      "exact": "The receiving server MUST take care to be sure that the Update is authorized to modify its object.\n",
      "suffix": "At minimum, this may be done by ensuring that the Update and its object are of same origin.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

intransient objects must have unique global identifiers

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:fc2bfa75-8fd0-45e0-809d-7aa3970be8ae",
  "type": "Behavior",
  "name": "intransient objects must have unique global identifiers",
  "content": "all objects distributed by the ActivityPub protocol MUST have unique global identifiers, unless they are intentionally transient\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#obj-id",
      "branch": [
        3,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "all objects distributed by the ActivityPub protocol MUST have unique global identifiers, unless they are intentionally transient (short lived activities that are not intended to be able to be looked up, such as some kinds of chat messages or game notifications).\n",
      "prefix": "All Objects in [ActivityStreams] should have unique global identifiers. ActivityPub extends this requirement; \n",
      "suffix": "These identifiers must fall into one of the following groups:\n"
    }
  },
  "uuid": "fc2bfa75-8fd0-45e0-809d-7aa3970be8ae",
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Servers MUST also exclude actors from the list which are the same as the actor of the Activity being notified about.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:4592b2b2-9f47-4db6-a0a1-e7ba637b002e",
  "uuid": "4592b2b2-9f47-4db6-a0a1-e7ba637b002e",
  "type": "Behavior",
  "content": "Servers MUST also exclude actors from the list which are the same as the actor of the Activity being notified about.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#delivery",
      "branch": [
        7,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Servers MUST de-duplicate the final recipient list.\n",
      "exact": "Servers MUST also exclude actors from the list which are the same as the actor of the Activity being notified about.\n",
      "suffix": "That is, actors shouldn't have their own activities delivered to themselves.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:66d5b416-7660-48c8-884b-fda5e26b28e8",
  "uuid": "66d5b416-7660-48c8-884b-fda5e26b28e8",
  "type": "Behavior",
  "content": "If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#delivery",
      "branch": [
        7,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The inbox is determined by first retrieving the target actor's JSON-LD representation and then looking up the inbox property.\n",
      "exact": "If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection.\n",
      "suffix": "Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The liked collection MUST be either an OrderedCollection or a Collection

Tags
ActivityPubServer
Test Cases
  • An ActivityPub Object `liked` Collection Must be a Collection
    JSON
    {
      "input": {
        "object": {
          "help": "object with a liked property",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\n\ntype:\n- TestCase\ntags:\n- tests-activitypub-actor\nuuid:\n- 018c3df2-d6d8-7f62-805b-b71a96cc6170\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# An ActivityPub Object `liked` Collection Must be a Collection\n\n## Background [<sup>?</sup>][test-background]\n\n[ActivityPub][activitypub] [§ 5.5 Liked Collection](https://www.w3.org/TR/activitypub/#liked):\n> Every object MAY have a liked collection.\n> &hellip;\n> The liked collection MUST be either an OrderedCollection or a Collection\n\n## About This Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* [requirement d2db8da3-25d4-4dd9-9c9c-b2793fd899cf](https://socialweb.coop/activitypub/behaviors/d2db8da3-25d4-4dd9-9c9c-b2793fd899cf/) - The liked collection MUST be either an OrderedCollection or a Collection\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:018c3df2-d6d8-7f62-805b-b71a96cc6170`.\n\n## Test Subject\n\nThe subject of this test is an ActivityPub Object.\n\n## Input [<sup>?</sup>][test-input]\n\nThis test requires the following [inputs](act-rules-input) indicating parts of the test subject:\n\n1. `object` - the object whose `liked` property will be tested\n    * type: binary\n    * constraints\n        * should be JSON\n\n## Applicability [<sup>?</sup>][test-applicability]\n\nThis test applies to each of the liked collections derived from the values of the property on `object` named `liked`.\n\n### Prerequisites\n\n* `object` is a JSON object\n* `object` JSON object has a property named `liked`\n\n### How to Derive Test Targets from Input\n\n* let `targets` be a set\n* let `likedValues` be the result of\n    * if `object.liked` is an Array, return it\n    * else return an Array whose only item is the value of `object.liked`\n* for each item in `likedValues`\n    * if item is a string\n        * let itemFetched be the result of interpreting the string as an ActivityPub Object Identifier and fetching the corresponding ActivityPub Object.\n        * add itemFetched to `targets`\n    * else add item to `targets`\n* return `targets`\n\n## Expectations [<sup>?</sup>][test-expectations]\n\nFor every target `target`\n\n* `target` is a JSON object\n* `target` has a property named `type`\n* the values of the `target`'s `type` property must be one of\n    * an array containing the string \"Collection\"\n    * an array containing the string \"OrderedCollection\"\n    * the string \"Collection\"\n    * the string \"OrderedCollection\"\n\n## Assumptions [<sup>?</sup>][test-assumptions]\n\n## Test Cases [<sup>?</sup>][test-test-cases]\n\nWhat follows are some specific cases of applything this test.\n\n### Passed Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": {\n      \"type\": \"Collection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"Collection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 2\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": {\n      \"type\": \"OrderedCollection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"OrderedCollection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 3\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": {\n      \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n  }\n  ```\n\n    * outcome: `passed`\n        * rationale: `\"FancyCollection\"` is not well-defined here, but this object still passes the test because at least one of the `type` property values is `OrderedCollection` and thus satisfies the requirement as interpreted.\n\n### Failed Example 1 - Likes is a number\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": 1\n  }\n  ```\n\ntest targets\n\n* ```json\n  1\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 2 - Likes is an empty object\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": {}\n  }\n  ```\n\ntest targets\n\n* ```json\n  {}\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 3 - Likes object type is array including only `\"Link\"`\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"liked\": {\n      \"type\": [\"Link\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"Link\"]\n  }\n  ```\n\n    * outcome: `failed`\n\n### Inapplicable Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  1\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n### Inapplicable Example 2 - object has no likes property\n\ninputs\n\n* `object`:\n\n  ```json\n  { \"foo\": \"bar\" }\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/#glossary)\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping [<sup>?</sup>][act-rules-requirements-mapping]\n\n* [ActivityPub requirement d2db8da3-25d4-4dd9-9c9c-b2793fd899cf](https://socialweb.coop/activitypub/behaviors/d2db8da3-25d4-4dd9-9c9c-b2793fd899cf/) - The liked collection MUST be either an OrderedCollection or a Collection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when every test target has outcome `passed`, the requirement is satisfied by the test subject\n        * when any test target has outcome `failed`, the requirement is not satisfied by the test subject\n        * when any test target has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Issues [<sup>?</sup>][test-issues-list]\n\n## Change Log\n\n* 2023-12-06T07:07:25.913Z - init\n* 2023-12-18T04:43:02.981Z - add test cases based on automating this test\n\n[act-rules-requirements-mapping]: https://www.w3.org/TR/act-rules-format/#accessibility-requirements-mapping\n[activitypub]: https://www.w3.org/TR/activitypub/\n[test-applicability]: https://www.w3.org/TR/act-rules-format/#applicability\n[test-assumptions]: https://www.w3.org/TR/act-rules-format/#assumptions\n[test-background]: https://www.w3.org/TR/act-rules-format/#background\n[test-expectations]: https://www.w3.org/TR/act-rules-format/#expectations\n[test-input]: https://www.w3.org/TR/act-rules-format/#input\n[test-issues-list]: https://www.w3.org/TR/act-rules-format/#issues-list\n[test-test-cases]: https://www.w3.org/TR/act-rules-format/#test-cases\n",
      "name": "An ActivityPub Object `liked` Collection Must be a Collection",
      "description": "tests whether an ActivityPub Object has a liked collection with an appropriate Collection type",
      "slug": "liked-collection-must-be-a-collection",
      "inapplicableCases": [
        {
          "name": "inapplicable example 1",
          "input": {
            "object": "1"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "inapplicble example 2 - object has no liked property",
          "input": {
            "object": "\n      { \"foo\": \"bar\" }\n      "
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "passedCases": [
        {
          "name": "passed example 1",
          "input": {
            "object": "\n        {\n          \"liked\": {\n            \"type\": \"Collection\"\n          }\n        }\n      "
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "passed example 2",
          "input": {
            "object": "\n      {\n        \"liked\": {\n          \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n        }\n      }\n      "
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "failedCases": [
        {
          "name": "failed example 1",
          "input": {
            "object": "\n        {\n          \"liked\": 1\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "failed example 2",
          "input": {
            "object": "\n        {\n          \"liked\": {}\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "failed example 3",
          "input": {
            "object": "\n        {\n          \"liked\": {\n            \"type\": [\"Link\"]\n          }\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "uuid": "018c3df2-d6d8-7f62-805b-b71a96cc6170",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:d2db8da3-25d4-4dd9-9c9c-b2793fd899cf",
          "url": "https://socialweb.coop/activitypub/behaviors/d2db8da3-25d4-4dd9-9c9c-b2793fd899cf/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:d2db8da3-25d4-4dd9-9c9c-b2793fd899cf",
  "type": "Behavior",
  "uuid": "d2db8da3-25d4-4dd9-9c9c-b2793fd899cf",
  "content": "The liked collection MUST be either an OrderedCollection or a Collection\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#liked",
      "branch": [
        5,
        5
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Every actor MAY have a liked collection. This is a list of every object from all of the actor's Like activities, added as a side effect.\n",
      "exact": "The liked collection MUST be either an OrderedCollection or a Collection\n",
      "suffix": "and MAY be filtered on privileges of an authenticated user or as appropriate when no authentication is given.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

ActivityPub server must present object in response to GET request with AS2 Accept Header

Tags
ActivityPubServer
Test Cases
  • ActivityPub Servers Must Serve Objects in Response to an HTTP GET Request Accepting ActivityStreams 2.0 Media Type
    JSON
    {
      "description": "This rule checks that URLs of ActivityPub objects can be resolved to a representation with well-known media type for further processing.",
      "expectations": "* `response` body is parseable as JSON\n* `response` body parsed JSON is a JSON Object\n* `response` body must be an \"ActivityStreams object representation\"\n",
      "assumptions": "",
      "applicability": "This test applies to a server hosting the ActivityPub Object identified by input `id`.\nIf input `id` is not a URI, outcome is `inapplicable`.\nIf input `id` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes).\nIf input `time` is not parseable as an RFC3339 `dur-time`, outcome is `inapplicable`.\n### Test Targets\n* `response` - the HTTP response that is the result of retrieving the ActivityPub Object identified by input `id`.\n    * how to derive `response` from inputs\n    1. start a timer with duration from input `time`. If the timer reaches zero before this derivation is complete, the whole test outcome is `inapplicable` because we weren't able to determine the `response` test target within the required time.\n    2. let `request` be a new HTTP Request whose URI is input `id`\n    3. add an http request header to `request` whose name is `Accept` and whose value is `application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`\n    4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization`\n    5. send the HTTP request and await a response\n    6. assign the HTTP response to the `response` test target\n",
      "failedCases": [
        {
          "name": "nginx 404 response body",
          "input": {
            "id": "https://bengo.is/404",
            "time": "T1M"
          },
          "result": {
            "outcome": "failed"
          },
          "targets": {
            "response": {
              "httpVersion": "2.0",
              "statusCodeValue": "404",
              "headers": [
                [
                  "content-type",
                  "text/html; charset=UTF-8"
                ],
                [
                  "content-length",
                  "153"
                ]
              ],
              "body": "\n      <html>\n      <head><title>404 Not Found</title></head>\n      <body>\n      <center><h1>404 Not Found</h1></center>\n      <hr><center>nginx/1.25.2</center>\n      </body>\n      </html>    \n      "
            }
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "non-URI input id",
          "input": {
            "id": "bafybeib5mvfjatmpswc3jnh7ydz4zxe25cm63xp6aafpg3j2awakf63qma",
            "time": "T1M"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "non-RFC3339-duration input time",
          "input": {
            "id": "https://bengo.is/actor.json",
            "time": "5 minutes"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "id": {
          "help": "identifier of an ActivityPub Object hosted at an ActivityPub Server",
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ],
          "required": true
        },
        "authorization": {
          "help": "proof of authorization to retrieve the object identified by input `id`"
        },
        "time": {
          "help": "amount of time allowed to run test. This is meant to configure the limit for how long this test will wait for network requests. MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)",
          "required": true,
          "type": [
            "rfc3339:dur-time",
            "TimeLimit"
          ]
        }
      },
      "markdown": "---\n\nuuid: e7ee491d-88d7-4e67-80c8-f74781bb247c\ntype:\n- TestCase\n- ConformanceTestingRule\nruleType: atomic\nname: |\n  ActivityPub server serves an object in response to GET request for AS2 media type\ndescription: |\n  This rule checks that URLs of ActivityPub objects can be resolved to a representation with well-known media type for further processing.\nrequirementReference:\n- id: urn:uuid:00330762-59a2-4072-8d93-87ee4c30411c\n  url: https://socialweb.coop/activitypub/behaviors/08549639-2888-4ee2-a320-97fc7ee32e00/\ninputs:\n- name: URL of AS2 Object served by an ActivityPub Server\n  type: URL\n\n# https://www.w3.org/QA/WG/2005/01/test-faq#review\n# *submitted, accepted, reviewed*, *returned for revision*, or *rejected*\ntestCaseState: submitted\n\neleventyComputed:\n  title: \"{{ name }}\"\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# ActivityPub Servers Must Serve Objects in Response to an HTTP GET Request Accepting ActivityStreams 2.0 Media Type\n\n## Background\n\n[ActivityPub][activitypub] [§3.2 Retrieving Objects](https://www.w3.org/TR/activitypub/#retrieving-objects):\n> Servers... MUST present the ActivityStreams object representation in response to `application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`\n\n## About This Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* [requirement 08549639-2888-4ee2-a320-97fc7ee32e00](https://socialweb.coop/activitypub/behaviors/08549639-2888-4ee2-a320-97fc7ee32e00/) - ActivityPub server must present object in response to GET request with AS2 Accept Header\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:e7ee491d-88d7-4e67-80c8-f74781bb247c`.\n\n## Test Subject\n\nThe subject of this test is an ActivityPub Server.\n\nActivityPub Servers host ActivityPub Objects and are responsible for serving them to clients that request a representation of them.\n\nThe Test Subject can be identified by a URI for an ActivityPub Object. The ActivityPub Object can be requested via HTTP, and the ActivityPub Server is the system that is expected to respond to the HTTP request.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n1. `id` - identifier of an ActivityPub Object hosted at an ActivityPub Server\n\n    * required: yes\n    * type: binary\n        * constraints\n            * MUST be a URI, i.e. an [ActivityPub Object Identifier](https://www.w3.org/TR/activitypub/#obj-id) that is not `null`\n\n2. `authorization` - proof of authorization to retrieve the object identified by input `id`\n\n    * required: no\n        * if this input is omitted, no `Authorization` will be provided in the HTTP request send by this test\n    * type: binary\n        * constraints\n            * If present, this should be valid as the value of an HTTP `Authorization` header\n\n3. `time` - amount of time allowed to run test. This is meant to configure the limit for how long this test will wait for network requests.\n\n    * required: yes\n    * example: `T0.0021S`\n    * type: binary\n        * constraints\n            * MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)\n\n## Applicability\n\nThis test applies to a server hosting the ActivityPub Object identified by input `id`.\n\nIf input `id` is not a URI, outcome is `inapplicable`.\n\nIf input `id` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes).\n\nIf input `time` is not parseable as an RFC3339 `dur-time`, outcome is `inapplicable`.\n\n### Test Targets\n\n* `response` - the HTTP response that is the result of retrieving the ActivityPub Object identified by input `id`.\n    * how to derive `response` from inputs\n    1. start a timer with duration from input `time`. If the timer reaches zero before this derivation is complete, the whole test outcome is `inapplicable` because we weren't able to determine the `response` test target within the required time.\n    2. let `request` be a new HTTP Request whose URI is input `id`\n    3. add an http request header to `request` whose name is `Accept` and whose value is `application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`\n    4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization`\n    5. send the HTTP request and await a response\n    6. assign the HTTP response to the `response` test target\n\n## Expectations\n\n* `response` body is parseable as JSON\n* `response` body parsed JSON is a JSON Object\n* `response` body must be an \"ActivityStreams object representation\"\n    * see issue [AP-ec50](#AP-ec50) below\n\n## Assumptions\n\n## Implementation Considerations\n\nThough the spec only *requires* serving the object in response to this `Accept` header value, servers seeking to interop widely may also want to serve the same object in response to Accept header values like:\n\n* `application/ld+json` (but without the `profile=` parameter)\n* `application/activity+json`\n* `application/json`\n\n## Test Cases\n\nThese are test cases for this test itself.\n\n### simple passed case\n\noutcome: `passed`\n\ninputs\n\n* `id`: `https://bengo.is/actor.json`\n* `time`: `T1M`\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/2 200 \n    content-type: application/json\n    content-length: 484\n\n    {\n      \"@context\": [\n        \"https://www.w3.org/ns/activitystreams\",\n        \"https://w3id.org/security/v1\"\n      ],\n      \"id\": \"https://bengo.is/\",\n      \"type\": \"Person\",\n      \"preferredUsername\": \"bengo\",\n      \"name\": \"bengo\",\n      \"url\": \"https://bengo.is/\",\n      \"inbox\": \"https://mastodon.social/users/bengo/inbox\",\n      \"attachments\": [\n        {\n          \"type\": \"PropertyValue\",\n          \"name\": \"Website\",\n          \"value\": \"https://bengo.is\"\n        }\n      ],\n      \"outbox\": \"https://bengo.is/activitypub/actors/bengo/outbox.json\"\n    }\n    ```\n\n    * outcome: `passed`\n\n### nginx 404 response body\n\noutcome: `failed`\n\n* rationale: test target `response` does not meet expectation of containing an ActivityPub Object representation in the response body\n\ninputs\n\n* `id`: `https://bengo.is/404`\n* `time`: `T1M`\n\ntest targets\n\n* `response`:\n\n  ```http\n  HTTP/2 404 \n  content-type: text/html; charset=UTF-8\n  content-length: 153\n  \n  <html>\n  <head><title>404 Not Found</title></head>\n  <body>\n  <center><h1>404 Not Found</h1></center>\n  <hr><center>nginx/1.25.2</center>\n  </body>\n  </html>\n  ```\n\n    * outcome: `failed`\n\n### non-URI input `id`\n\noutcome: `inapplicable`\n\n* rationale: input `id` is not a URI\n\ninputs\n\n* `id`: `bafybeib5mvfjatmpswc3jnh7ydz4zxe25cm63xp6aafpg3j2awakf63qma`\n* `time`: `T1M`\n\ntest targets\n\n* `response` - undefined\n    * irrelevant because `id` does not meet syntax requirements\n\n### non-RFC3339-duration input `time`\n\noutcome: `inapplicable`\n\n* rationale: input `time` does not meet syntax requirements\n\ninputs\n\n* `id`: `https://bengo.is/actor.json`\n* `time`: `5 minutes`\n\ntest targets\n\n* `response` - undefined\n    * irrelevant because `time` input is malformed regardless of resolved `resposne`\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub requirement 08549639-2888-4ee2-a320-97fc7ee32e00](https://socialweb.coop/activitypub/behaviors/08549639-2888-4ee2-a320-97fc7ee32e00/) - ActivityPub server must present object in response to GET request with AS2 Accept Header\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `response` has outcome `passed`, requirement is satisfied\n        * when test target `response` has outcome `failed`, requirement is not satisfied\n        * when test target `response` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* (~) 2023-10-15T00:00:00Z - sketch as first test case\n* 2023-11-07T21:07:59.214Z - update to be internally consistent and a good first draft\n* 2023-12-29T21:08:32.432Z - clean up test case formatting\n\n## Issues List\n\n* <div id=\"AP-ec50\">AP-ec50: clarify how to verify response body is an \"ActivityStreams object representation\"</div>\n* When rendered to HTML and [published here](https://socialweb.coop/activitypub/test-cases/actor-must-serve-as2-object-to-get/#simple-passed-case), the `response` test targets HTTP response syntax highlighting separates the HTTP headers from the response body. This is a quirk of the rendering process.\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n",
      "name": "ActivityPub Servers Must Serve Objects in Response to an HTTP GET Request Accepting ActivityStreams 2.0 Media Type",
      "passedCases": [
        {
          "name": "simple passed case",
          "input": {
            "id": "https://bengo.is/actor.json",
            "time": "T1M"
          },
          "result": {
            "outcome": "passed"
          },
          "targets": {
            "response": {
              "httpVersion": "2",
              "statusCodeValue": "200",
              "headers": [
                [
                  "content-type",
                  "application/json"
                ],
                [
                  "content-length",
                  "404"
                ]
              ],
              "body": "\n      {\n        \"@context\": [\n          \"https://www.w3.org/ns/activitystreams\",\n          \"https://w3id.org/security/v1\"\n        ],\n        \"id\": \"https://bengo.is/\",\n        \"type\": \"Person\",\n        \"preferredUsername\": \"bengo\",\n        \"name\": \"bengo\",\n        \"url\": \"https://bengo.is/\",\n        \"inbox\": \"https://mastodon.social/users/bengo/inbox\",\n        \"attachments\": [\n          {\n            \"type\": \"PropertyValue\",\n            \"name\": \"Website\",\n            \"value\": \"https://bengo.is\"\n          }\n        ],\n        \"outbox\": \"https://bengo.is/activitypub/actors/bengo/outbox.json\"\n      }\n      "
            }
          }
        }
      ],
      "slug": "actor-must-serve-as2-object-to-get",
      "uuid": "e7ee491d-88d7-4e67-80c8-f74781bb247c",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:08549639-2888-4ee2-a320-97fc7ee32e00",
          "url": "https://socialweb.coop/activitypub/behaviors/08549639-2888-4ee2-a320-97fc7ee32e00/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:08549639-2888-4ee2-a320-97fc7ee32e00",
  "type": "Behavior",
  "name": "ActivityPub server must present object in response to GET request with AS2 Accept Header",
  "content": "Servers… *MUST* present the ActivityStreams object representation in response\nto `application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#retrieving-objects",
      "branch": [
        3,
        2
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "MUST present the ActivityStreams object representation in response to application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
      "prefix": "in response to a request, but",
      "suffix": ", and SHOULD also present"
    }
  },
  "uuid": "08549639-2888-4ee2-a320-97fc7ee32e00",
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server... MUST utilize the addressing originally stored on the bto / bcc properties for determining recipients in delivery.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:24dc401b-1d15-4232-b730-a31dd5e6c4a0",
  "uuid": "24dc401b-1d15-4232-b730-a31dd5e6c4a0",
  "type": "Behavior",
  "content": "The server... MUST utilize the addressing originally stored on the bto / bcc properties for determining recipients in delivery.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "server delivering activities clients post to an actor's outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The server MUST remove the bto and/or bcc properties, if they exist, from the ActivityStreams object before delivery, but \n",
      "exact": "MUST utilize the addressing originally stored on the bto / bcc properties for determining recipients in delivery.\n",
      "suffix": "The server MUST then add this new Activity to the outbox collection.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Federated servers MUST perform delivery on all Activities posted to the outbox according to outbox delivery.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:5a236e14-53b7-43c6-a4aa-166ed711537c",
  "type": "Behavior",
  "uuid": "5a236e14-53b7-43c6-a4aa-166ed711537c",
  "content": "Federated servers MUST perform delivery on all Activities posted to the outbox according to outbox delivery.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6. Client to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#client-to-server-interactions"
    },
    {
      "name": "6.11 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#undo-activity-outbox",
      "name": "Delivery",
      "branch": [
        6,
        11
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "6.11 Delivery\n",
      "exact": "Federated servers MUST perform delivery on all Activities posted to the outbox according to outbox delivery.\n",
      "suffix": "6.12 Uploading Media\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

actor objects must have an outbox property

Tags
ActivityPubServer
Test Cases
  • Actor Objects Must Have Properties inbox and outbox
    JSON
    {
      "description": "This rule checks whether a given Actor Object has the properties inbox and outbox.",
      "failedCases": [
        {
          "name": "actor without required properties",
          "input": {
            "actor": {
              "type": "Person"
            }
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "null actor",
          "input": {
            "actor": null
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "empty object actor",
          "input": {
            "actor": {}
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "actor with input actor not json",
          "input": {
            "actor": "2b292jb"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "actor": {
          "help": "the Actor Object to be tested for presence of required properties",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\ntype:\n- TestCase\nstatus: draft\nname: Actor Objects must have properties inbox and outbox\ndescription: |\n  This rule checks whether a given Actor Object has the properties inbox and outbox.\nuuid: acaacb5f-8f7e-4f28-8d81-c7955070a767\nattributedTo:\n- https://bengo.is\n\n\"@context\":\n- TestCase:\n    \"@id\": http://www.w3.org/ns/earl#:TestCase\n  type:\n    \"@type\": \"@id\"\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n    latestVersion: https://socialweb.coop/activitypub/test-cases/actor-objects-must-have-inbox-outbox-properties/\n\n---\n\n# Actor Objects Must Have Properties inbox and outbox\n\n## Background\n\n[ActivityPub][activitypub] [§4.1 Actor Objects](https://www.w3.org/TR/activitypub/#actor-objects):\n> Actor objects MUST have... the following properties: inbox... outbox...\n\n## About this Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* Actor Objects must have inbox property [cc3f730a-37a9-4af9-948f-7c8a0b7f6c41](https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/)\n* Actor Objects must have outbox property [b8647b47-defb-483c-b468-8602d1124169](https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/)\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:acaacb5f-8f7e-4f28-8d81-c7955070a767`.\n\n## Test Subject\n\nThe subject of this test is any data claiming to conform to the specification of an ActivityPub Actor Object.\n\nThis test is *not* directly applicable to an ActivityPub Server.\nAn ActivityPub Server serves 0 or more Actor Objects.\nAn ActivityPub Server for a big community might serve hundreds of ActivityPub Actor Objects.\nAn ActivityPub Server for a single human may serve only that person's ActivityPub Actor Object.\n\nThis test applies to Actor *Objects*, but *not* all Actor Object are addressable by an HTTPS URL.\nThe URI that addresses an Actor Object is not the same as the Actor Object.\nA given URL may resolve to different Actor Objects in different contexts, and a given Actor Object may not be universally addressable across context by any one URL.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n* `actor` - the actor object that should be tested\n    * type: binary data\n    * constraints\n        * will be interpreted as JSON. If not parseable as JSON, the test result MUST be `inapplicable`.\n\n## Applicability\n\nThis test applies directly to the `actor` input.\n\nIf `actor` is not an Actor Object, the outcome MUST be `inapplicable`.\n\n* see assumption \"How to Determine Whether Input is an Actor Object\" below\n\nIf `actor` JSON does not have a `type` property, the outcome MUST be `inapplicable`.\n\n### Test Targets\n\n* `inbox` - the inbox property (or lack thereof) in the input `actor` JSON object\n* `outbox` - the outbox property (or lack thereof) in the input `actor` JSON object\n\n## Expectations\n\n1. `actor` JSON has a property whose name is `inbox`.\n    * If there is no `inbox` property, the outcome is `failed` for test target `inbox`\n\n2. `actor` JSON has a property whose name is `outbox`\n    * If there is no `outbox` property, the outcome is `failed` for test target `outbox`\n\n## Assumptions\n\n### 1. How to Determine Whether Input is an Actor Object\n\nFor the purposes of determining whether the input `actor` is in fact an actor object and whether this test is applicable to that input,\nthis test assumes that a valid way of determining whether the input is an Actor Object is:\n\n* the input is valid JSON\n* the input, once parsed as JSON\n    * has a `type` property\n    * has a `type` property value that is either a string or array of strings\n    * one of those `type` value strings is one of `Application`, `Group`, `Organization`, `Person`, `Service`\n        * rationale:\n            * [ActivityPub 4.3 Actors](https://www.w3.org/TR/activitystreams-core/#actors):\n              > The Activity Vocabulary provides the normative definition of five specific types of Actors: Application | Group | Organization | Person | Service.\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n\n### 2. No property value expectations\n\nThis test does not assert any expectations about the *values* of these required properties. The requirement being tested only requires that they are present.\n\nFor test cases about the values of these properties, see:\n\n* [inbox-must-be-an-orderedcollection](https://socialweb.coop/activitypub/test-cases/inbox-must-be-an-orderedcollection/)\n* [outbox-must-be-an-orderedcollection](https://socialweb.coop/activitypub/test-cases/outbox-must-be-an-orderedcollection/)\n\n## Test Cases\n\nThese are test cases for this test case, and can be used to verify that an implementation of this test case specification will be [consistent](https://www.w3.org/WAI/standards-guidelines/act/implementations/#understanding-act-consistency) with other implementations.\n\n### simple valid actor\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"inbox\": \"https://bengo.is/inbox\",\n      \"outbox\": \"https://bengo.is/outbox\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `passed`\n* `outbox`: present\n    * outcome: `passed`\n\n### missing inbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"outbox\": \"https://bengo.is/outbox\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n* `outbox`: present\n    * outcome: `passed`\n\n### missing outbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\",\n      \"inbox\": \"https://bengo.is/inbox\",\n    }\n    ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `passed`\n* `outbox`: not present\n    * outcome: `failed`\n\n### actor type, but no inbox nor outbox\n\ninputs\n\n* `actor`\n\n  ```json\n    {\n      \"type\": \"Person\"\n    }\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n* `outbox`: not present\n    * outcome: `failed`\n\n### not json\n\ninputs\n\n* `actor`\n\n  ```text\n  abc\n  ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `inapplicable`\n* `outbox`: not present\n    * outcome: `inapplicable`\n\n### json with no properties\n\ninputs\n\n* `actor`\n\n    ```json\n    {}\n    ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object\n* `outbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object\n\n### actor with no other properties\n\ninputs\n\n* `actor`\n\n  ```json\n  {\n    \"type\": \"Person\"\n  }\n  ```\n\ntest targets\n\n* `inbox`: not present\n    * outcome: `failed`\n        * rationale: the object is an Actor, but is missing the required property\n* `outbox`: not present\n    * outcome: `failed`\n        * rationale: the object is an Actor, but is missing the required property\n\n### object with `inbox` but no `type`\n\ninputs\n\n* `actor`\n\n  ```json\n  {\n    \"inbox\": \"https://bengo.is/inbox\"\n  }\n  ```\n\ntest targets\n\n* `inbox`: present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object. There is no `type` property indicating this is an Actor Object.\n* `outbox`: not present\n    * outcome: `inapplicable`\n        * because it is not an Actor Object. There is no `type` property indicating this is an Actor Object.\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject.\nAn outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub requirement cc3f730a-37a9-4af9-948f-7c8a0b7f6c41](https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/) - actor objects must have an inbox property\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `inbox` has outcome `passed`, requirement is satisfied\n        * when test target `inbox` has outcome `failed`, requirement is not satisfied\n        * when test target `inbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n* [ActivityPub requirement b8647b47-defb-483c-b468-8602d1124169](https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/) - actor objects must have an outbox property\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping for test target `outbox`\n        * when test target `outbox` has outcome `passed`, requirement is satisfied\n        * when test target `outbox` has outcome `failed`, requirement is not satisfied\n        * when test target `outbox` has outcome inapplicable, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* 2023-11-03T03:41:20.725Z - first draft\n* 2023-12-29T20:48:45.838Z - update test cases with consistent formatting, headings, and fix some mistakes\n\n## Issues List\n",
      "applicability": "This test applies directly to the `actor` input.\nIf `actor` is not an Actor Object, the outcome MUST be `inapplicable`.\n* see assumption \"How to Determine Whether Input is an Actor Object\" below\nIf `actor` JSON does not have a `type` property, the outcome MUST be `inapplicable`.\n### Test Targets\n* `inbox` - the inbox property (or lack thereof) in the input `actor` JSON object\n* `outbox` - the outbox property (or lack thereof) in the input `actor` JSON object\n",
      "assumptions": "### 1. How to Determine Whether Input is an Actor Object\nFor the purposes of determining whether the input `actor` is in fact an actor object and whether this test is applicable to that input,\nthis test assumes that a valid way of determining whether the input is an Actor Object is:\n* the input is valid JSON\n* the input, once parsed as JSON\n    * has a `type` property\n    * has a `type` property value that is either a string or array of strings\n    * one of those `type` value strings is one of `Application`, `Group`, `Organization`, `Person`, `Service`\n        * rationale:\n",
      "expectations": "1. `actor` JSON has a property whose name is `inbox`.\n    * If there is no `inbox` property, the outcome is `failed` for test target `inbox`\n2. `actor` JSON has a property whose name is `outbox`\n    * If there is no `outbox` property, the outcome is `failed` for test target `outbox`\n",
      "name": "Actor Objects Must Have Properties inbox and outbox",
      "passedCases": [
        {
          "name": "simple valid actor",
          "input": {
            "actor": {
              "type": "Person",
              "inbox": "https://bengo.is/inbox",
              "outbox": "https://bengo.is/outbox"
            }
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "slug": "actor-objects-must-have-inbox-outbox-properties",
      "uuid": "acaacb5f-8f7e-4f28-8d81-c7955070a767",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:cc3f730a-37a9-4af9-948f-7c8a0b7f6c41",
          "url": "https://socialweb.coop/activitypub/behaviors/cc3f730a-37a9-4af9-948f-7c8a0b7f6c41/"
        },
        {
          "id": "urn:uuid:b8647b47-defb-483c-b468-8602d1124169",
          "url": "https://socialweb.coop/activitypub/behaviors/b8647b47-defb-483c-b468-8602d1124169/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:b8647b47-defb-483c-b468-8602d1124169",
  "type": "Behavior",
  "uuid": "b8647b47-defb-483c-b468-8602d1124169",
  "content": "actor objects must have an outbox property",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#actor-objects",
      "branch": [
        4,
        1
      ]
    },
    "selector": {
      "type": "List",
      "items": [
        {
          "type": "TextQuoteSelector",
          "exact": "Actor objects MUST have, in addition to the properties mandated by 3.1 Object Identifiers, the following properties:\n",
          "prefix": "4.1 Actor objects\n",
          "suffix": "inbox\n  A reference to an [ActivityStreams] OrderedCollection comprised of all the messages received by the actor; see 5.2 Inbox.\n"
        },
        {
          "type": "TextQuoteSelector",
          "exact": "outbox\n  An [ActivityStreams] OrderedCollection comprised of all the messages produced by the actor; see 5.1 Outbox.\n",
          "prefix": "received by the actor; see 5.2 Inbox.\n",
          "suffix": "Implementations SHOULD, in addition, provide the following properties:\n"
        }
      ]
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Servers... unless the activity is transient, MUST include the new id in the Location header.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:00330762-59a2-4072-8d93-87ee4c30411c",
  "type": "Behavior",
  "uuid": "00330762-59a2-4072-8d93-87ee4c30411c",
  "content": "Servers... unless the activity is transient, MUST include the new id in the Location header.",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "server responding to client posting Activities to an actor's outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Servers MUST return a 201 Created HTTP code, and \n",
      "exact": "unless the activity is transient, MUST include the new id in the Location header.\n",
      "suffix": "Example 12: Outbox response to submitted Activity\n"
    },
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Origin servers sending publicly addressed activities to sharedInbox endpoints MUST still deliver to actors and collections otherwise addressed (through to, bto, cc, bcc, and audience) which do not have a sharedInbox and would not otherwise receive the activity through the sharedInbox mechanism.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:08ed852d-f36c-47ba-992b-b211e35dbdca",
  "uuid": "08ed852d-f36c-47ba-992b-b211e35dbdca",
  "type": "Behavior",
  "content": "Origin servers sending publicly addressed activities to sharedInbox endpoints MUST still deliver to actors and collections otherwise addressed (through to, bto, cc, bcc, and audience) which do not have a sharedInbox and would not otherwise receive the activity through the sharedInbox mechanism.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    },
    {
      "name": "7.1.3 Shared Inbox Delivery",
      "id": "https://www.w3.org/TR/activitypub/#shared-inbox-delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#shared-inbox-delivery",
      "branch": [
        7,
        1,
        3
      ]
    },
    "selector": {
      "prefix": "Additionally, if an object is addressed to the Public special collection, a server MAY deliver that object to all known sharedInbox endpoints on the network.\n",
      "exact": "Origin servers sending publicly addressed activities to sharedInbox endpoints MUST still deliver to actors and collections otherwise addressed (through to, bto, cc, bcc, and audience) which do not have a sharedInbox and would not otherwise receive the activity through the sharedInbox mechanism.\n",
      "suffix": "7.2 Create Activity\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Any to, bto, cc, bcc, and audience properties specified on the object MUST be copied over to the new Create activity by the server.

Tags
ActivityPubServer
Test Cases
  • Outbox Wraps Object With Create Activity
    JSON
    {
      "name": "Outbox Wraps Object With Create Activity",
      "description": "a test rule that submits an object to an outbox and checks for conformance with requirements for how the outbox server must wrap the object in a Create Activity.",
      "slug": "outbox-wraps-object-with-create-checked-using-get-location",
      "input": {
        "outbox": {
          "help": "URL of ActivityPub outbox that an object will be sent to",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value that will be sent with ActivityPub requests",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "applicability": "\nThis test applies to an ActivityPub Server handling POST requests to an ActivityPub Outbox.\n\nTest Targets\n* outbox: same value as input.outbox\n",
      "inapplicableCases": [],
      "passedCases": [],
      "failedCases": [],
      "uuid": "963a2313-788e-4bb7-b6ea-0d8bf7d255ef",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:e6d00349-6d07-4512-9a99-f698d3bc1dba",
          "url": "https://activitypub-testing.socialweb.coop/e6d00349-6d07-4512-9a99-f698d3bc1dba"
        },
        {
          "id": "urn:uuid:104e2cde-f072-43af-badd-bf9bd4129151",
          "url": "https://activitypub-testing.socialweb.coop/104e2cde-f072-43af-badd-bf9bd4129151"
        },
        {
          "id": "urn:uuid:f3725db1-1e6b-4d20-9cea-a49ef4291e9f",
          "url": "https://activitypub-testing.socialweb.coop/f3725db1-1e6b-4d20-9cea-a49ef4291e9f"
        }
      ],
      "testCases": [
        {
          "name": "outbox=https://socialweb.coop/outbox",
          "id": "urn:uuid:d7c8b377-2850-481b-992f-92ac437a9f3c",
          "input": {
            "outbox": "https://socialweb.coop/outbox"
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "outbox=https://example.com",
          "id": "urn:uuid:dde952d7-b9b0-488f-8b06-61a10ba134d4",
          "input": {
            "outbox": "https://example.com/"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "expectations": "\n* target `outboxPostLocationResponse` http request body must be a JSON string that parses to a JSON Object called `outboxPostResult`\n* `outboxPostResult` must have a `type` property whose value is either the string \"Create\" or an Array containing the string \"Create\"\n* `outboxPostResult` must have a `object` property whose value includes the object from test target `submission` (i.e. the object that was sent to the outbox, and should have been wrapped in a Create).\n* `outboxPostResult` must have a `id` property whose value is a URI\n* `outboxPostResult.object` must have a `id` property whose value is a URI\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult`\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult.object`\n"
    }
JSON
{
  "id": "urn:uuid:f3725db1-1e6b-4d20-9cea-a49ef4291e9f",
  "uuid": "f3725db1-1e6b-4d20-9cea-a49ef4291e9f",
  "type": "Behavior",
  "content": "Any to, bto, cc, bcc, and audience properties specified on the object MUST be copied over to the new Create activity by the server.\n",
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6.2 Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#create-activity-outbox"
    },
    {
      "name": "6.2.1 Object creation without a Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#object-without-create"
    }
  ],
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#object-without-create",
      "name": "Object creation without a Create Activity",
      "branch": [
        6,
        2,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The Location value returned by the server should be the URL of the new Create activity (rather than the object). \n",
      "exact": "Any to, bto, cc, bcc, and audience properties specified on the object MUST be copied over to the new Create activity by the server.\n",
      "suffix": "Example 15: Object with audience targeting\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Implementations MUST NOT deliver to the "public" special collection

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:c67a83bd-3f47-44cc-af3a-38f176a67a18",
  "type": "Behavior",
  "uuid": "c67a83bd-3f47-44cc-af3a-38f176a67a18",
  "content": "Implementations MUST NOT deliver to the \"public\" special collection\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#public-addressing",
      "branch": [
        5,
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Activities addressed to this special URI shall be accessible to all users, without authentication.\n",
      "exact": "Implementations MUST NOT deliver to the \"public\" special collection\n",
      "suffix": "; it is not capable of receiving actual activities.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Servers MUST return a 201 Created HTTP code

Tags
ActivityPubServer
Test Cases
  • Outbox Servers handling activity submissions MUST return a 201 created HTTP status code
    JSON
    {
      "description": "This test checks that an ActivityPub Outbox responds with a 201 status code when sent a POST request with an Activity submission.",
      "failedCases": [
        {
          "name": "with authorization, response status 403 Forbidden",
          "input": {
            "outbox": "https://socialweb.coop/activitypub/testing/utilities/response?status=403",
            "submission": "{\"type\":\"Create\"}",
            "authorization": "Bearer foo"
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "non-URI input `id`",
          "input": {
            "outbox": "bafybeib5mvfjatmpswc3jnh7ydz4zxe25cm63xp6aafpg3j2awakf63qma",
            "submission": "{\"type\":\"Create\"}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "outbox": {
          "help": "the url to the Outbox handling an Activity POST request",
          "required": true,
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value to include in outbox POST request",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        },
        "time": {
          "help": "amount of time allowed to run test, as IETF RFC3339 dur-time time duration. This is meant to configure the limit for how long this test will wait for network requests.",
          "required": true,
          "default": "T5S",
          "type": [
            "rfc3339:dur-time",
            "TimeLimit"
          ],
          "constraints": [
            {
              "content": " MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)",
              "mediaType": "test/markdown"
            }
          ]
        }
      },
      "markdown": "---\n\nuuid: 723afcbb-118d-433e-8ab4-560ffca93582\n\ntype:\n- TestCase\n\nname: |\n  Outbox Servers handling activity submissions MUST return a 201 created HTTP status code\n\nslug: |\n  outbox-post-servers-must-return-a-201-created-http-code\n\ndescription: |\n  This test checks that an ActivityPub Outbox responds with a 201 status code when sent a POST request with an Activity submission.\n\ntestsRequirement:\n- id: urn:uuid:3b925cdf-89fe-4f51-b41f-26df23f58e0c\n  url: https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c\n  name: Servers MUST return a 201 Created HTTP code\n\n# https://www.w3.org/QA/WG/2005/01/test-faq#review\n# *submitted, accepted, reviewed*, *returned for revision*, or *rejected*\ntestCaseState: submitted\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# Outbox Servers handling activity submissions MUST return a 201 created HTTP status code\n\n## Background\n\nIn the context of [client-to-server interactions](https://www.w3.org/TR/activitypub/#client-to-server-interactions) via Activity submission, [ActivityPub][activitypub] says\n> Servers MUST return a 201 Created HTTP code\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n\n## About this Test\n\nThis is a Test describing a rule to determine whether an ActivityPub Server is in conformance with the following behaviors required by [ActivityPub][activitypub]:\n\n* Servers MUST return a 201 Created HTTP code aka [requirement 3b925cdf-89fe-4f51-b41f-26df23f58e0c](https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c)\n\n### Identifier\n\nThe identifier of this test is `723afcbb-118d-433e-8ab4-560ffca93582`.\n\n## Test Subject\n\nThe subject of this test is an ActivityPub Server serving activity submission requests for an ActivityPub Outbox.\n\nThe Test Subject can be identified by a URI for an ActivityPub Outbox. The ActivityPub Outbox has an `id` property and the ActivityPub server is responsible for responding to requests sent to it.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n1. `outbox` - identifier of an ActivityPub Outbox\n    * required: yes\n    * type: binary\n      * constraints\n        * MUST be a URI, i.e. an [ActivityPub Object Identifier](https://www.w3.org/TR/activitypub/#obj-id) that is not `null`\n\n2. `authorization` - proof of authorization to submit an activity to the outbox identified by input `outbox`\n    * required: no\n      * if this input is omitted, no `Authorization` header will be provided in the HTTP request send by this test\n    * type: binary\n      * constraints\n        * If present, this should be valid as the value of an HTTP `Authorization` header\n\n3. `submission` - ActivityPub message that will be submitted to the Outbox.\n    * required: no\n      * if this input is omitted, the test will use a simple valid submission\n    * type: binary\n      * constraints\n        * should be JSON\n      * note\n        * this will be sent as the HTTP request body of an ActivityPub [client-to-server](https://www.w3.org/TR/activitypub/#client-to-server-interactions) request with header `Content-Type: application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`.\n\n## Applicability\n\nThis test applies to the server hosting the ActivityPub Outbox identified by input `outbox` and that support requests using HTTP method `POST`.\n\nIf input `outbox` is not a URI, outcome is `inapplicable`.\n\nIf input `outbox` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes).\n\nIf the server indicates it does not support `POST` requests, the test outcome MUST be `inapplicable`.\n\nIf the server indicates that the provided input includes insufficient proof of authorization to write to the outbox, the test outcome MUST be a [`CannotTell`](https://www.w3.org/TR/EARL10-Schema/#CannotTell).\n\n### Test Targets\n\n* `response` - the HTTP response that is the result of submitting the input `submission` to the ActivityPub Outbox identified by input `outbox`.\n    * how to derive `response` from inputs\n      1. start a timer with duration from input `time`. If the timer reaches zero before this derivation is complete, the whole test outcome is `inapplicable` because we weren't able to determine the `response` test target within the required time.\n      2. let `request` be a new HTTP Request whose URI is input `outbox`\n      3. add an http request header to `request` whose name is `Content-Type` and whose value is `application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"`\n      4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization`\n      5. let `submission` be the input `submission`, if provided, otherwise use the json value\n\n          ```json\n          {\n            \"@context\": \"https://www.w3.org/ns/activitystreams\",\n            \"type\": \"Note\",\n            \"content\": \"Say, did you finish reading that book I lent you?\"\n          }\n          ```\n\n      6. let the http request body of `request` be the value of `submission`\n      7. send the HTTP request and await a response\n      8. assign the HTTP response to the `response` test target\n      9. If the status code of `response` is 401, the test outcome MUST be a [`CannotTell`](https://www.w3.org/TR/EARL10-Schema/#CannotTell)\n      10. If the status code of `response` is 403,\n          * if input `authorization` was provided, the test outcome MAY be [`Fail`](https://www.w3.org/TR/EARL10-Schema/#Fail)\n          * else (input `authorization` was not provided) the test outcome MUST be a [`CannotTell`](https://www.w3.org/TR/EARL10-Schema/#CannotTell)\n      11. If the status code of `response` is 405 (e.g. 'Method Not Allowed'), the test outcome MUST be `inapplicable`\n          * rationale: The server is explicitly indicating it does not support requests with method `POST`, and this test is only applicable to servers that do.\n\n## Expectations\n\n* `response` http status code is `201`\n\n## Assumptions\n\n### Default Outbox Submission\n\nIf an input `submission` is not provided, the test will use an activity based on Example 2 in [ActivityPub](https://www.w3.org/TR/activitypub/). If this causes problems, provide an input `submission` that will work to produce a 201 response status code and satisfy the requirement being tested.\n\n### 403 Forbidden Semantics Varies on Authorization\n\nWhen interpreting a HTTP 403 response resulting sending a POST request to the input `outbox`, this test varies its outcome depending on whether an input `authorization` was provided to the test.\n\nA rationale is that HTTP semantics in general defines 403 to mean different things depending on whether authentication credentials were provided in the request:\n\n> The 403 (Forbidden) status code indicates that the server understood the request but refuses to fulfill it. A server that wishes to make public why the request has been forbidden can describe that reason in the response content (if any).\n>\n> If authentication credentials were provided in the request, the server considers them insufficient to grant access. The client SHOULD NOT automatically repeat the request with the same credentials. The client MAY repeat the request with new or different credentials. However, a request might be forbidden for reasons unrelated to the credentials.\n\n<cite><a href=\"https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden\">RFC9110 HTTP Semantics</a></cite>\n\nWhen authentication credentials are provided, and the server returns 403, the test outcome is `failed`. This is based on \"If authentication credentials were provided in the request, the server considers them insufficient to grant access\" from RFC9110 above. `failed` seems appropriate *for the provided inputs* because status 403 communicates that the server received the provided credentials, determined they cannot authorize the request, and that the client SHOULD NOT retrying the request with the same credentials. `failed` should draw attention to either flawed authorization logic or, more likely, an inappropriate `authorization` input passed to the test.\n\nIf no input `authorization` is provided, the test outcome is `CannotTell`. This is because there isn't enough evidence for another outcome, and it's best for a human to take a look at the rest of the result and decide what to do next.\n\nServer implementers are encouraged to review [RFC7235 for HTTP Authentication](https://datatracker.ietf.org/doc/html/rfc7235), which describes a way of responding to requests that lack proof of authorization by using a `401` status code + hints about what authorization is required.\n\n## Test Cases\n\nThese are test cases for this test itself, i.e. each test case is a set of test inputs and the corresponding test results.\n\n### simple passed case\n\ninputs\n\n* `outbox`\n\n    ```url\n    https://socialweb.coop/activitypub/testing/utilities/response?status=201`\n    ```\n\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`\n\n    ```http\n    HTTP/2 201 \n    content-type: application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\n\n    {}\n    ```\n\n    * outcome: `passed`\n\n### nginx 404 response body\n\ninputs\n\n* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=404`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/2 404 \n    content-type: text/html; charset=UTF-8\n    content-length: 153\n\n    <html>\n    <head><title>404 Not Found</title></head>\n    <body>\n    <center><h1>404 Not Found</h1></center>\n    <hr><center>nginx/1.25.2</center>\n    </body>\n    </html>\n    ```\n\n    * outcome: `cantTell`\n\n### non-URI input `outbox`\n\ninputs\n\n* `outbox`: `bafybeib5mvfjatmpswc3jnh7ydz4zxe25cm63xp6aafpg3j2awakf63qma`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\nresult\n\n* outcome: `inapplicable`\n    * rationale: input `outbox` is not a URI\n\n### without authorization, response status 401\n\ninputs\n\n* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=401`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/1.1 401 Forbidden\n    \n    401\n    ```\n\nresult\n\n* outcome: [`cantTell`](https://www.w3.org/TR/EARL10-Schema/#cantTell)\n\n### response status 405 Method Not Allowed\n\ninputs\n\n* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=405`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/1.1 405 Not Allowed\n    \n    405\n    ```\n\n* outcome: `inapplicable`\n    * rationale: input `response` status 405 indicates that requests with method `POST` are not supported, so this test does not apply (see 'Applicability')\n\n### without authorization, response status 403 Forbidden\n\ninputs\n\n* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=403`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/1.1 403 Forbidden\n    \n    403\n    ```\n\nresult\n\n* outcome: [`cantTell`](https://www.w3.org/TR/EARL10-Schema/#cantTell)\n\n### with authorization, response status 403 Forbidden\n\ninputs\n\n* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=403`\n* `authorization`: `Bearer foo`\n* `submission`\n\n    ```json\n    {\n      \"type\": \"Create\"\n    }\n    ```\n\ntest targets\n\n* `response`:\n\n    ```http\n    HTTP/1.1 403 Forbidden\n    \n    403\n    ```\n\nresult\n\n* outcome: [`failed`](https://www.w3.org/TR/EARL10-Schema/#failed)\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub requirement 3b925cdf-89fe-4f51-b41f-26df23f58e0c](https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c) - Servers MUST return a 201 Created HTTP code\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `response` has outcome `passed`, further testing is needed to determine requirement satisfaction\n        * when test target `response` has outcome `failed`, requirement is not satisfied\n        * when test target `response` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Issues\n\n* [x] host an example outbox that can be used as the `passed` test case for this\n    * it should always respond with http response 201\n    * 2023-12-22T18:20:55.076Z: done: <https://socialweb.coop/activitypub/testing/utilities/response?status=201>\n\n## Change Log\n\n* 2024-01-04T00:25:23.095Z - add support for read-only outboxes - i.e. a response with status 405 MUST result in an `inapplicable` test outcome.\n* 2024-01-07T21:39:50.838Z - add support for http responses with status 401, 403, 404\n",
      "name": "Outbox Servers handling activity submissions MUST return a 201 created HTTP status code",
      "passedCases": [
        {
          "name": "1. simple passed case",
          "input": {
            "outbox": "https://socialweb.coop/activitypub/testing/utilities/response?status=201",
            "submission": "{\"type\":\"Create\"}"
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "slug": "outbox-post-servers-must-return-a-201-created-http-code",
      "uuid": "723afcbb-118d-433e-8ab4-560ffca93582",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:3b925cdf-89fe-4f51-b41f-26df23f58e0c",
          "url": "https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:3b925cdf-89fe-4f51-b41f-26df23f58e0c",
  "type": "Behavior",
  "uuid": "3b925cdf-89fe-4f51-b41f-26df23f58e0c",
  "content": "Servers MUST return a 201 Created HTTP code\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "server responding to client posting Activities to an actor's outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "If an Activity is submitted with a value in the id property, servers MUST ignore this and generate a new id for the Activity. \n",
      "exact": "Servers MUST return a 201 Created HTTP code\n",
      "suffix": ", and unless the activity is transient, MUST include the new id in the Location header.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

If the object of a Reject received to an inbox is a Follow activity previously sent by the receiver, this means the recipient did not approve the Follow request. The server MUST NOT add the actor to the receiver's Following Collection.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:f2e7a703-c115-49c9-a7c5-07aa6a2ebf53",
  "uuid": "f2e7a703-c115-49c9-a7c5-07aa6a2ebf53",
  "type": "Behavior",
  "content": "If the object of a Reject received to an inbox is a Follow activity previously sent by the receiver, this means the recipient did not approve the Follow request. The server MUST NOT add the actor to the receiver's Following Collection. \n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.7 Reject Activity",
      "id": "https://www.w3.org/TR/activitypub/#reject-activity-inbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#reject-activity-inbox",
      "branch": [
        7,
        7
      ]
    },
    "selector": {
      "prefix": "The side effect of receiving this in an inbox is determined by the type of the object received, and it is possible to reject types not described in this document (for example, an Offer).\n",
      "exact": "If the object of a Reject received to an inbox is a Follow activity previously sent by the receiver, this means the recipient did not approve the Follow request. The server MUST NOT add the actor to the receiver's Following Collection.\n",
      "suffix": "7.8 Add Activity\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The likes collection MUST be either an OrderedCollection or a Collection

Tags
ActivityPubServer
Test Cases
  • An ActivityPub Object `likes` Collection Must be a Collection
    JSON
    {
      "input": {
        "object": {
          "help": "object with a likes property",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\n\ntype:\n- TestCase\ntags:\n- tests-activitypub-object\nuuid:\n- 200b9bc8-aae3-46f2-a6ab-5366042c0f6e\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# An ActivityPub Object `likes` Collection Must be a Collection\n\n## Background\n\n[ActivityPub][activitypub] says\n> Every object MAY have a likes collection.\n> &hellip;\n> The likes collection MUST be either an OrderedCollection or a Collection\n\n## About This Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub Object is in partial conformance with the following behaviors required by [ActivityPub][activitypub].\n\n* [requirement f965e989-4084-4f9d-9119-6a7ea13bcb64](https://socialweb.coop/activitypub/behaviors/f965e989-4084-4f9d-9119-6a7ea13bcb64/) - The likes collection MUST be either an OrderedCollection or a Collection\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:200b9bc8-aae3-46f2-a6ab-5366042c0f6e`.\n\n## Test Subject\n\nThe subject of this test is an ActivityPub Object.\n\n## Input\n\nThis test requires the following [inputs](act-rules-input) indicating parts of the test subject:\n\n### `input.object`\n\nthe object whose `likes` property will be tested\n\n* type: binary\n* constraints\n    * should be JSON\n\n## Applicability\n\nThis test applies to each of the likes collections derived from the values of the property on `object` named `likes`.\n\n### Prerequisites\n\n* `object` is a JSON object\n* `object` JSON object has a property named `likes`\n\n### Test Targets\n\nHow to Derive Test Targets from Input\n\n* let `targets` be a set\n* let `likesValues` be the result of\n    * if `object.likes` is an Array, return it\n    * else return an Array whose only item is the value of `object.likes`\n* for each item in `likesValues`\n    * if item is a string\n        * let itemFetched be the result of interpreting the string as an ActivityPub Object Identifier and fetching the corresponding ActivityPub Object.\n        * add itemFetched to `targets`\n    * else add item to `targets`\n* return `targets`\n\n## Expectations\n\nFor every target `target`\n\n* `target` is a JSON object\n* `target` has a property named `type`\n* the value of the `target`'s `type` property must be one of\n    * an array containing the string \"Collection\"\n    * an array containing the string \"OrderedCollection\"\n    * the string \"Collection\"\n    * the string \"OrderedCollection\"\n\n## Test Cases\n\nWhat follows are some specific cases of applything this test rule.\n\n### Passed Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": {\n      \"type\": \"Collection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"Collection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 2\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": {\n      \"type\": \"OrderedCollection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"OrderedCollection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 3\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": {\n      \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"likes\": {\n      \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n    }\n  }\n  ```\n\n    * outcome: `passed`\n        * rationale: `\"FancyCollection\"` is not well-defined here, but this object still passes the test because at least one of the `type` property values is `OrderedCollection` and thus satisfies the requirement as interpreted.\n\n### Failed Example 1 - Likes is a number\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": 1\n  }\n  ```\n\ntest targets\n\n* ```json\n  1\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 2 - Likes is an empty object\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": {}\n  }\n  ```\n\ntest targets\n\n* ```json\n  {}\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 3 - Likes object type is array including only `\"Link\"`\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"likes\": {\n      \"type\": [\"Link\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"Link\"]\n  }\n  ```\n\n    * outcome: `failed`\n\n### Inapplicable Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  1\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n### Inapplicable Example 2 - object has no likes property\n\ninputs\n\n* `object`:\n\n  ```json\n  { \"foo\": \"bar\" }\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n### Inapplicable Example 3 - object is html\n\ninputs\n\n* `object`:\n\n  ```html\n  <!doctype html>hi\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/#glossary)\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/#accessibility-requirements-mapping)\n\n* [ActivityPub requirement f965e989-4084-4f9d-9119-6a7ea13bcb64](https://socialweb.coop/activitypub/behaviors/f965e989-4084-4f9d-9119-6a7ea13bcb64/) - The likes collection MUST be either an OrderedCollection or a Collection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when every test target has outcome `passed`, the requirement is satisfied by the test subject\n        * when any test target has outcome `failed`, the requirement is not satisfied by the test subject\n        * when any test target has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Issues [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/issues-list)\n\n## Change Log\n\n* 2023-12-06T06:48:14.689Z: init\n* 2023-12-18T04:33:22.670Z: add more example cases that were useful for maximizing implementation code coverage\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n",
      "name": "An ActivityPub Object `likes` Collection Must be a Collection",
      "description": "tests whether an ActivityPub Object has a likes collection with an appropriate Collection type",
      "slug": "likes-collection-must-be-a-collection",
      "inapplicableCases": [
        {
          "name": "inapplicable example 1",
          "input": {
            "object": "1"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "inapplicble example 2 - object has no likes property",
          "input": {
            "object": "\n      { \"foo\": \"bar\" }\n      "
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "inapplicable example 3 - html object",
          "input": {
            "object": "<!doctype html>hi"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "passedCases": [
        {
          "name": "passed example 1",
          "input": {
            "object": "\n        {\n          \"likes\": {\n            \"type\": \"Collection\"\n          }\n        }\n      "
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "passed example 2",
          "input": {
            "object": "\n      {\n        \"likes\": {\n          \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n        }\n      }\n      "
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "failedCases": [
        {
          "name": "failed example 1",
          "input": {
            "object": "\n        {\n          \"likes\": 1\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "failed example 2",
          "input": {
            "object": "\n        {\n          \"likes\": {}\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "failed example 3",
          "input": {
            "object": "\n        {\n          \"likes\": {\n            \"type\": [\"Link\"]\n          }\n        }\n      "
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "uuid": "200b9bc8-aae3-46f2-a6ab-5366042c0f6e",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:f965e989-4084-4f9d-9119-6a7ea13bcb64",
          "url": "https://socialweb.coop/activitypub/behaviors/f965e989-4084-4f9d-9119-6a7ea13bcb64/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:f965e989-4084-4f9d-9119-6a7ea13bcb64",
  "type": "Behavior",
  "uuid": "f965e989-4084-4f9d-9119-6a7ea13bcb64",
  "content": "The likes collection MUST be either an OrderedCollection or a Collection\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#likes",
      "branch": [
        5,
        7
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Every object MAY have a likes collection. This is a list of all Like activities with this object as the object property, added as a side effect.\n",
      "exact": "The likes collection MUST be either an OrderedCollection or a Collection\n",
      "suffix": "and MAY be filtered on privileges of an authenticated user or as appropriate when no authentication is given.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

When Activities are received in the inbox, ...the server MUST target and deliver to the values of to, cc, and/or audience if and only if all of the following are true: This is the first time the server has seen this Activity. The values of to, cc, and/or audience contain a Collection owned by the server. The values of inReplyTo, object, target and/or tag are objects owned by the server.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:a26b534a-1c9f-42d8-b30e-3790561caa14",
  "uuid": "a26b534a-1c9f-42d8-b30e-3790561caa14",
  "type": "Behavior",
  "content": "When Activities are received in the inbox, ...the server MUST target and deliver to the values of to, cc, and/or audience if and only if all of the following are true:\n\n  This is the first time the server has seen this Activity.\n  The values of to, cc, and/or audience contain a Collection owned by the server.\n  The values of inReplyTo, object, target and/or tag are objects owned by the server.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    },
    {
      "name": "7.1.2 Forwarding from Inbox",
      "id": "https://www.w3.org/TR/activitypub/#inbox-forwarding"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#inbox-forwarding",
      "branch": [
        7,
        1,
        2
      ]
    },
    "selector": {
      "prefix": "When Activities are received in the inbox, the server needs to forward these to recipients that the origin was unable to deliver them to. To do this, \n",
      "exact": "the server MUST target and deliver to the values of to, cc, and/or audience if and only if all of the following are true:\n\nThis is the first time the server has seen this Activity.\nThe values of to, cc, and/or audience contain a Collection owned by the server.\nThe values of inReplyTo, object, target and/or tag are objects owned by the server.\n",
      "suffix": "The server SHOULD recurse through these values to look for linked objects owned by the server, and SHOULD set a maximum limit for recursion (ie. the point at which the thread is so deep the recipients followers may not mind if they are no longer getting updates that don't directly involve the recipient). The server MUST only target the values of to, cc, and/or audience on the original object being forwarded, and not pick up any new addressees whilst recursing through the linked objects (in case these addressees were purposefully amended by or via the client).\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

An OrderedCollection MUST be presented consistently in reverse chronological order

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:edc1ccb7-a9f7-499f-9687-186a5202b1a9",
  "type": "Behavior",
  "uuid": "edc1ccb7-a9f7-499f-9687-186a5202b1a9",
  "content": "An OrderedCollection MUST be presented consistently in reverse chronological order",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x5-collections",
      "branch": [
        5
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "An OrderedCollection MUST be presented consistently in reverse chronological order.\n",
      "prefix": "Note that some of these collections are specified to be of type OrderedCollection specifically, while others are permitted to be either a Collection or an OrderedCollection.\n",
      "suffix": "Note\n\nWhat property is used to determine the reverse chronological order is intentionally left as an implementation detail.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

In the case of a Reject, the server MUST NOT add the actor to the object actor's Followers Collection.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:4aec135c-45bb-45b2-8cee-27685b39e8ac",
  "uuid": "4aec135c-45bb-45b2-8cee-27685b39e8ac",
  "type": "Behavior",
  "content": "In the case of a Reject, the server MUST NOT add the actor to the object actor's Followers Collection.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.5 Follow Activity",
      "id": "https://www.w3.org/TR/activitypub/#follow-activity-inbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#follow-activity-inbox",
      "branch": [
        7,
        5
      ]
    },
    "selector": {
      "prefix": "In the case of receiving an Accept referencing this Follow as the object, the server SHOULD add the actor to the object actor's Followers Collection.\n",
      "exact": "In the case of a Reject, the server MUST NOT add the actor to the object actor's Followers Collection.\n",
      "suffix": "Note\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The outbox MUST be an OrderedCollection.

Tags
ActivityPubServer
Test Cases
  • outbox must be an OrderedCollection
    JSON
    {
      "description": "This rule checks whether the outbox property of an object appears to be an OrderedCollection",
      "failedCases": [
        {
          "name": "test case when the value of the outbox property is null",
          "input": {
            "object": {
              "outbox": null
            }
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "test case when outbox is an object without type OrderedCollection",
          "input": {
            "object": "{\"outbox\":{\"type\":[\"Person\"]}}"
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "inputs.object has outbox url to a non-collection",
          "input": {
            "object": "{\"outbox\":\"https://bengo.is/actor.json\"}"
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "test case when the outbox property is missing",
          "input": {
            "object": "{}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where the value of inputs.object is not JSON",
          "input": {
            "object": "\u0000x123abc_intentionallyNotJson"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where the value of of the outbox property is an array of length 2",
          "input": {
            "object": "{\"outbox\":[\"https://example.com\",\"https://example.com\"]}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where outbox property value is a URL that 404s when fetched",
          "input": {
            "authorization": "foo",
            "object": "{\"outbox\":\"https://bengo.is/404\"}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "object is null",
          "input": {
            "object": "null"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "object": {
          "help": "the object whose `outbox` property will be tested",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        },
        "time": {
          "help": "amount of time allowed to run test, as IETF RFC3339 dur-time time duration. This is meant to configure the limit for how long this test will wait for network requests.",
          "required": true,
          "default": "T5S",
          "type": [
            "rfc3339:dur-time",
            "TimeLimit"
          ],
          "constraints": [
            {
              "content": " MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)",
              "mediaType": "test/markdown"
            }
          ]
        }
      },
      "markdown": "---\ntype:\n- TestCase\nstatus: draft\nname: outbox must be an OrderedCollection\ndescription: |\n  This rule checks whether the outbox property of an object appears to be an OrderedCollection\nuuid: 4af549f4-3797-4d99-a151-67c3d8feaa46\nattributedTo:\n- https://bengo.is\n\"@context\":\n- TestCase:\n    \"@id\": http://www.w3.org/ns/earl#:TestCase\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# outbox must be an OrderedCollection\n\n## Background\n\n[ActivityPub](https://www.w3.org/TR/activitypub/) [§ 5.1 Outbox](https://www.w3.org/TR/activitypub/#outbox):\n> The outbox is discovered through the outbox property of an actor's profile. The outbox MUST be an OrderedCollection.\n\n## About this Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub object has an outbox property that meets this requirement to 'be an OrderedCollection', meeting requirement [3b925cdf-89fe-4f51-b41f-26df23f58e0c](https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c)\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:4af549f4-3797-4d99-a151-67c3d8feaa46\n`.\n\n## Test Subject\n\nThe subject of this test is any data claiming to conform to the specification of an ActivityPub Actor Object.\n\nThis test is *not* inherently applicable to an ActivityPub Server. An ActivityPub Server serves 0 or more Actor Objects. An ActivityPub Server for a big community might serve hundreds of ActivityPub Actor Objects. An ActivityPub Server for a single human may serve only that person's ActivityPub Actor Object.\n\nThis test is *not* inherently applicable to a URL of an Actor Object. The URL is not the same as the Actor Object. The URL may resolve to different Actor Objects in different contexts.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n1. `object` - the object whose `outbox` property will be tested\n    * type: binary\n    * constraints\n        * will be interpreted as JSON\n\n2. `time` - amount of time allowed to run test. This is meant to configure the limit for how long this test will wait for network requests.\n    * required: yes\n    * example: `T0.0021S`\n    * type: binary\n    * constraints\n        * MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)\n\n## Applicability\n\nThis test applies to outbox objects linked to from the `object` input. Usually an actor will have one outbox, but there may be multiple.\n\nThis test does not apply to objects with more than one outbox . If the input `object` has an outbox property whose value is an array of length >= 2, outcome is `inapplicable`.\n(See issue #5102 below to track expanding the applicability)\n\nIf `object` is not parseable as JSON to a JSON object, outcome is `inapplicable`.\n\nIf `object` is a JSON object, but it does not contain a property named `outbox`, outcome is `inapplicable`. The rationale is that there is already a test `acaacb5f-8f7e-4f28-8d81-c7955070a767` that can be used to determine if input `object` has an outbox property at all.\n\n### Test Targets\n\n* `outbox` - the value of the `outbox` property in the input `object` object\n    * since the `object` object must be JSON for this test's applicability, the value of `JSON.parse(actor).outbox` must also be JSON\n    * how to derive `outbox` from inputs\n        1. let `outboxValue` be\n            * if `inputs.actor.outbox` is a JSON string, `outboxValue` is that string\n            * if `inputs.actor.outbox` is a JSON object, `outboxValue` is that object\n            * if `inputs.actor.outbox` is an Array of length 1, `outboxValue` is the item in that array\n            * if `inputs.actor.outbox` is an Array of length greater than 1, the test outcome is `inapplicable` (covered in 'Applicability' above)\n              * see issue #5102 below to track expanding the applicability to support outbox property values that are arrays with length greater than 1\n        2. let `outboxObject` be\n            * if `outboxValue` is a JSON object, `outboxObject` is that object\n            * if `outboxValue` is a JSON string, `outboxObject` is the result of interpreting that string as an ActivityPub Object Identifier and fetching the corresponding ActivityPub Object (e.g. `GET https://...`).\n        3. test target `outbox` is `outboxObject`\n\n## Expectations\n\n1. `outbox` is a JSON object\n2. `outbox` has a property named `type``\n3. the values of the outbox's `type` property must contain `\"OrderedCollection\"`, as determined by\n    * if value of `outbox`'s `type` property is a string, it MUST be `\"OrderedCollection\"`\n    * if value of `outbox`'s `type` property is an Array, it MUST contain an entry identical to `\"OrderedCollection\"`\n\n## Assumptions\n\n### Interpreting \"MUST be an OrderedCollection\"\n\nRevisiting the background from above,\n[ActivityPub](https://www.w3.org/TR/activitypub/) [says](https://www.w3.org/TR/activitypub/#outbox)\n> The outbox is discovered through the outbox property of an actor's profile. The outbox MUST be an OrderedCollection.\n\n\"MUST be an OrderedCollection\" is open to some interpretation here.\n\nThis test chose to interpret 'be an' to include 'indicate its type as'. The rationale is that it's easy to check and easy to implement.\n\n## Test Cases\n\n### object has simple valid outbox\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"outbox\": {\n        \"id\": \"http://example.org/blog/\",\n        \"type\": \"OrderedCollection\",\n        \"name\": \"Martin's Blog\"\n      }\n    }\n    ```\n\n    * note: this OrderedCollection is from [Example 5 in activitystreams-core](https://www.w3.org/TR/activitystreams-core/#ex2-jsonld)\n\ntest targets\n\n* `outbox`\n    * value:\n\n        ```json\n        {\n          \"id\": \"http://example.org/blog/\",\n          \"type\": \"OrderedCollection\",\n          \"name\": \"Martin's Blog\"\n        }\n        ```\n\n    * outcome: `passed`\n\n### object outbox is null\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"outbox\": null\n    }\n    ```\n\ntest targets\n\n* `outbox`\n    * value:\n\n      ```json\n      null\n      ```\n\n    * outcome: `failed`\n        * rationale: outbox is obviously not an OrderedCollection\n\n### object outbox type array contains Person\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"outbox\": {\n        \"type\": [\"Person\"]\n      }\n    }\n    ```\n\ntest targets\n\n* `outbox`\n    * value:\n\n      ```json\n      {\n        \"type\": [\"Person\"]\n      }\n      ```\n\n    * outcome: `failed`\n        * rationale: outbox type is not OrderedCollection\n\n### inapplicable because outbox has many values\n\ninputs\n\n* `object`\n\n  ```json\n  {\n    \"outbox\": [\"https://example.com\", \"https://example.com\"]\n  }\n  ```\n\ntest targest\n\n* `outbox`\n    * value:\n\n      ```json\n      [\"https://example.com\", \"https://example.com\"]\n      ```\n\n    * outcome: `inapplicable`\n        * rationale: outbox values that are arrays of length greater than 1 are not covered by this test's applicability. See issue #5102 below to track expanding the applicability\n\nresult\n\n* outcome: `inapplicable`\n\n### object is empty object\n\ninputs\n\n* `object`\n\n    ```json\n    {}\n    ```\n\nresult\n\n* outcome: `inapplicable`\n\n### object is not JSON\n\ninputs\n\n* `object`\n\n    ```text\n    \\0x123abc_intentionallyNotJson\n    ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub Requirement 003a3be2-fb58-4812-a3a3-795067254327](https://socialweb.coop/activitypub/behaviors/003a3be2-fb58-4812-a3a3-795067254327) - The outbox MUST be an OrderedCollection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `outbox` has outcome `passed`, requirement is satisfied\n        * when test target `outbox` has outcome `failed`, requirement is not satisfied\n        * when test target `outbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* 2023-11-22T05:12:58.454Z - first draft, heavily copy from ../inbox-must-be-an-orderedcollection/inbox-must-be-an-orderedcollection.md\n* 2023-12-29T22:46:03.806Z - reformat test cases to give each one its own heading\n\n## Issues\n\nRequired\n\n* add a test case where outbox is a URI that is resolved to an OrderedCollection an the outcome is \"passed\"\n    * decide what the URL will be. Does it need to publicly resolve for a long time? maybe <https://socialweb.coop/actors/empty-outbox.json>\n        * if that's not feasible, the test case could have the `outbox` test target stubbed out so at least that part could be checked\n\nNice to Have\n\n* #01d2: resolving outbox url should use inputs.time and timeout\n* #5102: expand applicability to handle cases where there are multiple outboxes\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n",
      "name": "outbox must be an OrderedCollection",
      "passedCases": [
        {
          "name": "test case when outbox is an OrderedCollection",
          "input": {
            "object": "{\"outbox\":{\"id\":\"http://example.org/blog/\",\"type\":\"OrderedCollection\",\"name\":\"Martin's Blog\"}}"
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "slug": "outbox-must-be-an-orderedcollection",
      "uuid": "4af549f4-3797-4d99-a151-67c3d8feaa46",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:003a3be2-fb58-4812-a3a3-795067254327",
          "url": "https://socialweb.coop/activitypub/behaviors/003a3be2-fb58-4812-a3a3-795067254327"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:003a3be2-fb58-4812-a3a3-795067254327",
  "type": "Behavior",
  "uuid": "003a3be2-fb58-4812-a3a3-795067254327",
  "content": "The outbox MUST be an OrderedCollection.",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#outbox",
      "branch": [
        5,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "The outbox MUST be an OrderedCollection.\n",
      "prefix": "The outbox is discovered through the outbox property of an actor's profile. \n",
      "suffix": "The outbox stream contains activities the user has published, subject to the ability of the requestor to retrieve the activity\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Servers performing delivery to the inbox or sharedInbox properties of actors on other servers MUST provide the object property in the activity: Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:c274b155-edbd-4676-bbf2-4a2499accf7e",
  "uuid": "c274b155-edbd-4676-bbf2-4a2499accf7e",
  "type": "Behavior",
  "content": "Servers performing delivery to the inbox or sharedInbox properties of actors on other servers MUST provide the object property in the activity: Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions",
      "branch": [
        7
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "an Activity being created in an actor's outbox or inbox which references another object.\n",
      "exact": "Servers performing delivery to the inbox or sharedInbox properties of actors on other servers MUST provide the object property in the activity: Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo.\n",
      "suffix": "Additionally, servers performing server to server delivery of the following activities MUST also provide the target property: Add, Remove.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The followers collection MUST be either an OrderedCollection or a Collection

Tags
ActivityPubServer
Test Cases
  • An ActivityPub Actor Object's `followers` Collection Must be a Collection
    JSON
    {
      "input": {
        "object": {
          "help": "object with a `followers` property",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\n\ntype:\n- TestCase\ntags:\n- tests-activitypub-actor\nuuid:\n- 018c3e08-611f-7e56-9f45-2fe5e4877d4e\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# An ActivityPub Object `followers` Collection Must be a Collection\n\n## Background [<sup>?</sup>][test-background]\n\n[ActivityPub][activitypub] [§ 5.3 Followers Collection](https://www.w3.org/TR/activitypub/#followers):\n\n> Every actor SHOULD have a followers collection.\n> This is a list of everyone who has sent a Follow activity for the actor,\n> added as a side effect.\n> This is where one would find a list of all the actors that are following the actor.\n> The followers collection MUST be either an OrderedCollection or a Collection\n\n## About This Test\n\nThis is a Test Case describing a rule to determine\nwhether an ActivityPub Object is in partial conformance with\nthe following behaviors required by [ActivityPub][activitypub].\n\n* [requirement abef3a0a-d3c4-4dee-a320-b28837d0bcd8][requirement-1] -\nThe followers collection MUST be either an OrderedCollection or a Collection\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:018c3e08-611f-7e56-9f45-2fe5e4877d4e`.\n\n## Test Subject\n\nThe subject of this test is an ActivityPub Actor Object.\n\n## Input [<sup>?</sup>][test-input]\n\nThis test requires the following [inputs](act-rules-input)\nindicating parts of the test subject:\n\n1. `object` - the object whose `followers` property will be tested\n    * type: binary\n    * constraints\n        * should be JSON\n\n## Applicability [<sup>?</sup>][test-applicability]\n\nThis test applies to each of the followers collections derived\nfrom the values of the property on `object` named `followers`.\n\n### Prerequisites\n\n* `object` is a JSON object\n* `object` JSON object has a property named `followers`\n\n### How to Derive Test Targets from Input\n\n* let `targets` be a set\n* let `followersValues` be the result of\n    * if `object.followers` is an Array, return it\n    * else return an Array whose only item is the value of `object.followers`\n* for each item in `followersValues`\n    * if item is a string\n        * let itemFetched be the result of interpreting the string as\n          an ActivityPub Object Identifier and fetching the corresponding\n          ActivityPub Object.\n        * add itemFetched to `targets`\n    * else add item to `targets`\n* return `targets`\n\n## Expectations [<sup>?</sup>][test-expectations]\n\nFor every target `target`\n\n* `target` is a JSON object\n* `target` has a property named `type`\n* the values of the `target`'s `type` property must be one of\n    * an array containing the string \"Collection\"\n    * an array containing the string \"OrderedCollection\"\n    * the string \"Collection\"\n    * the string \"OrderedCollection\"\n\n## Assumptions [<sup>?</sup>][test-assumptions]\n\n## Test Cases [<sup>?</sup>][test-test-cases]\n\nWhat follows are some specific cases of applything this test.\n\n### Passed Example 1 - followers has type Collection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": {\n      \"type\": \"Collection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"Collection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 2 - followers has type OrderedCollection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": {\n      \"type\": \"OrderedCollection\"\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": \"OrderedCollection\"\n  }\n  ```\n\n    * outcome: `passed`\n\n### Passed Example 3 - followers type includes OrderedCollection\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": {\n      \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"FancyCollection\", \"OrderedCollection\"]\n  }\n  ```\n\n    * outcome: `passed`\n        * rationale\n            * `\"FancyCollection\"` is not well-defined here,\nbut this object still passes the test because at least one of the `type`\nproperty values is `OrderedCollection` and thus satisfies the requirement\nas interpreted.\n\n### Failed Example 1 - followers is a number\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": 1\n  }\n  ```\n\ntest targets\n\n* ```json\n  1\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 2 - followers is an empty object\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": {}\n  }\n  ```\n\ntest targets\n\n* ```json\n  {}\n  ```\n\n    * outcome: `failed`\n\n### Failed Example 3 - followers object type is array including only `\"Link\"`\n\ninputs\n\n* `object`:\n\n  ```json\n  {\n    \"followers\": {\n      \"type\": [\"Link\"]\n    }\n  }\n  ```\n\ntest targets\n\n* ```json\n  {\n    \"type\": [\"Link\"]\n  }\n  ```\n\n    * outcome: `failed`\n\n### Inapplicable Example 1\n\ninputs\n\n* `object`:\n\n  ```json\n  1\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n### Inapplicable Example 2 - object has no followers property\n\ninputs\n\n* `object`:\n\n  ```json\n  { \"foo\": \"bar\" }\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary [<sup>?</sup>](https://www.w3.org/TR/act-rules-format/#glossary)\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping [<sup>?</sup>][act-rules-requirements-mapping]\n\n* [ActivityPub requirement abef3a0a-d3c4-4dee-a320-b28837d0bcd8][requirement-1] - The followers collection MUST be either an OrderedCollection or a Collection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when every test target has outcome `passed`, the requirement is satisfied by the test subject\n        * when any test target has outcome `failed`, the requirement is not satisfied by the test subject\n        * when any test target has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Issues [<sup>?</sup>][test-issues-list]\n\n## Change Log\n\n* 2023-12-06T07:43:27.890Z - init\n* 2023-12-18T05:11:09.722Z - add test cases based on implementing\n\n[requirement-1]: https://socialweb.coop/activitypub/behaviors/abef3a0a-d3c4-4dee-a320-b28837d0bcd8/\n[act-rules-requirements-mapping]: https://www.w3.org/TR/act-rules-format/#accessibility-requirements-mapping\n[activitypub]: https://www.w3.org/TR/activitypub/\n[test-applicability]: https://www.w3.org/TR/act-rules-format/#applicability\n[test-assumptions]: https://www.w3.org/TR/act-rules-format/#assumptions\n[test-background]: https://www.w3.org/TR/act-rules-format/#background\n[test-expectations]: https://www.w3.org/TR/act-rules-format/#expectations\n[test-input]: https://www.w3.org/TR/act-rules-format/#input\n[test-issues-list]: https://www.w3.org/TR/act-rules-format/#issues-list\n[test-test-cases]: https://www.w3.org/TR/act-rules-format/#test-cases\n",
      "name": "An ActivityPub Actor Object's `followers` Collection Must be a Collection",
      "description": "tests whether an ActivityPub Object has a `followers`` collection with an appropriate Collection type",
      "slug": "followers-collection-must-be-a-collection",
      "inapplicableCases": [
        {
          "name": "Inapplicable Example 1 - object is a number",
          "result": {
            "outcome": "inapplicable"
          },
          "input": {
            "object": "42"
          }
        },
        {
          "name": "Inapplicable Example 2 - object has no followers property",
          "result": {
            "outcome": "inapplicable"
          },
          "input": {
            "object": "{ \"foo\": \"bar\" }"
          }
        },
        {
          "name": "inapplicable example 3 - html object",
          "input": {
            "object": "<!doctype html>hi"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "passedCases": [
        {
          "name": "Passed Example 1",
          "result": {
            "outcome": "passed"
          },
          "input": {
            "object": "\n        {\n          \"followers\": {\n            \"type\": \"Collection\"\n          }\n        }\n      "
          }
        },
        {
          "name": "Passed Example 2",
          "result": {
            "outcome": "passed"
          },
          "input": {
            "object": "\n        {\n          \"followers\": {\n            \"type\": \"OrderedCollection\"\n          }\n        }\n      "
          }
        }
      ],
      "failedCases": [
        {
          "name": "Failed Example 1 - followers is a number",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"followers\": 42\n        }\n      "
          }
        },
        {
          "name": "Failed Example 2 - followers is an empty object",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"followers\": {}\n        }\n      "
          }
        },
        {
          "name": "Failed Example 3 - followers object type is array including only `\"Link\"`",
          "result": {
            "outcome": "failed"
          },
          "input": {
            "object": "\n        {\n          \"followers\": {\n            \"type\": [\"Link\"]\n          }\n        }\n      "
          }
        }
      ],
      "uuid": "018c3e08-611f-7e56-9f45-2fe5e4877d4e",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:abef3a0a-d3c4-4dee-a320-b28837d0bcd8",
          "url": "https://socialweb.coop/activitypub/behaviors/abef3a0a-d3c4-4dee-a320-b28837d0bcd8"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:abef3a0a-d3c4-4dee-a320-b28837d0bcd8",
  "type": "Behavior",
  "uuid": "abef3a0a-d3c4-4dee-a320-b28837d0bcd8",
  "content": "The followers collection MUST be either an OrderedCollection or a Collection",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#followers",
      "branch": [
        5,
        3
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Every actor SHOULD have a followers collection. This is a list of everyone who has sent a Follow activity for the actor, added as a side effect. This is where one would find a list of all the actors that are following the actor.\n",
      "exact": "The followers collection MUST be either an OrderedCollection or a Collection\n",
      "suffix": "and MAY be filtered on privileges of an authenticated user or as appropriate when no authentication is given.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

For non-transient objects, the server MUST attach an id to both the wrapping Create and its wrapped Object.

Tags
ActivityPubServer
Test Cases
  • Outbox Wraps Object With Create Activity
    JSON
    {
      "name": "Outbox Wraps Object With Create Activity",
      "description": "a test rule that submits an object to an outbox and checks for conformance with requirements for how the outbox server must wrap the object in a Create Activity.",
      "slug": "outbox-wraps-object-with-create-checked-using-get-location",
      "input": {
        "outbox": {
          "help": "URL of ActivityPub outbox that an object will be sent to",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value that will be sent with ActivityPub requests",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "applicability": "\nThis test applies to an ActivityPub Server handling POST requests to an ActivityPub Outbox.\n\nTest Targets\n* outbox: same value as input.outbox\n",
      "inapplicableCases": [],
      "passedCases": [],
      "failedCases": [],
      "uuid": "963a2313-788e-4bb7-b6ea-0d8bf7d255ef",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:e6d00349-6d07-4512-9a99-f698d3bc1dba",
          "url": "https://activitypub-testing.socialweb.coop/e6d00349-6d07-4512-9a99-f698d3bc1dba"
        },
        {
          "id": "urn:uuid:104e2cde-f072-43af-badd-bf9bd4129151",
          "url": "https://activitypub-testing.socialweb.coop/104e2cde-f072-43af-badd-bf9bd4129151"
        },
        {
          "id": "urn:uuid:f3725db1-1e6b-4d20-9cea-a49ef4291e9f",
          "url": "https://activitypub-testing.socialweb.coop/f3725db1-1e6b-4d20-9cea-a49ef4291e9f"
        }
      ],
      "testCases": [
        {
          "name": "outbox=https://socialweb.coop/outbox",
          "id": "urn:uuid:d7c8b377-2850-481b-992f-92ac437a9f3c",
          "input": {
            "outbox": "https://socialweb.coop/outbox"
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "outbox=https://example.com",
          "id": "urn:uuid:dde952d7-b9b0-488f-8b06-61a10ba134d4",
          "input": {
            "outbox": "https://example.com/"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "expectations": "\n* target `outboxPostLocationResponse` http request body must be a JSON string that parses to a JSON Object called `outboxPostResult`\n* `outboxPostResult` must have a `type` property whose value is either the string \"Create\" or an Array containing the string \"Create\"\n* `outboxPostResult` must have a `object` property whose value includes the object from test target `submission` (i.e. the object that was sent to the outbox, and should have been wrapped in a Create).\n* `outboxPostResult` must have a `id` property whose value is a URI\n* `outboxPostResult.object` must have a `id` property whose value is a URI\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult`\n* every value linked from  `submission` using the to, bto, cc, bcc, and audience properties must be included in the values for the same property in `outboxPostResult.object`\n"
    }
JSON
{
  "id": "urn:uuid:104e2cde-f072-43af-badd-bf9bd4129151",
  "uuid": "104e2cde-f072-43af-badd-bf9bd4129151",
  "type": "Behavior",
  "content": "For non-transient objects, the server MUST attach an id to both the wrapping Create and its wrapped Object.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6.2 Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#create-activity-outbox"
    },
    {
      "name": "6.2.1 Object creation without a Create Activity",
      "id": "https://www.w3.org/TR/activitypub/#object-without-create"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#object-without-create",
      "name": "Object creation without a Create Activity",
      "branch": [
        6,
        2,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The server then MUST attach this object as the object of a Create Activity.\n",
      "exact": "For non-transient objects, the server MUST attach an id to both the wrapping Create and its wrapped Object.\n",
      "suffix": "Note\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server MUST perform de-duplication of activities returned by the Inbox.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:2a0323ec-2500-4bc3-b116-11e641035d2f",
  "type": "Behavior",
  "uuid": "2a0323ec-2500-4bc3-b116-11e641035d2f",
  "content": "The server MUST perform de-duplication of activities returned by the Inbox.",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#inbox",
      "branch": [
        5,
        2
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "The server MUST perform de-duplication of activities returned by the inbox.\n",
      "prefix": "Depending on access control, some other content may be public, whereas other content may require authentication for non-owner users, if they can access the inbox at all.\n",
      "suffix": "Duplication can occur if an activity is addressed both to an actor's followers, and a specific actor who also follows the recipient actor, and the server has failed to de-duplicate the recipients list. Such deduplication MUST be performed by comparing the id of the activities and dropping any activities already seen.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

If an Activity is submitted with a value in the id property, servers MUST ignore this and generate a new id for the Activity.

Tags
ActivityPubServer
Test Cases
  • post outbox server must overwrite id property
    JSON
    {
      "name": "post outbox server must overwrite id property",
      "description": "A Test Rule for an ActivityPub Requirement",
      "applicability": "\nHow to derive test targets from input:\n* let postOutboxResponse be the http response from using an activitypub client to post to input.outbox\n* if postOutboxResponse does not contain a 'location' header, result outcome is inapplicable.\n* let outboxResultResponse be the response from using an activitypub client to fetch an activitypub object for the URL indicated by the value of the postOutboxResponse http response header named 'location'\n* let target.result be the result of parsing outboxResultResponse http response body as JSON. If the response body is not a JSON object, result outcome is inapplicable.\n",
      "expectations": "\n* target.result must be a JSON object\n* target.result.id MUST not be the same as input.submission.id\n    * rationale: the requirement is that the submission.id MUST be overwrittedn/ignored\n* target.result.id MUST be a string\n    * rationale: the requirement says the server MUST generate a new id\n",
      "slug": "post-outbox-server-overwrites-id-property",
      "uuid": "30018b5d-699a-45a9-a623-1f09a36cf0a6",
      "requirementReference": [
        {
          "id": "urn:uuid:2bdc4682-308f-42ae-87cf-847f62f64e36",
          "url": "https://activitypub-testing-website.socialweb.coop/2bdc4682-308f-42ae-87cf-847f62f64e36"
        }
      ],
      "input": {
        "outbox": {
          "help": "the url to the Outbox handling an Activity POST request",
          "required": true,
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value to include in outbox POST request",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "passedCases": [],
      "inapplicableCases": [
        {
          "name": "404 response outbox",
          "input": {
            "outbox": "https://socialweb.coop/activitypub/testing/utilities/response?status=404"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "attributedTo": [
        {
          "name": "bengo",
          "url": "https://bengo.is/"
        },
        {
          "name": "codenamedmitri"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:2bdc4682-308f-42ae-87cf-847f62f64e36",
  "type": "Behavior",
  "uuid": "2bdc4682-308f-42ae-87cf-847f62f64e36",
  "content": "If an Activity is submitted with a value in the id property, servers MUST ignore this and generate a new id for the Activity.\n",
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "clients posting Activities to an actor's outbox"
    }
  ],
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "If an Activity is submitted with a value in the id property, servers MUST ignore this and generate a new id for the Activity.\n",
      "suffix": "Servers MUST return a 201 Created HTTP code, and unless the activity is transient, MUST include the new id in the Location header.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

Such deduplication MUST be performed by comparing the id of the activities and dropping any activities already seen.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:37ea3238-a8a1-4731-b6b9-2134ed3bd5dd",
  "type": "Behavior",
  "uuid": "37ea3238-a8a1-4731-b6b9-2134ed3bd5dd",
  "content": "Such deduplication MUST be performed by comparing the id of the activities and dropping any activities already seen.",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#inbox",
      "branch": [
        5,
        2
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "Such deduplication MUST be performed by comparing the id of the activities and dropping any activities already seen.\n",
      "prefix": "The server MUST perform de-duplication of activities returned by the inbox. Duplication can occur if an activity is addressed both to an actor's followers, and a specific actor who also follows the recipient actor, and the server has failed to de-duplicate the recipients list.\n",
      "suffix": "The inboxes of actors on federated servers accepts HTTP POST requests, with behaviour described in Delivery.\n"
    }
  },
  "seeAlso": [
    "urn:uuid:2a0323ec-2500-4bc3-b116-11e641035d2f"
  ],
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

When objects are received in the outbox..., the server MUST target and deliver to: The to, bto, cc, bcc or audience fields if their values are individuals or Collections owned by the actor.

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:7f16b1b5-6c95-4d4c-83f1-3a3962a2e37c",
  "uuid": "7f16b1b5-6c95-4d4c-83f1-3a3962a2e37c",
  "type": "Behavior",
  "content": "When objects are received in the outbox..., the server MUST target and deliver to: The to, bto, cc, bcc or audience fields if their values are individuals or Collections owned by the actor.\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "7. Server to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#server-to-server-interactions"
    },
    {
      "name": "7.1 Delivery",
      "id": "https://www.w3.org/TR/activitypub/#delivery"
    },
    {
      "name": "7.1.1 Outbox Delivery Requirements for Server to Server",
      "id": "https://www.w3.org/TR/activitypub/#outbox-delivery"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#outbox-delivery",
      "branch": [
        7,
        1,
        1
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "7.1.1 Outbox Delivery Requirements for Server to Server\n",
      "exact": "When objects are received in the outbox (for servers which support both Client to Server interactions and Server to Server Interactions), the server MUST target and deliver to:\n  The to, bto, cc, bcc or audience fields if their values are individuals or Collections owned by the actor.\n",
      "suffix": "These fields will have been populated appropriately by the client which posted the Activity to the outbox. \n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The shares collection MUST be either an OrderedCollection or a Collection

Tags
ActivityPubServer
Test Cases
  • shares collection MUST be either an OrderedCollection or a Collection
    JSON
    {
      "description": "This test case tests that an actor's shares collection is an expected kind of collection",
      "input": {
        "object": {
          "help": "the object whose `shares` property will be tested",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        }
      },
      "markdown": "---\ntype:\n- TestCase\nstatus: draft\nname: shares collection MUST be either an OrderedCollection or a Collection\nslug: shares-collection-must-be-a-collection\ndescription: |\n  This rule checks that the shares collection of an ActivityPub Object, if present, is an OrderedCollection or a Collection.\nuuid: b03a5245-1072-426d-91b3-a3d412d45ae8\nattributedTo:\n- https://bengo.is\n\"@context\":\n- TestCase:\n    \"@id\": http://www.w3.org/ns/earl#:TestCase\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# shares collection MUST be either an OrderedCollection or a Collection\n\n## Background\n\n[ActivityPub](https://www.w3.org/TR/activitypub/) [§ 5.8 Shares Collection](https://www.w3.org/TR/activitypub/#shares):\n> Every object MAY have a shares collection.&hellip;\n> The shares collection MUST be either an OrderedCollection or a Collection\n\n## About this Test\n\nThis is a Test describing a rule to determine whether an ActivityPub object has a `shares` property whose value conforms to a the [requirement](https://socialweb.coop/activitypub/behaviors/937ae4e2-dd33-40c7-be1d-3ecac7f9fad5/) that \"The shares collection MUST be either an OrderedCollection or a Collection\".\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:b03a5245-1072-426d-91b3-a3d412d45ae8`.\n\n## Test Subject\n\nThe subject of this test is data claiming to conform to the specification of an [ActivityPub Object](https://www.w3.org/TR/activitypub/#obj).\n\nThis test is *not* inherently applicable to an ActivityPub Server. An ActivityPub Server serves 1 or more ActivityPub Objects.\n\nThis test is *not* inherently applicable to a URL that resolves to an ActivityPub Object. The URL is not the same as the Object. The URL may resolve to different Objects in different contexts. Dereference the URL to test the referent with this test.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n1. `object` - the object whose `shares` property will be tested\n    * type: binary\n    * constraints\n        * will be interepreted as JSON\n\n## Applicability\n\nThis test applies to the the `shares` property on the input `object`.\n\nThis test does not apply to objects with more than one shares collection. If the input `object` has a `shares` property whose value is an array length >= 2, the test outcome is `inapplicable`.\n\nThis test does not apply to objects for which a `shares` collection cannot be found.\nIf input `object` is not parseable as JSON to a JSON object, the test outcome is `inapplicable`.\nIf input `object` is a JSON object, but it does not contain a property named `shares`, the test outcome is `inapplicable`.\n\n### Test Targets\n\n1. `shares` - the shares collection derived from the input `object` and its `shares` property\n    * because this is the value of a property from the input `object` that must be a JSON object, the value of this `shares` target will also be JSON\n    * how to derive `shares` from inputs\n        1. let `inputs.object` be the JSON object parsed from input `object`\n        2. let `propertyValue` be\n            * if `inputs.object.shares` is a JSON string, `propertyValue` is that string\n            * if `inputs.object.shares` is a JSON object, `propertyValue` is that object\n            * if `inputs.object.shares` is an Array of length 1, `propertyValue` is the item in that array\n            * if `inputs.object.shares` is an Array of length greater than 1, the test outcome is `inapplicable` (covered in 'Applicability' above)\n        3. let `value` be\n          *if `outboxValue` is a json string, `value` is the result of interpreting that string as an ActivityPub Object Identifier, fetching the corresponding ActivityPub Object (e.g. `GET https://...`), and parsing the resulting response body as `application/json`.\n          * otherwise `value` is `outboxValue`\n        4. test target `shares` is equal to `value`\n\n## Expectations\n\n1. `shares` is a JSON object\n2. `shares` has a property named `type`\n3. the values of the `shares` target's `type` property must indicate that the value is either an OrderedCollection or a Collection as determined by:\n    * if the value of the `shares` target's `type` property is a string, it MUST be equal to either `\"OrderedCollection\"` or `\"Collection\"`\n    * if the value of the `shares` target's `type` property is an Array, it MUST contain an entry for at least one of `\"OrderedCollection\"` or `\"Collection\"`\n\n## Assumptions\n\n### Interpreting \"MUST be either an OrderedCollection or a Collection\"\n\nThis test chose to interpret 'MUST be&hellip; an' to include 'indicate its type as'. The rationale is:\n\n* it's easy to check and implement.\n* I can't think of a better alternative\n* I haven't received any objections\n\n## Test Cases\n\n### simple passed case\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      shares: [{\n        type: [\"Collection\"]\n      }]\n    }\n    ```\n\ntest targets\n\n* `shares`\n\n    ```json\n    {\n      \"type\": [\"Collection\"]\n    }\n    ```\n\n    * outcome: `passed`\n\n### object.shares is a number\n\ninputs\n\n* `object`\n\n  ```json\n  {\n    shares: 1\n  }\n  ```\n\ntest targets\n\n* `shares`\n\n  ```json\n  1\n  ```\n\n    * outcome: `failed`\n        * rationale: shares does not seem to \"be either an OrderedCollection or a Collection\"\n\n### object is number\n\ninputs\n\n* `object`\n\n  ```json\n  1\n  ```\n\nresult\n\n* outcome: `inapplicable`\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub Requirement 937ae4e2-dd33-40c7-be1d-3ecac7f9fad5](https://socialweb.coop/activitypub/behaviors/937ae4e2-dd33-40c7-be1d-3ecac7f9fad5) - \"The shares collection MUST be either an OrderedCollection or a Collection\"\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `shares` has outcome `passed`, requirement is satisfied\n        * when test target `shares` has outcome `failed`, requirement is not satisfied\n        * when test target `shares` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* 2023-11-28T22:01:58.195Z - first draft\n* 2023-12-29T23:06:48.611Z - format test cases section so each test case has a heading\n\n## Issues\n\n* [ ] ff7a: expand applicability to cover objects with `shares` property whose value is an array of length >= 2\n    * or perhaps just make another more ambitious test case\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n",
      "name": "shares collection MUST be either an OrderedCollection or a Collection",
      "slug": "shares-collection-must-be-a-collection",
      "uuid": "b03a5245-1072-426d-91b3-a3d412d45ae8",
      "inapplicableCases": [
        {
          "name": "object is number",
          "input": {
            "object": "1"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "object has no shares property",
          "input": {
            "object": "{}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "object has shares property value with array of length 2",
          "input": {
            "object": "{\"shares\":[1,2]}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "passedCases": [
        {
          "name": "simple object w/ shares",
          "input": {
            "object": "{\"shares\":{\"type\":\"Collection\"}}"
          },
          "result": {
            "outcome": "passed"
          }
        },
        {
          "name": "object has shares property value with array of length 1",
          "input": {
            "object": "{\"shares\":[{\"type\":[\"Collection\"]}]}"
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "failedCases": [
        {
          "name": "shares is a number",
          "input": {
            "object": "{\"shares\":1}"
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "shares has non-collection type",
          "input": {
            "object": "{\"shares\":{\"type\":[\"Person\"]}}"
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "shares null type",
          "input": {
            "object": "{\"shares\":{\"type\":null}}"
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "id": "urn:uuid:937ae4e2-dd33-40c7-be1d-3ecac7f9fad5",
          "url": "https://socialweb.coop/activitypub/behaviors/937ae4e2-dd33-40c7-be1d-3ecac7f9fad5/"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:937ae4e2-dd33-40c7-be1d-3ecac7f9fad5",
  "type": "Behavior",
  "uuid": "937ae4e2-dd33-40c7-be1d-3ecac7f9fad5",
  "content": "The shares collection MUST be either an OrderedCollection or a Collection\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#shares",
      "branch": [
        5,
        8
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Every object MAY have a shares collection. This is a list of all Announce activities with this object as the object property, added as a side effect.\n",
      "exact": "The shares collection MUST be either an OrderedCollection or a Collection\n",
      "suffix": "and MAY be filtered on privileges of an authenticated user or as appropriate when no authentication is given. \n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The inbox MUST be an OrderedCollection.

Tags
ActivityPubServer
Test Cases
  • inbox must be an OrderedCollection
    JSON
    {
      "description": "This rule checks whether the inbox property of an object appears to be an OrderedCollection",
      "failedCases": [
        {
          "name": "test case when the value of the inbox property is null",
          "input": {
            "object": "{\"inbox\":null}"
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "test case when inbox is an object without type OrderedCollection",
          "input": {
            "object": "{\"inbox\":{\"type\":[\"Person\"]}}"
          },
          "result": {
            "outcome": "failed"
          }
        },
        {
          "name": "inputs.object has inbox url to a non-collection",
          "input": {
            "authorization": "foo",
            "object": "{\"inbox\":\"https://bengo.is/actor.json\"}"
          },
          "result": {
            "outcome": "failed"
          }
        }
      ],
      "inapplicableCases": [
        {
          "name": "test case when the inbox property is missing",
          "input": {
            "object": "{}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where the value of inputs.object is not JSON",
          "input": {
            "object": "\u0000x123abc_intentionallyNotJson"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where the value of of the inbox property is an array of length 2",
          "input": {
            "authorization": "",
            "object": "{\"inbox\":[\"https://example.com\",\"https://example.com\"]}"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "test case where inbox property value is a URL that 404s when fetched",
          "input": {
            "object": {
              "inbox": "https://mastodon.social/users/bengo/inbox"
            }
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "inputs.object is null",
          "input": {
            "object": "null"
          },
          "result": {
            "outcome": "inapplicable"
          }
        },
        {
          "name": "inputs.object is a number",
          "input": {
            "object": "123"
          },
          "result": {
            "outcome": "inapplicable"
          }
        }
      ],
      "input": {
        "object": {
          "help": "the object whose `inbox` property will be tested",
          "required": true,
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#Actor"
          ]
        },
        "time": {
          "help": "amount of time allowed to run test, as IETF RFC3339 dur-time time duration. This is meant to configure the limit for how long this test will wait for network requests.",
          "required": true,
          "default": "T5S",
          "type": [
            "rfc3339:dur-time",
            "TimeLimit"
          ],
          "constraints": [
            {
              "content": " MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)",
              "mediaType": "test/markdown"
            }
          ]
        }
      },
      "markdown": "---\ntype:\n- TestCase\nstatus: draft\nname: inbox must be an OrderedCollection\ndescription: |\n  This rule checks whether the inbox property of an object appears to be an OrderedCollection\nuuid: 5e94d155-ed4a-4d71-b797-d7c387736ecf\nattributedTo:\n- https://bengo.is\n\"@context\":\n- TestCase:\n    \"@id\": http://www.w3.org/ns/earl#:TestCase\n\nrespec:\n  config:\n    editors:\n    - name: bengo\n      url: \"https://bengo.is\"\n      w3cid: 49026\n---\n\n# inbox must be an OrderedCollection\n\n## Background\n\n[ActivityPub](https://www.w3.org/TR/activitypub/) [§ 5.2 Inbox](https://www.w3.org/TR/activitypub/#inbox):\n> The inbox is discovered through the inbox property of an actor's profile. The inbox MUST be an OrderedCollection.\n\n## About this Test\n\nThis is a Test Case describing a rule to determine whether an ActivityPub object has an inbox property that meets this requirement to 'be an OrderedCollection', meeting requirement [4edf6768-c751-448f-96ac-4ef44cb4291f](https://socialweb.coop/activitypub/behaviors/4edf6768-c751-448f-96ac-4ef44cb4291f)\n\n### Identifier\n\nThe identifier of this test is `urn:uuid:5e94d155-ed4a-4d71-b797-d7c387736ecf`.\n\n## Test Subject\n\nThe subject of this test is any data claiming to conform to the specification of an ActivityPub Actor Object.\n\nThis test is *not* inherently applicable to an ActivityPub Server. An ActivityPub Server serves 0 or more Actor Objects. An ActivityPub Server for a big community might serve hundreds of ActivityPub Actor Objects. An ActivityPub Server for a single human may serve only that person's ActivityPub Actor Object.\n\nThis test is *not* inherently applicable to a URL of an Actor Object. The URL is not the same as the Actor Object. The URL may resolve to different Actor Objects in different contexts.\n\n## Inputs\n\nThis test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):\n\n1. `object` - the object whose `inbox` property will be tested\n\n    * type: binary\n    * constraints\n        * will be interpreted as JSON\n\n2. `time` - amount of time allowed to run test. This is meant to configure the limit for how long this test will wait for network requests.\n\n    * required: yes\n    * example: `T0.0021S`\n    * type: bytes\n    * constraints\n        * MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)\n\n## Applicability\n\nThis test applies to inbox objects linked to from the `object` input. Usually an actor will have one inbox, but there may be multiple.\n\nThis test does not apply to objects with more than one inbox . If the input `object` has an inbox property whose value is an array of length >= 2, outcome is `inapplicable`.\n(See issue #5102 below to track expanding the applicability)\n\nIf `object` is not parseable as JSON to a JSON object, outcome is `inapplicable`.\n\nIf `object` is a JSON object, but it does not contain a property named `inbox`, outcome is `inapplicable`. The rationale is that there is already a test `acaacb5f-8f7e-4f28-8d81-c7955070a767` that can be used to determine if input `object` has an inbox property at all.\n\n### Test Targets\n\n1. `inbox` - the value of the `inbox` property in the input `object` object\n    * since the `object` object must be JSON for this test's applicability, the value of `JSON.parse(actor).inbox` must also be JSON\n    * how to derive `inbox` from inputs\n        1. let `inboxValue` be\n            * if `inputs.actor.inbox` is a JSON string, `inboxValue` is that string\n            * if `inputs.actor.inbox` is a JSON object, `inboxValue` is that object\n            * if `inputs.actor.inbox` is an Array of length 1, `inboxValue` is the item in that array\n            * if `inputs.actor.inbox` is an Array of length greater than 1, the test outcome is `not applicable` (covered in 'Applicability' above)\n              * see issue #5102 below to track expanding the applicability to support inbox property values that are arrays with length greater than 1\n        2. let `inboxObject` be\n            * if `inboxValue` is a json object, `inboxObject` is that object\n            * if `inboxValue` is a json string, `inboxObject` is the result of interpreting that string as an ActivityPub Object Identifier and fetching the corresponding ActivityPub Object (e.g. `GET https://...`).\n        3. test target `inbox` is `inboxValue`\n\n## Expectations\n\n1. `inbox` is a json object\n2. `inbox` has a property named `type`\n3. the values of the inbox's `type` property must contain `\"OrderedCollection\"`, as determined by\n    * if value of `inbox`'s `type` property is a string, it MUST be `\"OrderedCollection\"`\n    * if value of `inbox`'s `type` property is an Array, it MUST contain an entry identical to `\"OrderedCollection\"`\n\n## Assumptions\n\n### Interpreting \"MUST be an OrderedCollection\"\n\nRevisiting the background from above,\n[ActivityPub](https://www.w3.org/TR/activitypub/) [says](https://www.w3.org/TR/activitypub/#inbox)\n> The inbox is discovered through the inbox property of an actor's profile. The inbox MUST be an OrderedCollection.\n\n\"MUST be an OrderedCollection\" is open to some interpretation here.\n\nThis test chose to interpret 'be an' to include 'indicate its type as'. The rationale is that it's easy to check and easy to implement.\n\n## Test Cases\n\n### simple valid inbox\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"inbox\": {\n        \"id\": \"http://example.org/blog/\",\n        \"type\": \"OrderedCollection\",\n        \"name\": \"Martin's Blog\"\n      }\n    }\n    ```\n\n    * note: this OrderedCollection is from [Example 5 in activitystreams-core](https://www.w3.org/TR/activitystreams-core/#ex2-jsonld)\n\ntest targets\n\n* `inbox`\n    * value:\n\n        ```json\n        {\n        \"id\": \"http://example.org/blog/\",\n        \"type\": \"OrderedCollection\",\n        \"name\": \"Martin's Blog\"\n        }\n        ```\n\n    * outcome: `passed`\n\n### inbox is null\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"inbox\": null\n    }\n    ```\n\ntest targets\n\n* `inbox`\n    * value:\n\n        ```json\n        null\n        ```\n\n    * outcome: `failed`\n        * rationale: inbox is not an OrderedCollection\n\n### inbox type array contains only Person\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"inbox\": {\n        \"type\": [\"Person\"]\n      }\n    }\n    ```\n\ntest targets\n\n* `inbox`\n    * value:\n\n        ```json\n        {\n          \"type\": [\"Person\"]\n        }\n        ```\n\n    * outcome: `failed`\n        * rationale: inbox type is not OrderedCollection\n\n### object with no properties\n\ninputs\n\n* `object`\n\n    ```json\n    {}\n    ```\n\nresult\n\n* outcome: `inapplicable`\n\n### object is not JSON\n\ninputs\n\n* `object`\n\n    ```text\n    \\0x123abc_intentionallyNotJson\n    ```\n\nresult\n\n* outcome: `inapplicable`\n\n### inapplicable because inbox has many values\n\ninputs\n\n* `object`\n\n    ```json\n    {\n      \"inbox\": [\"https://example.com\", \"https://example.com\"]\n    }\n    ```\n\ntest targets\n\n* `inbox`\n    * value:\n\n        ```json\n        [\"https://example.com\", \"https://example.com\"]\n        ```\n\n    * outcome: `inapplicable`\n        * rationale: inbox values that are arrays of length greater than 1 or inapplicable. See issue #5102 below to track expanding the applicability\n\n## Glossary\n\n### `outcome`\n\nAn outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:\n\n* `inapplicable`: No part of the test subject matches the applicability\n* `passed`: A test target meets all expectations\n* `failed`: A test target does not meet all expectations\n\n## Requirements Mapping\n\n* [ActivityPub Requirement 4edf6768-c751-448f-96ac-4ef44cb4291f](https://socialweb.coop/activitypub/behaviors/4edf6768-c751-448f-96ac-4ef44cb4291f) - The inbox MUST be an OrderedCollection\n    * Required for Conformance to [ActivityPub][activitypub]\n    * Outcome Mapping\n        * when test target `inbox` has outcome `passed`, requirement is satisfied\n        * when test target `inbox` has outcome `failed`, requirement is not satisfied\n        * when test target `inbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction\n\n## Change Log\n\n* 2023-11-15T01:18:34.191Z - first draft\n* 2023-11-16T06:09:56.915Z - finish all sections in first draft\n    * add test cases for passed outcome\n    * add test cases for failed outcome\n    * add test cases for inapplicable outcome\n    * add requirements mapping for <https://socialweb.coop/activitypub/requirements/4edf6768-c751-448f-96ac-4ef44cb4291f/>\n    * add a test case where inbox is a URI that is resolved to an OrderedCollection an the outcome is \"passed\"\n* 2023-12-29T21:51:17.983Z - reformat test cases\n\n## Issues\n\n* #01d2: resolving inbox url should use inputs.time and timeout\n* #5102: expand applicability to handle cases where there are multiple inboxes\n\n[activitypub]: https://www.w3.org/TR/activitypub/\n",
      "name": "inbox must be an OrderedCollection",
      "passedCases": [
        {
          "name": "test case when inbox is an OrderedCollection",
          "input": {
            "object": "{\"inbox\":{\"id\":\"http://example.org/blog/\",\"type\":\"OrderedCollection\",\"name\":\"Martin's Blog\"}}"
          },
          "result": {
            "outcome": "passed"
          }
        }
      ],
      "slug": "inbox-must-be-an-orderedcollection",
      "uuid": "5e94d155-ed4a-4d71-b797-d7c387736ecf",
      "isPartOf": [
        "https://socialweb.coop/activitypub/test-cases/"
      ],
      "requirementReference": [
        {
          "url": "https://socialweb.coop/activitypub/behaviors/4edf6768-c751-448f-96ac-4ef44cb4291f/",
          "id": "urn:uuid:4edf6768-c751-448f-96ac-4ef44cb4291f"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:4edf6768-c751-448f-96ac-4ef44cb4291f",
  "type": "Behavior",
  "uuid": "4edf6768-c751-448f-96ac-4ef44cb4291f",
  "content": "The inbox MUST be an OrderedCollection.",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#inbox",
      "branch": [
        5,
        2
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "exact": "The inbox MUST be an OrderedCollection.\n",
      "prefix": "The inbox is discovered through the inbox property of an actor's profile.\n",
      "suffix": "The inbox stream contains all activities received by the actor.\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

the object MUST be modified to reflect the new structure as defined in the update activity

Tags
ActivityPubServer
Test Cases
  • Create then Update modifies an Object as checked by getting the Object after the Update
    JSON
    {
      "name": "Create then Update modifies an Object as checked by getting the Object after the Update",
      "description": "A test rule that sends to an ActivityPub Outbox a Create Activity creating an Object, then sends an Update Activity for that object, then sends an http get to the object URI and expects the object to be modified by the Update.",
      "slug": "create-then-update-modifies-object-checked-by-get",
      "uuid": "08efdc1e-195e-4e36-a2b3-e33205bb737b",
      "input": {
        "outbox": {
          "help": "the url to the Outbox handling an Activity POST request",
          "required": true,
          "type": "xsd:anyUri",
          "rangeIncludes": [
            "https://www.w3.org/ns/activitystreams#outbox"
          ]
        },
        "authorization": {
          "help": "HTTP Authorization header value to include in outbox POST request",
          "required": false,
          "type": [
            "xsd:string",
            "https://activitypub-testing.socialweb.coop/ns/HiddenInTestResults"
          ],
          "rangeIncludes": [
            "https://www.rfc-editor.org/rfc/rfc9110#field.authorization.value"
          ]
        }
      },
      "applicability": "This test applies only when it is possible to\nsend a Create activity submission to the outbox,\nfollow the response Location header to the result of submission,\ninspect the resulting activity to see the id provisioned for its object,\nsend an Update activity to update that object,\nfetch the object,\nand determine whether the update occurred.\n\nTo derive test targets, we'll need to do the above and make serveral requests:\n* POST outbox with a Create to create an object\n* fetch the result of that to determine the object id\n* POST outbox with an Update to update the object with that id\n* fetch the object by id\n\nHow to derive test targets from input:\n* input.outbox MUST be a URI\n* let create be `{\"type\": \"Create\": \"object\": {\"type\":\"Note\",\"content\":\"v0\"}}`\n* let createObjectResponse be the http response from using an ActivityPub Client to send create to input.outbox including, if available, input.authorization as the Authorization header\n* let createObjectLocation be the value of createObjectResponse's http response header named 'location'. If it is a relative URI, resolve it to an absolute uri using a base uri of input.outbox.\n* let fetchedCreate be the result of using an activitypub client to fetch the ActivityPub object at createObjectLocatioh. If available, send input.authorization as the Authorization header.\n* let createdObjectId be fetchedCreate.object.id. This should be a URI assigned by the outbox server.\n* let update be `{\"type\": \"Update\", \"object\": { \"id\": createdObjectId, \"content\": \"v1\" }}`\n* use an ActivityPub Client to send update to input.outbox\n* the test runner may wait some time to allow the outbox server to process the update\n* let objectV1Response be the http response from using an ActivityPub Client to fetch the ActivityPub Object at createdObjectId\n* let target.objectV1 be the response body of objectV1Response",
      "expectations": "* target.objectV1 MUST be a JSON object\n* target.objectV1.content MUST be `\"v1\"`",
      "requirementReference": [
        {
          "id": "urn:uuid:c1cd98fe-ae9c-48a7-9b43-cdd8eb008bc8",
          "url": "https://activitypub-testing-website.socialweb.coop/c1cd98fe-ae9c-48a7-9b43-cdd8eb008bc8"
        }
      ]
    }
JSON
{
  "id": "urn:uuid:c1cd98fe-ae9c-48a7-9b43-cdd8eb008bc8",
  "uuid": "c1cd98fe-ae9c-48a7-9b43-cdd8eb008bc8",
  "type": "Behavior",
  "content": "the object MUST be modified to reflect the new structure as defined in the update activity\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "6. Client to Server Interactions",
      "id": "https://www.w3.org/TR/activitypub/#client-to-server-interactions"
    },
    {
      "name": "6.3 Update Activity",
      "id": "https://www.w3.org/TR/activitypub/#update-activity-outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#update-activity-outbox",
      "name": "Update Activity",
      "branch": [
        6,
        3
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "The Update activity is used when updating an already existing object.\n",
      "exact": "The side effect of this is that the object MUST be modified to reflect the new structure as defined in the update activity, assuming the actor has permission to update this object.\n",
      "suffix": "6.3.1 Partial Updates\n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}

The server MUST remove the bto and/or bcc properties, if they exist, from the ActivityStreams object before delivery

Tags
ActivityPubServer
JSON
{
  "id": "urn:uuid:e69ce2d6-2946-4513-b475-10690767a734",
  "type": "Behavior",
  "uuid": "e69ce2d6-2946-4513-b475-10690767a734",
  "content": "The server MUST remove the bto and/or bcc properties, if they exist, from the ActivityStreams object before delivery\n",
  "tag": [
    {
      "name": "ActivityPubServer",
      "id": "https://socialweb.coop/tag/ActivityPubServer"
    }
  ],
  "context": [
    "https://www.w3.org/TR/activitypub/",
    {
      "name": "Client to server interaction"
    },
    {
      "name": "server delivering activities clients post to an actor's outbox"
    }
  ],
  "origin": {
    "source": "https://www.w3.org/TR/activitypub/",
    "section": {
      "id": "https://www.w3.org/TR/activitypub/#x6-client-to-server-interactions",
      "branch": [
        6
      ]
    },
    "selector": {
      "type": "TextQuoteSelector",
      "prefix": "Location: https://dustycloud.org/likes/345\n",
      "exact": "The server MUST remove the bto and/or bcc properties, if they exist, from the ActivityStreams object before delivery\n",
      "suffix": ", but MUST utilize the addressing originally stored on the bto / bcc properties for determining recipients in delivery. \n"
    }
  },
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://socialweb.coop/ns/testing/context.json"
  ]
}