---

uuid: 723afcbb-118d-433e-8ab4-560ffca93582

type:
- TestCase

name: |
  Outbox Servers handling activity submissions MUST return a 201 created HTTP status code

slug: |
  outbox-post-servers-must-return-a-201-created-http-code

description: |
  This test checks that an ActivityPub Outbox responds with a 201 status code when sent a POST request with an Activity submission.

testsRequirement:
- id: urn:uuid:3b925cdf-89fe-4f51-b41f-26df23f58e0c
  url: https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c
  name: Servers MUST return a 201 Created HTTP code

# https://www.w3.org/QA/WG/2005/01/test-faq#review
# *submitted, accepted, reviewed*, *returned for revision*, or *rejected*
testCaseState: submitted

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

# Outbox Servers handling activity submissions MUST return a 201 created HTTP status code

## Background

In the context of [client-to-server interactions](https://www.w3.org/TR/activitypub/#client-to-server-interactions) via Activity submission, [ActivityPub][activitypub] says
> Servers MUST return a 201 Created HTTP code

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

## About this Test

This is a Test describing a rule to determine whether an ActivityPub Server is in conformance with the following behaviors required by [ActivityPub][activitypub]:

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

### Identifier

The identifier of this test is `723afcbb-118d-433e-8ab4-560ffca93582`.

## Test Subject

The subject of this test is an ActivityPub Server serving activity submission requests for an ActivityPub Outbox.

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

## Inputs

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

1. `outbox` - identifier of an ActivityPub Outbox
    * required: yes
    * type: binary
      * constraints
        * MUST be a URI, i.e. an [ActivityPub Object Identifier](https://www.w3.org/TR/activitypub/#obj-id) that is not `null`

2. `authorization` - proof of authorization to submit an activity to the outbox identified by input `outbox`
    * required: no
      * if this input is omitted, no `Authorization` header will be provided in the HTTP request send by this test
    * type: binary
      * constraints
        * If present, this should be valid as the value of an HTTP `Authorization` header

3. `submission` - ActivityPub message that will be submitted to the Outbox.
    * required: no
      * if this input is omitted, the test will use a simple valid submission
    * type: binary
      * constraints
        * should be JSON
      * note
        * 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"`.

## Applicability

This test applies to the server hosting the ActivityPub Outbox identified by input `outbox` and that support requests using HTTP method `POST`.

If input `outbox` is not a URI, outcome is `inapplicable`.

If input `outbox` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes).

If the server indicates it does not support `POST` requests, the test outcome MUST be `inapplicable`.

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

### Test Targets

* `response` - the HTTP response that is the result of submitting the input `submission` to the ActivityPub Outbox identified by input `outbox`.
    * how to derive `response` from inputs
      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.
      2. let `request` be a new HTTP Request whose URI is input `outbox`
      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"`
      4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization`
      5. let `submission` be the input `submission`, if provided, otherwise use the json value

          ```json
          {
            "@context": "https://www.w3.org/ns/activitystreams",
            "type": "Note",
            "content": "Say, did you finish reading that book I lent you?"
          }
          ```

      6. let the http request body of `request` be the value of `submission`
      7. send the HTTP request and await a response
      8. assign the HTTP response to the `response` test target
      9. If the status code of `response` is 401, the test outcome MUST be a [`CannotTell`](https://www.w3.org/TR/EARL10-Schema/#CannotTell)
      10. If the status code of `response` is 403,
          * if input `authorization` was provided, the test outcome MAY be [`Fail`](https://www.w3.org/TR/EARL10-Schema/#Fail)
          * else (input `authorization` was not provided) the test outcome MUST be a [`CannotTell`](https://www.w3.org/TR/EARL10-Schema/#CannotTell)
      11. If the status code of `response` is 405 (e.g. 'Method Not Allowed'), the test outcome MUST be `inapplicable`
          * rationale: The server is explicitly indicating it does not support requests with method `POST`, and this test is only applicable to servers that do.

## Expectations

* `response` http status code is `201`

## Assumptions

### Default Outbox Submission

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

### 403 Forbidden Semantics Varies on Authorization

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

A rationale is that HTTP semantics in general defines 403 to mean different things depending on whether authentication credentials were provided in the request:

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

<cite><a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden">RFC9110 HTTP Semantics</a></cite>

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

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

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

## Test Cases

These are test cases for this test itself, i.e. each test case is a set of test inputs and the corresponding test results.

### simple passed case

inputs

* `outbox`

    ```url
    https://socialweb.coop/activitypub/testing/utilities/response?status=201`
    ```

* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`

    ```http
    HTTP/2 201 
    content-type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"

    {}
    ```

    * outcome: `passed`

### nginx 404 response body

inputs

* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=404`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`:

    ```http
    HTTP/2 404 
    content-type: text/html; charset=UTF-8
    content-length: 153

    <html>
    <head><title>404 Not Found</title></head>
    <body>
    <center><h1>404 Not Found</h1></center>
    <hr><center>nginx/1.25.2</center>
    </body>
    </html>
    ```

    * outcome: `cantTell`

### non-URI input `outbox`

inputs

* `outbox`: `bafybeib5mvfjatmpswc3jnh7ydz4zxe25cm63xp6aafpg3j2awakf63qma`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

result

* outcome: `inapplicable`
    * rationale: input `outbox` is not a URI

### without authorization, response status 401

inputs

* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=401`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`:

    ```http
    HTTP/1.1 401 Forbidden
    
    401
    ```

result

* outcome: [`cantTell`](https://www.w3.org/TR/EARL10-Schema/#cantTell)

### response status 405 Method Not Allowed

inputs

* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=405`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`:

    ```http
    HTTP/1.1 405 Not Allowed
    
    405
    ```

* outcome: `inapplicable`
    * rationale: input `response` status 405 indicates that requests with method `POST` are not supported, so this test does not apply (see 'Applicability')

### without authorization, response status 403 Forbidden

inputs

* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=403`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`:

    ```http
    HTTP/1.1 403 Forbidden
    
    403
    ```

result

* outcome: [`cantTell`](https://www.w3.org/TR/EARL10-Schema/#cantTell)

### with authorization, response status 403 Forbidden

inputs

* `outbox`: `https://socialweb.coop/activitypub/testing/utilities/response?status=403`
* `authorization`: `Bearer foo`
* `submission`

    ```json
    {
      "type": "Create"
    }
    ```

test targets

* `response`:

    ```http
    HTTP/1.1 403 Forbidden
    
    403
    ```

result

* outcome: [`failed`](https://www.w3.org/TR/EARL10-Schema/#failed)

## 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 3b925cdf-89fe-4f51-b41f-26df23f58e0c](https://socialweb.coop/activitypub/behaviors/3b925cdf-89fe-4f51-b41f-26df23f58e0c) - Servers MUST return a 201 Created HTTP code
    * Required for Conformance to [ActivityPub][activitypub]
    * Outcome Mapping
        * when test target `response` has outcome `passed`, further testing is needed to determine requirement satisfaction
        * when test target `response` has outcome `failed`, requirement is not satisfied
        * when test target `response` has outcome `inapplicable`, further testing is needed to determine requirement satisfaction

## Issues

* [x] host an example outbox that can be used as the `passed` test case for this
    * it should always respond with http response 201
    * 2023-12-22T18:20:55.076Z: done: <https://socialweb.coop/activitypub/testing/utilities/response?status=201>

## Change Log

* 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.
* 2024-01-07T21:39:50.838Z - add support for http responses with status 401, 403, 404
