---
type:
- TestCase
status: draft
name: inbox must be an OrderedCollection
description: |
  This rule checks whether the inbox property of an object appears to be an OrderedCollection
uuid: 5e94d155-ed4a-4d71-b797-d7c387736ecf
attributedTo:
- https://bengo.is
"@context":
- TestCase:
    "@id": http://www.w3.org/ns/earl#:TestCase

respec:
  config:
    format: markdown
    editors:
    - name: "Benjamin Goering"
      company: "bengo.is"
      companyURL: "https://bengo.is"
---

# inbox must be an OrderedCollection

## Background

[ActivityPub](https://www.w3.org/TR/activitypub/) [says](https://www.w3.org/TR/activitypub/#inbox)
> The inbox is discovered through the inbox property of an actor's profile. The inbox MUST be an OrderedCollection.

## About this Test

This 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)

### Identifier

The identifier of this test is `5e94d155-ed4a-4d71-b797-d7c387736ecf`.

## Test Subject

The subject of this test is any data claiming to conform to the specification of an ActivityPub Actor Object.

This 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.

This 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.

## Inputs

This test requires the following [inputs](https://www.w3.org/TR/act-rules-format/#input):

1. `object` - the object whose `inbox` property will be tested

    * type
        * bytes - will be interpreted as JSON

2. `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.

    * required: yes
    * example: `T0.0021S`
    * type: bytes
        * constraints
            * MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)

## Applicability

This test applies to inbox objects linked to from the `object` input. Usually an actor will have one inbox, but there may be multiple.

This 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`.
(See issue #5102 below to track expanding the applicability)

If `object` is not parseable as JSON to a JSON object, outcome is `inapplicable`.

If `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.

### Test Targets

* `inbox` - the value of the `inbox` property in the input `object` object
    * since the `object` object must be JSON for this test's applicability, the value of `JSON.parse(actor).inbox` must also be JSON
    * how to derive `inbox` from inputs
    1. let `inboxValue` be
        * if `inputs.actor.inbox` is a JSON string, `inboxValue` is that string
        * if `inputs.actor.inbox` is a JSON object, `inboxValue` is that object
        * if `inputs.actor.inbox` is an Array of length 1, `inboxValue` is the item in that array
        * if `inputs.actor.inbox` is an Array of length greater than 1, the test outcome is `not applicable` (covered in 'Applicability' above)
          * see issue #5102 below to track expanding the applicability to support inbox property values that are arrays with length greater than 1
    2. let `inboxObject` be
        * if `inboxValue` is a json object, `inboxObject` is that object
        * 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://...`).
    3. test target `inbox` is `inboxValue`

## Expectations

1. `inbox` is a json object
2. `inbox` has a property named 'type'
3. the values of the inbox's `type` property must contain `"OrderedCollection"`, as determined by
    * if value of `inbox`'s `type` property is a string, it MUST be `"OrderedCollection"`
    * if value of `inbox`'s `type` property is an Array, it MUST contain an entry identical to `"OrderedCollection"`

## Assumptions

### Interpreting "MUST be an OrderedCollection"

Revisiting the background from above,
[ActivityPub](https://www.w3.org/TR/activitypub/) [says](https://www.w3.org/TR/activitypub/#inbox)
> The inbox is discovered through the inbox property of an actor's profile. The inbox MUST be an OrderedCollection.

"MUST be an OrderedCollection" is open to some interpretation here.

This test chose to interpret 'be an' to include 'indicate its type as'. The rationale is that it's easy to check, easy to implement, and we have yet to meet an objection. We anticipate the following objections:

* this test should more strictly interpret this to check other properties on the `inbox` other than `type`
    * response
        * maybe, but which ones?
        * this test, as is, will fail if the inbox is `2` or `{}` or something way unexpected. Maybe more aggressive expectations can go in a more opinionated test?

## Test Cases

1. simple valid inbox
    * inputs
      * `object`

        ```json
        {
          "inbox": {
            "id": "http://example.org/blog/",
            "type": "OrderedCollection",
            "name": "Martin's Blog"
          }
        }
        ```

          * note: this OrderedCollection is from [Example 5 in activitystreams-core](https://www.w3.org/TR/activitystreams-core/#ex2-jsonld)
    * test targets
      * `inbox`
        * outcome: `passed`

2. inbox is null
    * inputs
      * `object`

        ```json
        {
          "inbox": null
        }
        ```

    * test targets
      * `inbox`
        * outcome: `failed`
          * rationale: inbox is obviously not an OrderedCollection

3. inbox type array contains Person
    * inputs
      * `object`

        ```json
        {
          "inbox": {
            "type": ["Person"]
          }
        }
        ```

    * test targets
      * `inbox`
        * outcome: `failed`
          * rationale: inbox type is not OrderedCollection

4. inapplicable because inbox property missing
    * inputs
      * `object`

        ```json
        {}
        ```

    * outcome: `inapplicable`

5. inapplicable because not JSON
    * inputs
      * `object`

        ```text
        \0x123abc_intentionallyNotJson
        ```

    * outcome: `inapplicable`

6. inapplicable because inbox has many values
    * inputs
      * `object`

        ```json
        {
          "inbox": ["https://example.com", "https://example.com"]
        }
        ```

    * outcome: `inapplicable`
      * rationale: inbox values that are arrays of length greater than 1 or inapplicable. See issue #5102 below to track expanding the applicability

## Glossary

### `outcome`

An outcome is a conclusion that comes from evaluating a test on a test subject. An outcome can be one of the three following types:

* `inapplicable`: No part of the test subject matches the applicability
* `passed`: A test target meets all expectations
* `failed`: A test target does not meet all expectations

## Requirements Mapping

* [ActivityPub Requirement 4edf6768-c751-448f-96ac-4ef44cb4291f](https://socialweb.coop/activitypub/behaviors/4edf6768-c751-448f-96ac-4ef44cb4291f) - The inbox MUST be an OrderedCollection
    * Required for Conformance to [ActivityPub][activitypub]
    * Outcome Mapping
        * when test target `inbox` has outcome `passed`, requirement is satisfied
        * when test target `inbox` has outcome `failed`, requirement is not satisfied
        * when test target `inbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction

## Change Log

* 2023-11-15T01:18:34.191Z - first draft
* [x] add test cases for passed outcome
* [x] add test cases for failed outcome
* [x] add test cases for inapplicable outcome
* [x] add requirements mapping for <https://socialweb.coop/activitypub/requirements/4edf6768-c751-448f-96ac-4ef44cb4291f/>
* [X] add a test case where inbox is a URI that is resolved to an OrderedCollection an the outcome is "passed"
    * [X] decide what the URL will be. Does it need to publicly resolve for a long time? maybe <https://socialweb.coop/actors/empty-inbox.json>
        * if that's not feasible, the test case could have the `inbox` test target stubbed out so at least that part could be checked
* 2023-11-16T06:09:56.915Z - finish all sections in first draft

## Issues

* [ ] #01d2: resolving inbox url should use inputs.time and timeout
* [ ] #5102: expand applicability to handle cases where there are multiple inboxes

[activitypub]: https://www.w3.org/TR/activitypub/
