Skip to content

Commit

Permalink
Fix #184 abao should understand security schemes
Browse files Browse the repository at this point in the history
  • Loading branch information
Brock Pytlik committed Oct 14, 2016
1 parent a4ac7ee commit 841c2de
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 10 deletions.
58 changes: 48 additions & 10 deletions lib/add-tests.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ async = require 'async'
_ = require 'underscore'
csonschema = require 'csonschema'

selectSchemes = (names, schemes) ->
return _.pick(schemes, names)

parseSchema = (source) ->
if source.contains('$schema')
Expand Down Expand Up @@ -32,16 +34,31 @@ addTests = (raml, tests, hooks, parent, callback, testFactory) ->

return callback() unless raml.resources

parent ?= {
path: "",
params: {}
}

top_securedBy = raml.securedBy ? parent.securedBy

if not parent.security_schemes?
parent.security_schemes = {}
for scheme_map in raml.securitySchemes ? []
for scheme_name, scheme of scheme_map
parent.security_schemes[scheme_name] ?= []
parent.security_schemes[scheme_name].push(scheme)

# Iterate endpoint
async.each raml.resources, (resource, callback) ->
path = resource.relativeUri
params = {}
query = {}

resource_securedBy = resource.securedBy ? top_securedBy

# Apply parent properties
if parent
path = parent.path + path
params = _.clone parent.params
path = parent.path + path
params = _.clone parent.params

# Setup param
if resource.uriParameters
Expand All @@ -55,27 +72,29 @@ addTests = (raml, tests, hooks, parent, callback, testFactory) ->
# Iterate response method
async.each resource.methods, (api, callback) ->
method = api.method.toUpperCase()
headers = parseHeaders(api.headers)
method_securedBy = api.securedBy ? resource_securedBy

# Setup query
if api.queryParameters
for qkey, qvalue of api.queryParameters
if (!!qvalue.required)
query[qkey] = qvalue.example


# Iterate response status
for status, res of api.responses

buildTest = (status, res, security) ->
testName = "#{method} #{path} -> #{status}"
if security?
testName += " (#{security})"
if testName in hooks.skippedTests
return null

# Append new test to tests
test = testFactory.create(testName, hooks.contentTests[testName])
tests.push test

# Update test.request
test.request.path = path
test.request.method = method
test.request.headers = parseHeaders(api.headers)
test.request.headers = headers

# select compatible content-type in request body (to support vendor tree types, i.e. application/vnd.api+json)
contentType = (type for type of api.body when type.match(/^application\/(.*\+)?json/i))?[0]
Expand All @@ -102,13 +121,32 @@ addTests = (raml, tests, hooks, parent, callback, testFactory) ->
contentType = (type for type of res.body when type.match(/^application\/(.*\+)?json/i))?[0]
if res.body[contentType]?.schema
test.response.schema = parseSchema res.body[contentType].schema
return test

# Iterate response status
for status, res of api.responses
t = buildTest(status, res)
if t?
tests.push t

for scheme, lst of selectSchemes(method_securedBy, parent.security_schemes)
for l in lst
for status, res of l.describedBy?.responses ? {}
t = buildTest(status, res, scheme)
if t?
tests.push t

callback()
, (err) ->
return callback(err) if err

# Recursive
addTests resource, tests, hooks, {path, params}, callback, testFactory
new_parent = {
path, params,
securedBy: resource_securedBy,
security_schemes: parent.security_schemes
}
addTests resource, tests, hooks, new_parent, callback, testFactory
, callback


Expand Down
43 changes: 43 additions & 0 deletions test/fixtures/multiple-resources.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#%RAML 0.8

title: World Music API
baseUri: http://example.api.com/{version}
version: v1

/songs:
/song1:
get:
responses:
200:
body:
application/json:
schema: |
{ "$schema": "http://json-schema.org/schema",
"type": "object",
"description": "A canonical song",
"properties": {
"title": { "type": "string" },
"artist": { "type": "string" }
},
"required": [ "title", "artist" ]
}
example: |
{ "title": "A Beautiful Day", "artist": "Mike" }
/song2:
get:
responses:
200:
body:
application/json:
schema: |
{ "$schema": "http://json-schema.org/schema",
"type": "object",
"description": "A canonical song",
"properties": {
"title": { "type": "string" },
"artist": { "type": "string" }
},
"required": [ "title", "artist" ]
}
example: |
{ "title": "A Beautiful Day", "artist": "Mike" }
53 changes: 53 additions & 0 deletions test/fixtures/three-levels-security-top.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#%RAML 0.8

title: World Music API
baseUri: http://example.api.com/{version}
version: v1

securitySchemes:
- oauth_2_0: !include ./oauth_2_0.yml
- another_oauth_2_0: !include ./oauth_2_0.yml
- third_oauth_2_0: !include ./oauth_2_0.yml

