Skip to content

Commit 3f60eb8

Browse files
authored
Minor improvements for slash semantic and content-type header tests (#77)
* Improve tests for missing content-type * Improve tests slash semantics * Add tests for containers creating by POST * Update run script for CSS 4
1 parent ba9dc2c commit 3f60eb8

5 files changed

Lines changed: 150 additions & 39 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
*.env
44
config
55
reports
6-
target
6+
target
7+
/certs/

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Solid Specification Conformance Tests
22

3+
## Release 0.0.12
4+
* Use harness API to test sending requests without a content type header.
5+
* Ensure container created correctly on slash semantic tests.
6+
37
## Release 0.0.11
48
* Moved repository to `solid-contrib` organization.
59

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,19 @@
11
Feature: Server MUST reject write requests without Content-Type
22

33
Background: Set up clients and paths
4-
* def testContainer = rootTestContainer.reserveContainer()
5-
* def resource = testContainer.reserveResource('.ttl')
4+
* def testContainer = rootTestContainer.createContainer()
65

76
Scenario: Server rejects PUT requests without Content-Type
8-
Given url resource.url
9-
And headers clients.alice.getAuthHeaders('PUT', resource.url)
10-
And header Content-Type = ''
11-
And request "<> a <#Something> ."
12-
When method PUT
13-
Then status 400
7+
* def resource = testContainer.reserveResource('.ttl')
8+
* def response = clients.alice.sendAuthorized('PUT', resource.url, '<> a <#Something> .', null, null)
9+
Then assert response.status == 400
1410

1511
Scenario: Server rejects POST requests without Content-Type
16-
* def containerUrl = testContainer.url
17-
Given url containerUrl
18-
And headers clients.alice.getAuthHeaders('POST', containerUrl)
19-
And header Content-Type = ''
20-
And request "<> a <#Something> ."
21-
When method POST
22-
Then status 400
12+
* def response = clients.alice.sendAuthorized('POST', testContainer.url, '<> a <#Something> .', null, null)
13+
Then assert response.status == 400
2314

2415
Scenario: Server rejects PATCH requests without Content-Type
25-
Given url resource.url
26-
And headers clients.alice.getAuthHeaders('PATCH', resource.url)
27-
And header Content-Type = ''
28-
And request "INSERT DATA { <> a <#Something> . }"
29-
When method PATCH
30-
Then status 400
31-
32-
16+
* def resource = testContainer.createResource('.ttl', karate.readAsString('../fixtures/example.ttl'), 'text/turtle')
17+
* def patch = '@prefix solid: <http://www.w3.org/ns/solid/terms#>. _:insert a solid:InsertDeletePatch; solid:inserts { <> a <http://example.org#Foo> . }.'
18+
* def response = clients.alice.sendAuthorized('PATCH', resource.url, patch, null, null)
19+
Then assert response.status == 400

protocol/writing-resource/slash-semantics-exclude.feature

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Feature: With and without trailing slash cannot co-exist
33

44
Background: Set up clients and paths
5-
* def testContainer = rootTestContainer.reserveContainer()
5+
* def testContainer = rootTestContainer.createContainer()
66
* configure followRedirects = false
77

88
Scenario: PUT container, then try resource with same name
@@ -13,47 +13,115 @@ Feature: With and without trailing slash cannot co-exist
1313
When method PUT
1414
Then assert responseStatus >= 200 && responseStatus < 300
1515
16+
# confirm there is no non-container resource with the same URI
1617
* def resourceUrl = testContainer.url + 'foo'
1718
Given url resourceUrl
1819
And headers clients.alice.getAuthHeaders('GET', resourceUrl)
1920
When method GET
2021
Then match [301, 404, 410] contains responseStatus
2122
23+
# attempt to overwrite the container with a resource of the same name
2224
Given url resourceUrl
2325
And headers clients.alice.getAuthHeaders('PUT', resourceUrl)
2426
And header Content-Type = 'text/plain'
25-
And request "Hello"
27+
And request 'Hello'
2628
When method PUT
2729
# See https://www.rfc-editor.org/rfc/rfc7231.html#page-27 for why 409 or 415
2830
Then match [409, 415] contains responseStatus
2931
30-
3132
Scenario: PUT resource, then try container with same name
3233
* def resourceUrl = testContainer.url + 'foo'
3334
Given url resourceUrl
3435
And headers clients.alice.getAuthHeaders('PUT', resourceUrl)
3536
And header Content-Type = 'text/plain'
36-
And request "Hello"
37+
And request 'Hello'
3738
When method PUT
3839
Then assert responseStatus >= 200 && responseStatus < 300
3940
41+
# confirm there is no container with the same URI
4042
* def childContainerUrl = testContainer.url + 'foo/'
4143
Given url childContainerUrl
4244
And headers clients.alice.getAuthHeaders('GET', childContainerUrl)
4345
When method GET
4446
Then match [301, 404, 410] contains responseStatus
4547
46-
* def childContainerUrl = testContainer.url + 'foo/'
48+
# attempt to overwrite the resource with a container of the same name
4749
Given url childContainerUrl
4850
And headers clients.alice.getAuthHeaders('PUT', childContainerUrl)
4951
And header Content-Type = 'text/turtle'
5052
When method PUT
5153
# See https://www.rfc-editor.org/rfc/rfc7231.html#page-27 for why 409 or 415
5254
Then match [409, 415] contains responseStatus
5355
54-
# TODO: Evil test to check various suffices.
56+
Scenario: POST container, then try resource with same name
57+
Given url testContainer.url
58+
And headers clients.alice.getAuthHeaders('POST', testContainer.url)
59+
And header Content-Type = 'text/turtle'
60+
And header Link = '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"'
61+
When method POST
62+
Then assert responseStatus >= 200 && responseStatus < 300
63+
And def childContainerUrl = responseHeaders['Location'][0]
64+
And assert childContainerUrl.endsWith('/')
5565
66+
# confirm there is no non-container resource with the same URI
67+
* def resourceUrl = childContainerUrl.slice(0, -1)
68+
Given url resourceUrl
69+
And headers clients.alice.getAuthHeaders('GET', resourceUrl)
70+
When method GET
71+
Then match [301, 404, 410] contains responseStatus
5672
73+
# attempt to overwrite the container with a resource of the same name by PUT
74+
Given url resourceUrl
75+
And headers clients.alice.getAuthHeaders('PUT', resourceUrl)
76+
And header Content-Type = 'text/plain'
77+
And request 'Hello'
78+
When method PUT
79+
# See https://www.rfc-editor.org/rfc/rfc7231.html#page-27 for why 409 or 415
80+
Then match [409, 415] contains responseStatus
5781
82+
# attempt to overwrite the container with a resource of the same name by POST with a slug
83+
Given url testContainer.url
84+
And headers clients.alice.getAuthHeaders('POST', testContainer.url)
85+
And header Slug = resourceUrl.substring(resourceUrl.lastIndexOf('/') + 1)
86+
And header Content-Type = 'text/plain'
87+
And request 'Hello'
88+
When method POST
89+
# this should either succeed (without using the slug) or fail as a conflict
90+
Then assert (responseStatus >= 200 && responseStatus < 300 && responseHeaders['Location'][0] != resourceUrl) || [409, 415].includes(responseStatus)
5891
92+
Scenario: POST resource, then try container with same name
93+
Given url testContainer.url
94+
And headers clients.alice.getAuthHeaders('POST', testContainer.url)
95+
And header Content-Type = 'text/plain'
96+
And request 'Hello'
97+
When method POST
98+
Then assert responseStatus >= 200 && responseStatus < 300
99+
And def resourceUrl = responseHeaders['Location'][0]
100+
And assert !resourceUrl.endsWith('/')
101+
102+
# confirm there is no container with the same URI
103+
* def childContainerUrl = resourceUrl + '/'
104+
Given url childContainerUrl
105+
And headers clients.alice.getAuthHeaders('GET', childContainerUrl)
106+
When method GET
107+
Then match [301, 404, 410] contains responseStatus
108+
109+
# attempt to overwrite the resource with a container of the same name by PUT
110+
Given url childContainerUrl
111+
And headers clients.alice.getAuthHeaders('PUT', childContainerUrl)
112+
And header Content-Type = 'text/turtle'
113+
When method PUT
114+
# See https://www.rfc-editor.org/rfc/rfc7231.html#page-27 for why 409 or 415
115+
Then match [409, 415] contains responseStatus
116+
117+
# attempt to overwrite the resource with a container of the same name by POST with a slug
118+
Given url testContainer.url
119+
And headers clients.alice.getAuthHeaders('POST', testContainer.url)
120+
And header Slug = resourceUrl.substring(resourceUrl.lastIndexOf('/') + 1)
121+
And header Content-Type = 'text/turtle'
122+
And header Link = '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"'
123+
When method POST
124+
# this should either succeed (without using the slug) or fail as a conflict
125+
Then assert (responseStatus >= 200 && responseStatus < 300 && responseHeaders['Location'][0] != resourceUrl + '/') || [409, 415].includes(responseStatus)
59126
127+
# TODO: Evil test to check various suffices.

run.sh

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ setup_css() {
2828
mkdir -p config
2929
cat > ./config/css-config.json <<EOF
3030
{
31-
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^3.0.0/components/context.jsonld",
31+
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^4.0.0/components/context.jsonld",
3232
"import": [
3333
"files-scs:config/app/main/default.json",
3434
"files-scs:config/app/init/initialize-prefilled-root.json",
@@ -64,12 +64,54 @@ setup_css() {
6464
"@graph": [
6565
{
6666
"comment": [
67-
"An example of what a config could look like if HTTPS is required.",
67+
"Adds CLI options --httpsKey and --httpsCert and uses those to start an HTTPS server.",
6868
"The http/server-factory import above has been omitted since that feature is set below."
6969
]
7070
},
7171
{
72-
"comment": "The key/cert values should be replaces with paths to the correct files. The 'options' block can be removed if not needed.",
72+
"@id": "urn:solid-server-app-setup:default:CliExtractor",
73+
"@type": "YargsCliExtractor",
74+
"extendedParameters": {
75+
"httpsKey": {
76+
"demandOption": true,
77+
"requiresArg": true,
78+
"type": "string",
79+
"describe": "File path to the HTTPS key."
80+
},
81+
"httpsCert": {
82+
"demandOption": true,
83+
"requiresArg": true,
84+
"type": "string",
85+
"describe": "File path to the HTTPS certificate."
86+
}
87+
}
88+
},
89+
{
90+
"comment": "Adds resolvers to assign the CLI values to the Components.js variables.",
91+
"@id": "urn:solid-server-app-setup:default:SettingsResolver",
92+
"@type": "CombinedSettingsResolver",
93+
"resolvers": [
94+
{
95+
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsKey",
96+
"CombinedSettingsResolver:_resolvers_value": {
97+
"@type": "KeyExtractor",
98+
"key": "httpsKey"
99+
}
100+
},
101+
{
102+
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsCert",
103+
"CombinedSettingsResolver:_resolvers_value": {
104+
"@type": "KeyExtractor",
105+
"key": "httpsCert"
106+
}
107+
}
108+
]
109+
},
110+
{
111+
"comment": [
112+
"Creates an HTTPS server with the settings provided via the command line.",
113+
"Replaces the example import from config/http/server-factory.https-example.json."
114+
],
73115
"@id": "urn:solid-server:default:ServerFactory",
74116
"@type": "WebSocketServerFactory",
75117
"baseServerFactory": {
@@ -78,8 +120,14 @@ setup_css() {
78120
"handler": { "@id": "urn:solid-server:default:HttpHandler" },
79121
"options_showStackTrace": { "@id": "urn:solid-server:default:variable:showStackTrace" },
80122
"options_https": true,
81-
"options_key": "/config/server.key",
82-
"options_cert": "/config/server.cert"
123+
"options_key": {
124+
"@id": "urn:solid-server:custom:variable:httpsKey",
125+
"@type": "Variable"
126+
},
127+
"options_cert": {
128+
"@id": "urn:solid-server:custom:variable:httpsCert",
129+
"@type": "Variable"
130+
}
83131
},
84132
"webSocketHandler": {
85133
"@type": "UnsecureWebSocketsProtocol",
@@ -91,16 +139,19 @@ setup_css() {
91139
EOF
92140

93141
openssl req -new -x509 -days 365 -nodes \
94-
-out config/server.cert \
95-
-keyout config/server.key \
142+
-out certs/server.cert \
143+
-keyout certs/server.key \
96144
-subj "/C=US/ST=California/L=Los Angeles/O=Security/OU=IT Department/CN=server"
97145

98146
# Assumption: You have added 'server' as a mapping of localhost in /etc/hosts
99147

100148
docker network create testnet
101149
docker run -d --name=server --network=testnet --env NODE_TLS_REJECT_UNAUTHORIZED=0 \
102-
-v "$(pwd)"/config:/config -p 443:443 -it solidproject/community-server:3 \
103-
-c /config/css-config.json --port=443 --baseUrl=https://server/
150+
-v "$(pwd)"/config:/config \
151+
-v "$(pwd)"/certs:/certs -p 443:443 -it solidproject/community-server:4 \
152+
-c /config/css-config.json \
153+
--httpsKey=/certs/server.key --httpsCert=/certs/server.cert \
154+
--port=443 --baseUrl=https://server/
104155

105156
until $(curl --output /dev/null --silent --head --fail -k https://server); do
106157
printf '.'

0 commit comments

Comments
 (0)