diff --git a/lib/add-tests.coffee b/lib/add-tests.coffee index d3fdf0f..f007d0a 100644 --- a/lib/add-tests.coffee +++ b/lib/add-tests.coffee @@ -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') @@ -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 @@ -55,6 +72,8 @@ 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 @@ -62,20 +81,20 @@ addTests = (raml, tests, hooks, parent, callback, testFactory) -> 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] @@ -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 diff --git a/test/fixtures/multiple-resources.raml b/test/fixtures/multiple-resources.raml new file mode 100644 index 0000000..dbf44b6 --- /dev/null +++ b/test/fixtures/multiple-resources.raml @@ -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" } diff --git a/test/fixtures/three-levels-security-top.raml b/test/fixtures/three-levels-security-top.raml new file mode 100644 index 0000000..1c03fa8 --- /dev/null +++ b/test/fixtures/three-levels-security-top.raml @@ -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' diff --git a/test/unit/add-tests-test.coffee b/test/unit/add-tests-test.coffee index 4c91ef1..0a6e733 100644 --- a/test/unit/add-tests-test.coffee +++ b/test/unit/add-tests-test.coffee @@ -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 = [] @@ -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 = [] @@ -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'