securedBy: [oauth_2_0, another_oauth_2_0]

/machines:
get:
responses:
200:
body:
application/json:
schema: |
[
type: 'string'
name: 'string'
]
/{machine_id}:
securedBy: [oauth_2_0]
uriParameters:
machine_id:
description: |
The ID of the Machine
type: string
example: '1'
delete:
responses:
204:
/parts:
get:
responses:
200:
body:
application/json:
schema: |
type: 'string'
name: 'string'
put:
securedBy: [third_oauth_2_0]
responses:
200:
body:
application/json:
schema: |
type: 'string'
name: 'string'
110 changes: 110 additions & 0 deletions test/unit/add-tests-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,35 @@ describe '#addTests', ->
assert.isNull res.headers
assert.isNull res.body

describe 'when hooks skip one POST', ->
tests = []
testFactory = new TestFactory()
callback = ''

before (done) ->

hooks.skippedTests = ["POST /machines -> 201"]
ramlParser.loadFile("#{RAML_DIR}/1-get-1-post.raml")
.then (data) ->
callback = sinon.stub()
callback.returns(done())

addTests data, tests, hooks, callback, testFactory
, done
return
after ->
tests = []
hooks.skippedTests = []

it 'should run callback', ->
assert.ok callback.called

it 'should add 1 test', ->
assert.lengthOf tests, 1

it 'should set test.name', ->
assert.equal tests[0].name, 'GET /machines -> 200'

describe 'when RAML includes multiple referencing schemas', ->

tests = []
Expand Down Expand Up @@ -247,6 +276,57 @@ describe '#addTests', ->
assert.deepEqual test.request.params,
machine_id: '1'

describe 'when RAML has securedBy set at top level', ->
tests = []
testFactory = new TestFactory()
callback = ''

before (done) ->

ramlParser.loadFile(
"#{RAML_DIR}/three-levels-security-top.raml"
).then (data) ->
callback = sinon.stub()
callback.returns(done())

addTests data, tests, hooks, callback, testFactory
, done
return
after ->
tests = []

it 'should run callback', ->
assert.ok callback.called

it 'should added 16 tests', ->
assert.lengthOf tests, 16

it 'should set test.name', ->
assert.equal tests[0].name, 'GET /machines -> 200'
assert.equal tests[1].name, 'GET /machines -> 401 (oauth_2_0)'
assert.equal tests[2].name, 'GET /machines -> 403 (oauth_2_0)'
assert.equal tests[3].name, 'GET /machines -> 401 (another_oauth_2_0)'
assert.equal tests[4].name, 'GET /machines -> 403 (another_oauth_2_0)'
assert.equal tests[5].name, 'DELETE /machines/{machine_id} -> 204'
assert.equal tests[6].name,
'DELETE /machines/{machine_id} -> 401 (oauth_2_0)'
assert.equal tests[7].name,
'DELETE /machines/{machine_id} -> 403 (oauth_2_0)'
assert.equal tests[8].name, 'GET /machines/{machine_id}/parts -> 200'
assert.equal tests[9].name,
'GET /machines/{machine_id}/parts -> 401 (oauth_2_0)'
assert.equal tests[10].name,
'GET /machines/{machine_id}/parts -> 403 (oauth_2_0)'
assert.equal tests[11].name,
'GET /machines/{machine_id}/parts -> 401 (another_oauth_2_0)'
assert.equal tests[12].name,
'GET /machines/{machine_id}/parts -> 403 (another_oauth_2_0)'
assert.equal tests[13].name, 'PUT /machines/{machine_id}/parts -> 200'
assert.equal tests[14].name,
'PUT /machines/{machine_id}/parts -> 401 (third_oauth_2_0)'
assert.equal tests[15].name,
'PUT /machines/{machine_id}/parts -> 403 (third_oauth_2_0)'

describe 'when RAML has resource not defined method', ->

tests = []
Expand Down Expand Up @@ -408,3 +488,33 @@ describe '#addTests', ->

it 'should not append query parameters', ->
assert.deepEqual tests[0].request.query, {}

describe 'when RAML has multiple resources', ->

tests = []
testFactory = new TestFactory()
callback = ''

before (done) ->
ramlParser.loadFile("#{RAML_DIR}/multiple-resources.raml")
.then (data) ->
console.error("got data")
callback = sinon.stub()
callback.returns(done())

console.error("calling addTests")
addTests data, tests, hooks, callback, testFactory
, done
return
after ->
tests = []

it 'should run callback', ->
assert.ok callback.called

it 'should add 2 tests', ->
assert.lengthOf tests, 2

it 'should set test.name', ->
assert.equal tests[0].name, 'GET /songs/song1 -> 200'
assert.equal tests[1].name, 'GET /songs/song2 -> 200'

0 comments on commit 841c2de

Please sign in to comment.