diff --git a/Gemfile b/Gemfile
index d4c5bc7..670cece 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,6 +13,7 @@ gemspec
# To use a debugger
# gem 'debugger', group: [:development, :test]
+gem 'sqlite3'
gem 'pry'
gem 'rspec-rails'
gem 'generator_spec'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9863a5b..a957712 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -105,6 +105,7 @@ GEM
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.11)
thor (0.19.1)
tilt (1.4.1)
treetop (1.4.15)
@@ -119,4 +120,5 @@ DEPENDENCIES
generator_spec
pry
rspec-rails
+ sqlite3
swagger_rails!
diff --git a/lib/swagger_rails/testing/test_data_builder.rb b/lib/swagger_rails/testing/test_case_builder.rb
similarity index 56%
rename from lib/swagger_rails/testing/test_data_builder.rb
rename to lib/swagger_rails/testing/test_case_builder.rb
index 0433475..4336a6a 100644
--- a/lib/swagger_rails/testing/test_data_builder.rb
+++ b/lib/swagger_rails/testing/test_case_builder.rb
@@ -1,13 +1,12 @@
module SwaggerRails
-
- class TestDataBuilder
+
+ class TestCaseBuilder
def initialize(path_template, http_method, swagger)
@path_template = path_template
@http_method = http_method
@swagger = swagger
@param_values = {}
- @expected_status = nil
end
def set(param_values)
@@ -15,7 +14,7 @@ module SwaggerRails
end
def expect(status)
- @expected_status = status
+ @expected_status = status.to_s
end
def test_data
@@ -33,7 +32,7 @@ module SwaggerRails
private
def find_operation!
- keys = [ 'paths', @path_template, @http_method ]
+ keys = [ 'paths', @path_template, @http_method ]
operation = find_hash_item!(@swagger, keys)
operation || (raise MetadataError.new(keys))
end
@@ -52,35 +51,59 @@ module SwaggerRails
end
def build_params(parameters)
- {}.tap do |params|
- params.merge!(param_values_for(parameters, 'query'))
- body_param_values = param_values_for(parameters, 'body')
- params.merge!(body_param_values.values.first) if body_param_values.any?
- end
+ body_param_values = param_values_for(parameters, 'body')
+ return body_param_values.values.first.to_json if body_param_values.any?
+ param_values_for(parameters, 'query')
end
def build_headers(parameters)
param_values_for(parameters, 'header')
+ .merge({
+ 'CONTENT_TYPE' => 'application/json',
+ 'ACCEPT' => 'application/json'
+ })
end
def build_expected_response(responses)
+ status = @expected_status || responses.keys.find { |k| k.start_with?('2') }
+ response = responses[status] || (raise MetadataError.new('paths', @path_template, @http_method, 'responses', status))
+ {
+ status: status.to_i,
+ body: response_body_for(response)
+ }
end
def param_values_for(parameters, location)
applicable_parameters = parameters.select { |p| p['in'] == location }
- Hash[applicable_parameters.map { |p| [ p['name'], value_for(p) ] }]
+ Hash[applicable_parameters.map { |p| [ p['name'], param_value_for(p) ] }]
end
- def value_for(param)
- return @param_values[param['name']] if @param_values.has_key?(param['name'])
- return param['default'] unless param['in'] == 'body'
- schema_for(param['schema'])['example']
+ def param_value_for(parameter)
+ return @param_values[parameter['name']] if @param_values.has_key?(parameter['name'])
+ return parameter['default'] unless parameter['in'] == 'body'
+ schema = schema_for(parameter['schema'])
+ schema_example_for(schema)
+ end
+
+ def response_body_for(response)
+ return nil if response['schema'].nil?
+ schema = schema_for(response['schema'])
+ schema_example_for(schema)
end
def schema_for(schema_or_ref)
return schema_or_ref if schema_or_ref['$ref'].nil?
@swagger['definitions'][schema_or_ref['$ref'].sub('#/definitions/', '')]
end
+
+ def schema_example_for(schema)
+ return schema['example'] if schema['example'].present?
+ # If an array, try construct from the item example
+ if schema['type'] == 'array' && schema['item'].present?
+ item_schema = schema_for(schema['item'])
+ return [ schema_example_for(item_schema) ]
+ end
+ end
end
class MetadataError < StandardError
diff --git a/lib/swagger_rails/testing/test_visitor.rb b/lib/swagger_rails/testing/test_visitor.rb
index f024b5a..9603e1c 100644
--- a/lib/swagger_rails/testing/test_visitor.rb
+++ b/lib/swagger_rails/testing/test_visitor.rb
@@ -1,4 +1,4 @@
-require 'swagger_rails/testing/test_data_builder'
+require 'swagger_rails/testing/test_case_builder'
module SwaggerRails
@@ -9,7 +9,7 @@ module SwaggerRails
end
def run_test(path_template, http_method, test, &block)
- builder = TestDataBuilder.new(path_template, http_method, @swagger)
+ builder = TestCaseBuilder.new(path_template, http_method, @swagger)
builder.instance_exec(&block) if block_given?
test_data = builder.test_data
@@ -19,7 +19,8 @@ module SwaggerRails
test_data[:headers]
)
- test.assert_response(test_data[:expected_status])
+ test.assert_response(test_data[:expected_response][:status])
+ test.assert_equal(test_data[:expected_response][:body], JSON.parse(test.response.body))
end
end
end
diff --git a/spec/dummy/app/controllers/blogs_controller.rb b/spec/dummy/app/controllers/blogs_controller.rb
index ea51be9..a36145a 100644
--- a/spec/dummy/app/controllers/blogs_controller.rb
+++ b/spec/dummy/app/controllers/blogs_controller.rb
@@ -1,14 +1,22 @@
class BlogsController < ApplicationController
+ wrap_parameters Blog
+ respond_to :json
+ # POST /blogs
def create
- render json: {}
+ @blog = Blog.create(params[:blog])
+ respond_with @blog
end
+ # GET /blogs
def index
- render json: {}
+ @blogs = Blog.all
+ respond_with @blogs
end
+ # GET /blogs/1
def show
- render json: {}
+ @blog = Blog.find(params[:id])
+ respond_with @blog
end
end
diff --git a/spec/dummy/app/models/blog.rb b/spec/dummy/app/models/blog.rb
new file mode 100644
index 0000000..2f4b8f8
--- /dev/null
+++ b/spec/dummy/app/models/blog.rb
@@ -0,0 +1,10 @@
+class Blog < ActiveRecord::Base
+ attr_accessible :content, :title
+
+ def as_json(options)
+ {
+ title: title,
+ content: content
+ }
+ end
+end
diff --git a/spec/dummy/app/views/layouts/application.html.erb b/spec/dummy/app/views/layouts/application.html.erb
deleted file mode 100644
index 593a778..0000000
--- a/spec/dummy/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- Dummy
- <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
- <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
- <%= csrf_meta_tags %>
-
-
-
-<%= yield %>
-
-
-
diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb
index f0d61fd..11d35dc 100644
--- a/spec/dummy/config/application.rb
+++ b/spec/dummy/config/application.rb
@@ -1,7 +1,7 @@
require File.expand_path('../boot', __FILE__)
# Pick the frameworks you want:
-# require "active_record/railtie"
+require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
diff --git a/spec/dummy/config/database.yml b/spec/dummy/config/database.yml
new file mode 100644
index 0000000..dc954e3
--- /dev/null
+++ b/spec/dummy/config/database.yml
@@ -0,0 +1,25 @@
+#SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb
index 2636b39..99850bd 100644
--- a/spec/dummy/config/routes.rb
+++ b/spec/dummy/config/routes.rb
@@ -1,5 +1,5 @@
Rails.application.routes.draw do
- mount SwaggerRails::Engine => '/api-docs'
+ resources :blogs, defaults: { :format => :json }
- resources :blogs, only: [ :create, :index, :show ]
+ mount SwaggerRails::Engine => '/api-docs'
end
diff --git a/spec/dummy/config/swagger/v1/swagger.json b/spec/dummy/config/swagger/v1/swagger.json
index d5584c7..8b859d9 100644
--- a/spec/dummy/config/swagger/v1/swagger.json
+++ b/spec/dummy/config/swagger/v1/swagger.json
@@ -24,8 +24,8 @@
}
],
"responses": {
- "200": {
- "description": "Ok",
+ "201": {
+ "description": "Created",
"schema": {
"$ref": "#/definitions/Blog"
}
@@ -58,7 +58,10 @@
"200": {
"description": "Ok",
"schema": {
- "$ref": "#/definitions/Blog"
+ "type": "array",
+ "item": {
+ "$ref": "#/definitions/Blog"
+ }
}
}
}
diff --git a/spec/dummy/db/migrate/20160218212104_create_blogs.rb b/spec/dummy/db/migrate/20160218212104_create_blogs.rb
new file mode 100644
index 0000000..81d9813
--- /dev/null
+++ b/spec/dummy/db/migrate/20160218212104_create_blogs.rb
@@ -0,0 +1,10 @@
+class CreateBlogs < ActiveRecord::Migration
+ def change
+ create_table :blogs do |t|
+ t.string :title
+ t.text :content
+
+ t.timestamps
+ end
+ end
+end
diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb
new file mode 100644
index 0000000..082bb8d
--- /dev/null
+++ b/spec/dummy/db/schema.rb
@@ -0,0 +1,23 @@
+# encoding: UTF-8
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20160218212104) do
+
+ create_table "blogs", :force => true do |t|
+ t.string "title"
+ t.text "content"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
+ end
+
+end
diff --git a/spec/dummy/test/integration/v1_contract_test.rb b/spec/dummy/test/integration/v1_contract_test.rb
index 01f3fc2..586d94b 100644
--- a/spec/dummy/test/integration/v1_contract_test.rb
+++ b/spec/dummy/test/integration/v1_contract_test.rb
@@ -5,5 +5,14 @@ class V1ContractTest < ActionDispatch::IntegrationTest
include SwaggerRails::TestHelpers
swagger_doc 'v1/swagger.json'
- swagger_test_all
+#
+# test '/blogs post' do
+# swagger_test '/blogs', 'post'
+# end
+
+ test '/blogs get' do
+ blog = Blog.create(title: 'Test Blog', content: 'Hello World')
+
+ swagger_test '/blogs', 'get'
+ end
end
diff --git a/spec/testing/test_data_builder_spec.rb b/spec/testing/test_case_builder_spec.rb
similarity index 57%
rename from spec/testing/test_data_builder_spec.rb
rename to spec/testing/test_case_builder_spec.rb
index f403107..d5f7404 100644
--- a/spec/testing/test_data_builder_spec.rb
+++ b/spec/testing/test_case_builder_spec.rb
@@ -1,9 +1,9 @@
require 'rails_helper'
-require 'swagger_rails/testing/test_data_builder'
+require 'swagger_rails/testing/test_case_builder'
module SwaggerRails
- describe TestDataBuilder do
+ describe TestCaseBuilder do
subject { described_class.new(path, method, swagger) }
let(:swagger) do
file_path = File.join(Rails.root, 'config/swagger', 'v1/swagger.json')
@@ -59,20 +59,20 @@ module SwaggerRails
end
end
- context 'operation has body params' do
+ context 'operation has body param' do
let(:path) { '/blogs' }
let(:method) { 'post' }
context 'by default' do
- it "includes params built from 'default' values" do
- expect(test_data[:params]).to eq({ 'title' => 'Test Blog', 'content' => 'Hello World' })
+ 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 build from set values' do
- expect(test_data[:params]).to eq({ 'title' => 'foobar' })
+ it 'includes params string based on set value' do
+ expect(test_data[:params]).to eq({ 'title' => 'foobar' }.to_json)
end
end
end
@@ -83,14 +83,54 @@ module SwaggerRails
context 'by default' do
it "includes headers built from 'default' values" do
- expect(test_data[:headers]).to eq({ 'X-Forwarded-For' => 'client1' })
+ 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' })
+ 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
diff --git a/spec/testing/test_visitor_spec.rb b/spec/testing/test_visitor_spec.rb
index 18ce6ef..7409fe9 100644
--- a/spec/testing/test_visitor_spec.rb
+++ b/spec/testing/test_visitor_spec.rb
@@ -12,19 +12,30 @@ module SwaggerRails
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' },
- { 'X-Forwarded-For' => 'client1' }
+ { 'title' => 'Test Blog', 'content' => 'Hello World' }.to_json,
+ { 'X-Forwarded-For' => 'client1', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
)
end
- it "asserts response matches first 2xx status in operation 'responses'" do
- expect(test).to have_received(:assert_response).with(200)
+ 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
@@ -39,22 +50,29 @@ module SwaggerRails
it 'submits a request based on provided param values' do
expect(test).to have_received(:post).with(
'/blogs',
- { 'title' => 'foobar' },
- { 'X-Forwarded-For' => '192.168.1.1' }
+ { 'title' => 'foobar' }.to_json,
+ { 'X-Forwarded-For' => '192.168.1.1', 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
)
end
end
- context 'expected status explicitly params' do
+ context 'expected status explicitly set' do
before do
subject.run_test('/blogs', 'post', test) do
expect 400
end
end
- it "asserts response matches params status" do
+ 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