swift/test/cors/test-container.js
Tim Burke c5152ed4d3 Add some functional CORS tests
If you've got selenium installed (and working), the whole thing can be
automated pretty well; run main.py, wait while some windows pop up (or
use xvfb-run to run things on a virtual display), then check out what
tests were run on which browsers and whether any of them failed. Exit
code is the number of failed tests.

Includes tests against:
- Account
- Containers, with various ACLs/CORS settings
- Objects
- /info
- SLOs
- DLOs
- Symlinks

Include a gate job that runs the tests in firefox.

Areas for future work:

- Install chromium and chromedriver in the gate; tests should
  automatically pick up on the fact that it's available
- Capture the web browser's console logs, too, so we can get
  more info when things go wrong

Change-Id: Ic1d3a062419f1133c6e2f00a598867d567358c9f
2021-03-01 10:09:03 -08:00

149 lines
5.6 KiB
JavaScript

import {
runTests,
MakeRequest,
HasStatus,
HasHeaders,
HasCommonResponseHeaders,
HasNoBody
} from './harness.js'
function CheckJsonListing (resp) {
HasHeaders({ 'Content-Type': 'application/json; charset=utf-8' })(resp)
const listing = JSON.parse(resp.responseText)
for (const item of listing) {
if ('subdir' in item) {
if (Object.keys(item).length !== 1) {
throw new Error('Expected subdir to be the only key, got ' + JSON.stringify(item))
}
continue
}
const missing = ['name', 'bytes', 'content_type', 'hash', 'last_modified'].filter((key) => !(key in item))
if (missing.length) {
throw new Error('Listing item is missing expected keys ' + JSON.stringify(missing) + '; got ' + JSON.stringify(item))
}
}
return listing
}
function HasStatus200Or204 (resp) {
if (resp.status === 200) {
// NB: some browsers (like chrome) may serve HEADs from cached GETs, leading to the 200
HasStatus(200, 'OK')(resp)
} else {
HasStatus(204, 'No Content')(resp)
}
return resp
}
const expectedListing = [
'dlo',
'dlo-with-unlistable-segments',
'dlo/seg00',
'dlo/seg01',
'dlo/seg02',
'dlo/seg03',
'dlo/seg04',
'dlo/seg05',
'dlo/seg06',
'dlo/seg07',
'dlo/seg08',
'dlo/seg09',
'obj',
'slo',
'symlink-to-private',
'symlink-to-public-no-cors',
'symlink-to-public-with-cors'
]
const expectedWithDelimiter = [
'dlo',
'dlo-with-unlistable-segments',
'dlo/',
'obj',
'slo',
'symlink-to-private',
'symlink-to-public-no-cors',
'symlink-to-public-with-cors'
]
runTests('container', [
['GET format=txt',
() => MakeRequest('GET', 'public-with-cors', {}, '', {'format': 'txt'})
.then(HasStatus(200, 'OK'))
.then(HasCommonResponseHeaders)
.then(HasHeaders({ 'Content-Type': 'text/plain; charset=utf-8' }))
.then((resp) => {
const names = resp.responseText.split('\n')
if (!(names.length === expectedListing.length + 1 && names.every((name, i) => name === (i === expectedListing.length ? '' : expectedListing[i])))) {
throw new Error('Expected listing to have items ' + JSON.stringify(expectedListing) + '; got ' + JSON.stringify(names))
}
})],
['GET format=json',
() => MakeRequest('GET', 'public-with-cors', {}, '', {'format': 'json'})
.then(HasStatus(200, 'OK'))
.then(HasCommonResponseHeaders)
.then(CheckJsonListing)
.then((listing) => {
const names = listing.map((item) => 'subdir' in item ? item.subdir : item.name)
if (!(names.length === expectedListing.length && names.every((name, i) => expectedListing[i] === name))) {
throw new Error('Expected listing to have items ' + JSON.stringify(expectedListing) + '; got ' + JSON.stringify(names))
}
})],
['GET format=json&delimiter=/',
() => MakeRequest('GET', 'public-with-cors', {}, '', {'format': 'json', 'delimiter': '/'})
.then(HasStatus(200, 'OK'))
.then(HasCommonResponseHeaders)
.then(CheckJsonListing)
.then((listing) => {
const names = listing.map((item) => 'subdir' in item ? item.subdir : item.name)
if (!(names.length === expectedWithDelimiter.length && names.every((name, i) => expectedWithDelimiter[i] === name))) {
throw new Error('Expected listing to have items ' + JSON.stringify(expectedWithDelimiter) + '; got ' + JSON.stringify(names))
}
})],
['GET format=xml',
() => MakeRequest('GET', 'public-with-cors', {}, '', {'format': 'xml'})
.then(HasStatus(200, 'OK'))
.then(HasHeaders({ 'Content-Type': 'application/xml; charset=utf-8' }))
.then((resp) => {
const prefix = '<?xml version="1.0" encoding="UTF-8"?>\n<container name="public-with-cors">'
if (resp.responseText.substr(0, prefix.length) !== prefix) {
throw new Error('Expected response to start with ' + JSON.stringify(prefix) + '; got ' + resp.responseText)
}
})],
['GET Accept: json',
() => MakeRequest('GET', 'public-with-cors', { Accept: 'application/json' })
.then(HasStatus(200, 'OK'))
.then(HasCommonResponseHeaders)
.then(CheckJsonListing)
.then((listing) => {
if (listing.length !== 17) {
throw new Error('Expected exactly 17 items in listing; got ' + listing.length)
}
})],
['GET Accept: xml',
// NB: flakey on Safari -- sometimes it serves JSON from cache, *even with* a Vary: Accept header
() => MakeRequest('GET', 'public-with-cors', { Accept: 'application/xml' })
.then(HasStatus(200, 'OK'))
.then(HasHeaders({ 'Content-Type': 'application/xml; charset=utf-8' }))
.then((resp) => {
const prefix = '<?xml version="1.0" encoding="UTF-8"?>\n<container name="public-with-cors">'
if (resp.responseText.substr(0, prefix.length) !== prefix) {
throw new Error('Expected response to start with ' + JSON.stringify(prefix) + '; got ' + resp.responseText)
}
})],
['HEAD format=txt',
() => MakeRequest('HEAD', 'public-with-cors', {}, '', {'format': 'txt'})
.then(HasStatus200Or204)
.then(HasHeaders({ 'Content-Type': 'text/plain; charset=utf-8' }))
.then(HasNoBody)],
['HEAD format=json',
() => MakeRequest('HEAD', 'public-with-cors', {}, '', {'format': 'json'})
.then(HasStatus200Or204)
.then(HasHeaders({ 'Content-Type': 'application/json; charset=utf-8' }))
.then(HasNoBody)],
['HEAD format=xml',
() => MakeRequest('HEAD', 'public-with-cors', {}, '', {'format': 'xml'})
.then(HasStatus200Or204)
.then(HasHeaders({ 'Content-Type': 'application/xml; charset=utf-8' }))
.then(HasNoBody)]
])