This rule checks that URLs of ActivityPub objects can be resolved to a representation with well-known media type for further processing.
Use these identifiers to refer to this Test.
urn:uuid:e7ee491d-88d7-4e67-80c8-f74781bb247c
This slug is memorable, but it is not guaranteed to be globally unique like a URI.
actor-must-serve-as2-object-to-get
This describes the input that each test run will use to select test targets.
identifier of an ActivityPub Object hosted at an ActivityPub Server
{ "help": "identifier of an ActivityPub Object hosted at an ActivityPub Server", "type": "xsd:anyUri", "rangeIncludes": [ "https://www.w3.org/ns/activitystreams#Actor" ], "required": true }
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)
{ "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" ] }
{ "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" ] } }
This test applies to a server hosting the ActivityPub Object identified by input `id`. If input `id` is not a URI, outcome is `inapplicable`. If input `id` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes). If input `time` is not parseable as an RFC3339 `dur-time`, outcome is `inapplicable`. ### Test Targets * `response` - the HTTP response that is the result of retrieving the ActivityPub Object identified by input `id`. * 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 `id` 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"` 4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization` 5. send the HTTP request and await a response 6. assign the HTTP response to the `response` test target
This test applies to a server hosting the ActivityPub Object identified by input `id`. If input `id` is not a URI, outcome is `inapplicable`. If input `id` URI scheme is not `https` or `http`, outcome is `inapplicable`. (This test may be revised later to handle other URI schemes). If input `time` is not parseable as an RFC3339 `dur-time`, outcome is `inapplicable`. ### Test Targets * `response` - the HTTP response that is the result of retrieving the ActivityPub Object identified by input `id`. * 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 `id` 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"` 4. if input `authorization` was provided, add an http request header to `request` whose name is `Authorization` and whose value is input `authorization` 5. send the HTTP request and await a response 6. assign the HTTP response to the `response` test target
* `response` body is parseable as JSON * `response` body parsed JSON is a JSON Object * `response` body must be an "ActivityStreams object representation"
* `response` body is parseable as JSON * `response` body parsed JSON is a JSON Object * `response` body must be an "ActivityStreams object representation"
This Test has been derived from these specified requirements.
{ "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" } }
{ "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" ] }
This test is part of the following Test Suites:
{ "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/" } ] }