---
type:
- TestCase
status: draft
name: outbox must be an OrderedCollection
description: |
  This rule checks whether the outbox property of an object appears to be an OrderedCollection
uuid: 4af549f4-3797-4d99-a151-67c3d8feaa46
attributedTo:
- https://bengo.is
"@context":
- TestCase:
    "@id": http://www.w3.org/ns/earl#:TestCase

respec:
  config:
    editors:
    - name: bengo
      url: "https://bengo.is"
      w3cid: 49026
---

# outbox must be an OrderedCollection

## Background

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

## About this Test

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

### Identifier

The identifier of this test is `urn:uuid:4af549f4-3797-4d99-a151-67c3d8feaa46
`.

## 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 `outbox` property will be tested
    * type: binary
    * constraints
        * 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: binary
    * constraints
        * MUST be an [RFC3339 `dur-time`](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A)

## Applicability

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

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

### Test Targets

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

## Expectations

1. `outbox` is a JSON object
2. `outbox` has a property named `type``
3. the values of the outbox's `type` property must contain `"OrderedCollection"`, as determined by
    * if value of `outbox`'s `type` property is a string, it MUST be `"OrderedCollection"`
    * if value of `outbox`'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/#outbox)
> The outbox is discovered through the outbox property of an actor's profile. The outbox 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 and easy to implement.

## Test Cases

### object has simple valid outbox

inputs

* `object`

    ```json
    {
      "outbox": {
        "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

* `outbox`
    * value:

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

    * outcome: `passed`

### object outbox is null

inputs

* `object`

    ```json
    {
      "outbox": null
    }
    ```

test targets

* `outbox`
    * value:

      ```json
      null
      ```

    * outcome: `failed`
        * rationale: outbox is obviously not an OrderedCollection

### object outbox type array contains Person

inputs

* `object`

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

test targets

* `outbox`
    * value:

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

    * outcome: `failed`
        * rationale: outbox type is not OrderedCollection

### inapplicable because outbox has many values

inputs

* `object`

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

test targest

* `outbox`
    * value:

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

    * outcome: `inapplicable`
        * 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

result

* outcome: `inapplicable`

### object is empty object

inputs

* `object`

    ```json
    {}
    ```

result

* outcome: `inapplicable`

### object is not JSON

inputs

* `object`

    ```text
    \0x123abc_intentionallyNotJson
    ```

result

* outcome: `inapplicable`

## 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 003a3be2-fb58-4812-a3a3-795067254327](https://socialweb.coop/activitypub/behaviors/003a3be2-fb58-4812-a3a3-795067254327) - The outbox MUST be an OrderedCollection
    * Required for Conformance to [ActivityPub][activitypub]
    * Outcome Mapping
        * when test target `outbox` has outcome `passed`, requirement is satisfied
        * when test target `outbox` has outcome `failed`, requirement is not satisfied
        * when test target `outbox` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction

## Change Log

* 2023-11-22T05:12:58.454Z - first draft, heavily copy from ../inbox-must-be-an-orderedcollection/inbox-must-be-an-orderedcollection.md
* 2023-12-29T22:46:03.806Z - reformat test cases to give each one its own heading

## Issues

Required

* add a test case where outbox is a URI that is resolved to an OrderedCollection an the outcome is "passed"
    * decide what the URL will be. Does it need to publicly resolve for a long time? maybe <https://socialweb.coop/actors/empty-outbox.json>
        * if that's not feasible, the test case could have the `outbox` test target stubbed out so at least that part could be checked

Nice to Have

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

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