mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-25 07:16:40 +00:00
First iteration of rspec driven swagger
This commit is contained in:
@@ -3,8 +3,8 @@ module SwaggerRails
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
@discovery_paths = Hash[
|
@discovery_paths = Hash[
|
||||||
SwaggerRails.swagger_docs.map do |name, path|
|
SwaggerRails.swagger_docs.map do |path, doc|
|
||||||
[ name, "#{root_path}#{path}" ]
|
[ "#{root_path}#{path}", doc[:info][:title] ]
|
||||||
end
|
end
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
if (url && url.length > 1) {
|
if (url && url.length > 1) {
|
||||||
url = decodeURIComponent(url[1]);
|
url = decodeURIComponent(url[1]);
|
||||||
} else {
|
} else {
|
||||||
url = "<%= @discovery_paths.values.first %>";
|
url = "<%= @discovery_paths.keys.first %>";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre load translate...
|
// Pre load translate...
|
||||||
@@ -110,8 +110,8 @@
|
|||||||
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
|
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
|
||||||
<div class='input'>
|
<div class='input'>
|
||||||
<select id="select_version">
|
<select id="select_version">
|
||||||
<% @discovery_paths.each do |name, path| %>
|
<% @discovery_paths.each do |path, title| %>
|
||||||
<option value="<%= path %>"><%= name %></option>
|
<option value="<%= path %>"><%= title %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
SwaggerRails.configure do |c|
|
SwaggerRails.configure do |c|
|
||||||
|
|
||||||
# List the names and paths (relative to config/swagger) of Swagger
|
# Define the swagger documents you'd like to expose and provide global metadata
|
||||||
# documents you'd like to expose in your swagger-ui
|
c.swagger_doc 'v1/swagger.json' do
|
||||||
c.swagger_docs = {
|
{
|
||||||
'API V1' => 'v1/swagger.json'
|
info: {
|
||||||
}
|
title: 'API V1',
|
||||||
|
version: 'v1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,10 +7,19 @@ module SwaggerRails
|
|||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :swagger_docs
|
attr_accessor :doc_factories
|
||||||
|
@@doc_factories = {}
|
||||||
|
|
||||||
@@swagger_docs = {
|
def swagger_doc(path, &block)
|
||||||
'V1' => 'v1/swagger.json'
|
@@doc_factories[path] = block
|
||||||
}
|
end
|
||||||
|
|
||||||
|
def swagger_docs
|
||||||
|
Hash[
|
||||||
|
@@doc_factories.map do |path, factory|
|
||||||
|
[ path, { swagger: '2.0' }.merge(factory.call) ]
|
||||||
|
end
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,55 +5,56 @@ module SwaggerRails
|
|||||||
module Adapter
|
module Adapter
|
||||||
|
|
||||||
def path(path_template, &block)
|
def path(path_template, &block)
|
||||||
describe(path_template, path_template: path_template, &block)
|
metadata = {
|
||||||
|
path_template: path_template
|
||||||
|
}
|
||||||
|
describe(path_template, metadata, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def operation(method, summary=nil, &block)
|
def operation(http_verb, summary=nil, &block)
|
||||||
operation_metadata = {
|
metadata = {
|
||||||
method: method,
|
http_verb: http_verb,
|
||||||
summary: summary,
|
summary: summary,
|
||||||
parameters: []
|
parameters: []
|
||||||
}
|
}
|
||||||
describe(method, operation: operation_metadata, &block)
|
describe(http_verb, metadata, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
[ :get, :post, :patch, :put, :delete, :head ].each do |http_verb|
|
||||||
|
define_method(http_verb) do |summary=nil, &block|
|
||||||
|
operation(http_verb, summary, &block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def consumes(*mime_types)
|
def consumes(*mime_types)
|
||||||
metadata[:operation][:consumes] = mime_types
|
metadata[:consumes] = mime_types
|
||||||
end
|
end
|
||||||
|
|
||||||
def produces(*mime_types)
|
def produces(*mime_types)
|
||||||
metadata[:operation][:produces] = mime_types
|
metadata[:produces] = mime_types
|
||||||
end
|
end
|
||||||
|
|
||||||
def header(name, attributes={})
|
def parameter(name, attributes={})
|
||||||
parameter(name, 'header', attributes)
|
metadata[:parameters] << { name: name.to_s }.merge(attributes)
|
||||||
end
|
end
|
||||||
|
|
||||||
def body(name, attributes={})
|
def response(code, description, &block)
|
||||||
parameter(name, 'body', attributes)
|
metadata = {
|
||||||
end
|
response_code: code,
|
||||||
|
response: {
|
||||||
def parameter(name, location, attributes={})
|
description: description
|
||||||
parameter_metadata = { name: name.to_s, in: location }.merge(attributes)
|
}
|
||||||
metadata[:operation][:parameters] << parameter_metadata
|
}
|
||||||
end
|
context(description, metadata, &block)
|
||||||
|
|
||||||
def response(status, description, &block)
|
|
||||||
response_metadata = { status: status, description: description }
|
|
||||||
context(description, response: response_metadata, &block)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_test!
|
def run_test!
|
||||||
before do |example|
|
before do |example|
|
||||||
SwaggerRails::TestVisitor.instance.act!(
|
SwaggerRails::TestVisitor.instance.submit_request!(self, example.metadata)
|
||||||
self, example.metadata[:path_template], example.metadata[:operation]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a #{metadata[:response][:status]} status" do |example|
|
it "returns a #{metadata[:response_code]} status" do |example|
|
||||||
SwaggerRails::TestVisitor.instance.assert!(
|
SwaggerRails::TestVisitor.instance.assert_response!(self, example.metadata)
|
||||||
self, example.metadata[:response]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,36 +1,60 @@
|
|||||||
require 'rspec/core/formatters/base_text_formatter'
|
require 'rspec/core/formatters/base_text_formatter'
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
module SwaggerRails
|
module SwaggerRails
|
||||||
module RSpec
|
module RSpec
|
||||||
|
|
||||||
class Formatter
|
class Formatter
|
||||||
::RSpec::Core::Formatters.register self,
|
::RSpec::Core::Formatters.register self,
|
||||||
:example_group_started,
|
|
||||||
:example_group_finished,
|
:example_group_finished,
|
||||||
:stop
|
:stop
|
||||||
|
|
||||||
def initialize(output)
|
def initialize(output)
|
||||||
@output = output
|
@output = output
|
||||||
@swagger_docs = {}
|
@swagger_docs = SwaggerRails.swagger_docs
|
||||||
@group_level = 0
|
|
||||||
|
|
||||||
@output.puts 'Generating Swagger Docs ...'
|
@output.puts 'Generating Swagger Docs ...'
|
||||||
end
|
end
|
||||||
|
|
||||||
def example_group_started(notification)
|
|
||||||
@group_level += 1
|
|
||||||
group = notification.group
|
|
||||||
metadata = group.metadata
|
|
||||||
|
|
||||||
@output.puts "group_level: #{@group_level}"
|
|
||||||
@output.puts metadata.slice(:doc, :path_template, :operation, :response).inspect
|
|
||||||
end
|
|
||||||
|
|
||||||
def example_group_finished(notification)
|
def example_group_finished(notification)
|
||||||
@group_level -= 1
|
metadata = notification.group.metadata
|
||||||
|
return unless metadata.has_key?(:response_code)
|
||||||
|
|
||||||
|
swagger_data = swagger_data_from(metadata)
|
||||||
|
swagger_doc = @swagger_docs[metadata[:docs_path]] || @swagger_docs.values.first
|
||||||
|
swagger_doc.deep_merge!(swagger_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop(notification)
|
def stop(notification)
|
||||||
|
@swagger_docs.each do |path, doc|
|
||||||
|
file_path = File.join(Rails.root, 'config/swagger', path)
|
||||||
|
|
||||||
|
File.open(file_path, 'w') do |file|
|
||||||
|
file.write(JSON.pretty_generate(doc))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@output.puts 'Swagger Doc generated'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def swagger_data_from(metadata)
|
||||||
|
{
|
||||||
|
paths: {
|
||||||
|
metadata[:path_template] => {
|
||||||
|
metadata[:http_verb] => operation_from(metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def operation_from(metadata)
|
||||||
|
metadata.slice(:summary, :consumes, :produces, :parameters).tap do |operation|
|
||||||
|
operation[:responses] = {
|
||||||
|
metadata[:response_code] => metadata[:response]
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ module SwaggerRails
|
|||||||
|
|
||||||
include Singleton
|
include Singleton
|
||||||
|
|
||||||
def act!(test, path_template, operation)
|
def submit_request!(test, metadata)
|
||||||
params_data = params_data_for(test, operation[:parameters])
|
params_data = params_data_for(test, metadata[:parameters])
|
||||||
|
|
||||||
path = build_path(path_template, params_data)
|
path = build_path(metadata[:path_template], params_data)
|
||||||
body_or_params = build_body_or_params(params_data)
|
body_or_params = build_body_or_params(params_data)
|
||||||
headers = build_headers(params_data, operation[:consumes], operation[:produces])
|
headers = build_headers(params_data, metadata[:consumes], metadata[:produces])
|
||||||
|
|
||||||
test.send(operation[:method], path, body_or_params, headers)
|
test.send(metadata[:http_verb], path, body_or_params, headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert!(test, expected_response)
|
def assert_response!(test, metadata)
|
||||||
test.assert_response(expected_response[:status].to_i)
|
test.assert_response(metadata[:response_code].to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -30,57 +30,29 @@ module SwaggerRails
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_path(path_template, params_data)
|
def build_path(path_template, params_data)
|
||||||
path = path_template.dup
|
path_template.dup.tap do |path|
|
||||||
params_data.each do |param_data|
|
params_data.each do |param_data|
|
||||||
path.sub!("\{#{param_data[:name]}\}", param_data[:value].to_s)
|
path.sub!("\{#{param_data[:name]}\}", param_data[:value].to_s)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_body_or_params(params_data)
|
def build_body_or_params(params_data)
|
||||||
body_params_data = params_data.select { |p| p[:in] == 'body' }
|
body_params_data = params_data.select { |p| p[:in] == :body }
|
||||||
return body_params_data.first[:value].to_json if body_params_data.any?
|
return body_params_data.first[:value].to_json if body_params_data.any?
|
||||||
|
|
||||||
query_params_data = params_data.select { |p| p[:in] == 'query' }
|
query_params_data = params_data.select { |p| p[:in] == :query }
|
||||||
Hash[query_params_data.map { |p| [ p[:name], p[:value] ] }]
|
Hash[query_params_data.map { |p| [ p[:name], p[:value] ] }]
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_headers(params_data, consumes, produces)
|
def build_headers(params_data, consumes, produces)
|
||||||
header_params_data = params_data.select { |p| p[:in] == 'header' }
|
header_params_data = params_data.select { |p| p[:in] == :header }
|
||||||
headers = Hash[header_params_data.map { |p| [ p[:name].underscore.upcase, p[:value] ] }]
|
headers = Hash[header_params_data.map { |p| [ p[:name].underscore.upcase, p[:value] ] }]
|
||||||
|
|
||||||
headers['ACCEPT'] = consumes.join(';') if consumes.present?
|
headers['ACCEPT'] = produces.join(';') if produces.present?
|
||||||
headers['CONTENT_TYPE'] = produces.join(';') if produces.present?
|
headers['CONTENT_TYPE'] = consumes.join(';') if consumes.present?
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#require 'swagger_rails/testing/test_case_builder'
|
|
||||||
#
|
|
||||||
#module SwaggerRails
|
|
||||||
#
|
|
||||||
# class TestVisitor
|
|
||||||
#
|
|
||||||
# def initialize(swagger)
|
|
||||||
# @swagger = swagger
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def run_test(path_template, http_method, test, &block)
|
|
||||||
# builder = TestCaseBuilder.new(path_template, http_method, @swagger)
|
|
||||||
# builder.instance_exec(&block) if block_given?
|
|
||||||
# test_data = builder.test_data
|
|
||||||
#
|
|
||||||
# test.send(http_method,
|
|
||||||
# test_data[:path],
|
|
||||||
# test_data[:params],
|
|
||||||
# test_data[:headers]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# test.assert_response(test_data[:expected_response][:status])
|
|
||||||
# test.assert_equal(test_data[:expected_response][:body], JSON.parse(test.response.body))
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#end
|
|
||||||
#
|
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ require 'rspec/core/rake_task'
|
|||||||
desc 'Generate Swagger JSON files from integration specs'
|
desc 'Generate Swagger JSON files from integration specs'
|
||||||
RSpec::Core::RakeTask.new('swagger_rails:gen') do |t|
|
RSpec::Core::RakeTask.new('swagger_rails:gen') do |t|
|
||||||
t.pattern = 'spec/integration/**/*_spec.rb'
|
t.pattern = 'spec/integration/**/*_spec.rb'
|
||||||
t.rspec_opts = [ '--format SwaggerRails::RSpec::SwaggerFormatter' ]
|
t.rspec_opts = [ '--format SwaggerRails::RSpec::Formatter', '--dry-run' ]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class Blog < ActiveRecord::Base
|
|||||||
|
|
||||||
def as_json(options)
|
def as_json(options)
|
||||||
{
|
{
|
||||||
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
content: content
|
content: content
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
SwaggerRails.configure do |c|
|
SwaggerRails.configure do |c|
|
||||||
|
|
||||||
# List the names and paths (relative to config/swagger) of Swagger
|
# Define the swagger documents you'd like to expose and provide global metadata
|
||||||
# documents you'd like to expose in your swagger-ui
|
c.swagger_doc 'v1/swagger.json' do
|
||||||
c.swagger_docs = {
|
{
|
||||||
'API V1' => 'v1/swagger.json'
|
info: {
|
||||||
}
|
title: 'API V1',
|
||||||
|
version: 'v1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,122 +1,79 @@
|
|||||||
{
|
{
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"version": "0.0.0",
|
"title": "API V1",
|
||||||
"title": "Dummy app for testing swagger_rails"
|
"version": "v1"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/blogs": {
|
"/blogs": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Creates a new Blog",
|
"summary": "creates a new blog",
|
||||||
"parameters": [
|
"consumes": [
|
||||||
{
|
"application/json"
|
||||||
"name": "X-Forwarded-For",
|
],
|
||||||
"in": "header",
|
"produces": [
|
||||||
"type": "string",
|
"application/json"
|
||||||
"default": "client1"
|
],
|
||||||
},
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "blog",
|
"name": "blog",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/Blog"
|
"type": "object",
|
||||||
}
|
"properties": {
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"201": {
|
|
||||||
"description": "Created",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/Blog"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Invalid Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/RequestError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"get": {
|
|
||||||
"description": "Searches Bloggs",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "published",
|
|
||||||
"in": "query",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": "true"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keywords",
|
|
||||||
"in": "query",
|
|
||||||
"type": "string",
|
|
||||||
"default": "Ruby on Rails"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Ok",
|
|
||||||
"schema": {
|
|
||||||
"type": "array",
|
|
||||||
"item": {
|
|
||||||
"$ref": "#/definitions/Blog"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
"/blogs/{id}": {
|
|
||||||
"get": {
|
|
||||||
"description": "Retrieves a specific Blog by unique ID",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"in": "path",
|
|
||||||
"type": "string",
|
|
||||||
"default": "123"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Ok",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/Blog"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"definitions": {
|
|
||||||
"Blog": {
|
|
||||||
"properties": {
|
|
||||||
"title": {
|
"title": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"format": "date-time"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"example": {
|
|
||||||
"title": "Test Blog",
|
|
||||||
"content": "Hello World"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RequestError": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "array",
|
|
||||||
"item": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"example": {
|
|
||||||
"title": [ "is required" ]
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "valid request"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "invalid request"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"get": {
|
||||||
|
"summary": "searches existing blogs",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "valid request"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/blogs/{id}": {
|
||||||
|
"get": {
|
||||||
|
"summary": "retreives a specific blog",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "blog found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe 'Blogs API', doc: 'blogs/v1' do
|
describe 'Blogs API', docs_path: 'blogs/v1/swagger.json' do
|
||||||
|
|
||||||
path '/blogs' do
|
path '/blogs' do
|
||||||
|
|
||||||
operation 'post', 'creates a new blog' do
|
post 'creates a new blog' do
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
body :blog
|
parameter :blog, :in => :body, schema: {
|
||||||
|
:type => :object,
|
||||||
|
:properties => {
|
||||||
|
title: { type: 'string' },
|
||||||
|
content: { type: 'string' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let(:blog) { { title: 'foo', content: 'bar' } }
|
let(:blog) { { title: 'foo', content: 'bar' } }
|
||||||
|
|
||||||
@@ -21,7 +27,7 @@ describe 'Blogs API', doc: 'blogs/v1' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
operation 'get', 'searches existing blogs' do
|
get 'searches existing blogs' do
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
response '200', 'valid request' do
|
response '200', 'valid request' do
|
||||||
@@ -31,9 +37,9 @@ describe 'Blogs API', doc: 'blogs/v1' do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/blogs/{id}' do
|
path '/blogs/{id}' do
|
||||||
operation 'get', 'retreives a specific blog' do
|
get 'retreives a specific blog' do
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter :id, 'path'
|
parameter :id, :in => :path, :type => :string
|
||||||
|
|
||||||
response '200', 'blog found' do
|
response '200', 'blog found' do
|
||||||
let(:blog) { Blog.create(title: 'foo', content: 'bar') }
|
let(:blog) { Blog.create(title: 'foo', content: 'bar') }
|
||||||
|
|||||||
@@ -50,4 +50,7 @@ RSpec.configure do |config|
|
|||||||
config.filter_rails_from_backtrace!
|
config.filter_rails_from_backtrace!
|
||||||
# arbitrary gems may also be filtered via:
|
# arbitrary gems may also be filtered via:
|
||||||
# config.filter_gems_from_backtrace("gem name")
|
# config.filter_gems_from_backtrace("gem name")
|
||||||
|
|
||||||
|
require 'swagger_rails/rspec/adapter'
|
||||||
|
config.extend SwaggerRails::RSpec::Adapter
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,7 +89,4 @@ RSpec.configure do |config|
|
|||||||
# as the one that triggered the failure.
|
# as the one that triggered the failure.
|
||||||
Kernel.srand config.seed
|
Kernel.srand config.seed
|
||||||
=end
|
=end
|
||||||
|
|
||||||
require 'swagger_rails/rspec/adapter'
|
|
||||||
config.extend SwaggerRails::RSpec::Adapter
|
|
||||||
end
|
end
|
||||||
|
|||||||
107
spec/test_visitor_spec.rb
Normal file
107
spec/test_visitor_spec.rb
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
require 'swagger_rails/test_visitor'
|
||||||
|
|
||||||
|
module SwaggerRails
|
||||||
|
|
||||||
|
describe TestVisitor do
|
||||||
|
let(:test) { spy('test') }
|
||||||
|
subject { described_class.instance }
|
||||||
|
|
||||||
|
describe '#submit_request!' do
|
||||||
|
before { subject.submit_request!(test, metadata) }
|
||||||
|
|
||||||
|
context 'always' do
|
||||||
|
let(:metadata) do
|
||||||
|
{
|
||||||
|
path_template: '/resource',
|
||||||
|
http_verb: :get,
|
||||||
|
parameters: []
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'dispatches the request to the provided test object' do
|
||||||
|
expect(test).to have_received(:get)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given path parameters' do
|
||||||
|
let(:metadata) do
|
||||||
|
allow(test).to receive(:id).and_return(1)
|
||||||
|
return {
|
||||||
|
path_template: '/resource/{id}',
|
||||||
|
http_verb: :get,
|
||||||
|
parameters: [ { name: 'id', :in => :path, type: 'string' } ]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'builds the path from values on the test object' do
|
||||||
|
expect(test).to have_received(:get).with('/resource/1', {}, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given body parameters' do
|
||||||
|
let(:metadata) do
|
||||||
|
allow(test).to receive(:resource).and_return({ foo: 'bar' })
|
||||||
|
return {
|
||||||
|
path_template: '/resource',
|
||||||
|
http_verb: :post,
|
||||||
|
consumes: [ 'application/json' ],
|
||||||
|
parameters: [ { name: 'resource', :in => :body, schema: { type: 'object' } } ]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'builds a body from value on the test object' do
|
||||||
|
expect(test).to have_received(:post).with(
|
||||||
|
'/resource',
|
||||||
|
"{\"foo\":\"bar\"}",
|
||||||
|
{ 'CONTENT_TYPE' => 'application/json' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given query parameters' do
|
||||||
|
let(:metadata) do
|
||||||
|
allow(test).to receive(:type).and_return('foo')
|
||||||
|
return {
|
||||||
|
path_template: '/resource',
|
||||||
|
http_verb: :get,
|
||||||
|
parameters: [ { name: 'type', :in => :query, type: 'string' } ]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'builds query params from values on the test object' do
|
||||||
|
expect(test).to have_received(:get).with('/resource', { 'type' => 'foo' }, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given header parameters' do
|
||||||
|
let(:metadata) do
|
||||||
|
allow(test).to receive(:date).and_return('2000-01-01')
|
||||||
|
return {
|
||||||
|
path_template: '/resource',
|
||||||
|
http_verb: :get,
|
||||||
|
produces: [ 'application/json' ],
|
||||||
|
parameters: [ { name: 'Date', :in => :header, type: 'string' } ]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'builds request headers from values on the test object' do
|
||||||
|
expect(test).to have_received(:get).with(
|
||||||
|
'/resource',
|
||||||
|
{},
|
||||||
|
{ 'DATE' => '2000-01-01', 'ACCEPT' => 'application/json' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#assert_response' do
|
||||||
|
before { subject.assert_response!(test, metadata) }
|
||||||
|
let(:metadata) { { response_code: '200' } }
|
||||||
|
|
||||||
|
it 'dispatches the assert to the provided test object' do
|
||||||
|
expect(test).to have_received(:assert_response).with(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
require 'swagger_rails/testing/test_case_builder'
|
|
||||||
|
|
||||||
module SwaggerRails
|
|
||||||
|
|
||||||
describe TestCaseBuilder do
|
|
||||||
subject { described_class.new(path, method, swagger) }
|
|
||||||
let(:swagger) do
|
|
||||||
file_path = File.join(Rails.root, 'config/swagger', 'v1/swagger.json')
|
|
||||||
JSON.parse(File.read(file_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#test_data' do
|
|
||||||
let(:test_data) { subject.test_data }
|
|
||||||
|
|
||||||
context 'swagger includes basePath' do
|
|
||||||
before { swagger['basePath'] = '/foobar' }
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'post' }
|
|
||||||
|
|
||||||
it 'includes a path prefixed with basePath' do
|
|
||||||
expect(test_data[:path]).to eq('/foobar/blogs')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation has path params' do
|
|
||||||
let(:path) { '/blogs/{id}' }
|
|
||||||
let(:method) { 'get' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes a path built from 'default' values" do
|
|
||||||
expect(test_data[:path]).to eq('/blogs/123')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'values explicitly set' do
|
|
||||||
before { subject.set id: '456' }
|
|
||||||
it 'includes a path built from set values' do
|
|
||||||
expect(test_data[:path]).to eq('/blogs/456')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation has query params' do
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'get' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes params built from 'default' values" do
|
|
||||||
expect(test_data[:params]).to eq({ 'published' => 'true', 'keywords' => 'Ruby on Rails' })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'values explicitly set' do
|
|
||||||
before { subject.set keywords: 'Java' }
|
|
||||||
it 'includes params build from set values' do
|
|
||||||
expect(test_data[:params]).to eq({ 'published' => 'true', 'keywords' => 'Java' })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation has body param' do
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'post' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes params string based on schema 'example'" do
|
|
||||||
expect(test_data[:params]).to eq({ 'title' => 'Test Blog', 'content' => 'Hello World' }.to_json)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'values explicitly set' do
|
|
||||||
before { subject.set blog: { 'title' => 'foobar' } }
|
|
||||||
it 'includes params string based on set value' do
|
|
||||||
expect(test_data[:params]).to eq({ 'title' => 'foobar' }.to_json)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation has header params' do
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'post' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes headers built from 'default' values" do
|
|
||||||
expect(test_data[:headers]).to eq({
|
|
||||||
'X-Forwarded-For' => 'client1',
|
|
||||||
'CONTENT_TYPE' => 'application/json',
|
|
||||||
'ACCEPT' => 'application/json'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'values explicitly params' do
|
|
||||||
before { subject.set 'X-Forwarded-For' => '192.168.1.1' }
|
|
||||||
it 'includes headers built from set values' do
|
|
||||||
expect(test_data[:headers]).to eq({
|
|
||||||
'X-Forwarded-For' => '192.168.1.1',
|
|
||||||
'CONTENT_TYPE' => 'application/json',
|
|
||||||
'ACCEPT' => 'application/json'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation returns an object' do
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'post' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes expected_response based on spec'd 2xx status" do
|
|
||||||
expect(test_data[:expected_response][:status]).to eq(201)
|
|
||||||
expect(test_data[:expected_response][:body]).to eq({ 'title' => 'Test Blog', 'content' => 'Hello World' })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'expected status explicitly set' do
|
|
||||||
before { subject.expect 400 }
|
|
||||||
it "includes expected_response based on set status" do
|
|
||||||
expect(test_data[:expected_response][:status]).to eq(400)
|
|
||||||
expect(test_data[:expected_response][:body]).to eq({ 'title' => [ 'is required' ] })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'operation returns an array' do
|
|
||||||
let(:path) { '/blogs' }
|
|
||||||
let(:method) { 'get' }
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
it "includes expected_response based on spec'd 2xx status" do
|
|
||||||
expect(test_data[:expected_response][:status]).to eq(200)
|
|
||||||
expect(test_data[:expected_response][:body]).to eq([ { 'title' => 'Test Blog', 'content' => 'Hello World' } ])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
require 'swagger_rails/testing/test_visitor'
|
|
||||||
|
|
||||||
module SwaggerRails
|
|
||||||
|
|
||||||
describe TestVisitor do
|
|
||||||
subject { described_class.new(swagger) }
|
|
||||||
let(:swagger) do
|
|
||||||
file_path = File.join(Rails.root, 'config/swagger', 'v1/swagger.json')
|
|
||||||
JSON.parse(File.read(file_path))
|
|
||||||
end
|
|
||||||
let(:test) { spy('test') }
|
|
||||||
|
|
||||||
describe '#run_test' do
|
|
||||||
before do
|
|
||||||
allow(test).to receive(:response).and_return(OpenStruct.new(body: "{}"))
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'by default' do
|
|
||||||
before { subject.run_test('/blogs', 'post', test) }
|
|
||||||
|
|
||||||
it "submits request based on 'default' and 'example' param values" do
|
|
||||||
expect(test).to have_received(:post).with(
|
|
||||||
'/blogs',
|
|
||||||
{ 'title' => 'Test Blog', 'content' => 'Hello World' }.to_json,
|
|
||||||
{ 'X-Forwarded-For' => 'client1', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "asserts response matches spec'd 2xx status" do
|
|
||||||
expect(test).to have_received(:assert_response).with(201)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "asserts response body matches schema 'example' for 2xx status" do
|
|
||||||
expect(test).to have_received(:assert_equal).with(
|
|
||||||
{ 'title' => 'Test Blog', 'content' => 'Hello World' },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'param values explicitly provided' do
|
|
||||||
before do
|
|
||||||
subject.run_test('/blogs', 'post', test) do
|
|
||||||
set blog: { 'title' => 'foobar' }
|
|
||||||
set 'X-Forwarded-For' => '192.168.1.1'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'submits a request based on provided param values' do
|
|
||||||
expect(test).to have_received(:post).with(
|
|
||||||
'/blogs',
|
|
||||||
{ 'title' => 'foobar' }.to_json,
|
|
||||||
{ 'X-Forwarded-For' => '192.168.1.1', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'expected status explicitly set' do
|
|
||||||
before do
|
|
||||||
subject.run_test('/blogs', 'post', test) do
|
|
||||||
expect 400
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "asserts response matches set status" do
|
|
||||||
expect(test).to have_received(:assert_response).with(400)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "asserts response body matches schema 'example' for set status" do
|
|
||||||
expect(test).to have_received(:assert_equal).with(
|
|
||||||
{ 'title' => [ 'is required' ] },
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user