diff --git a/lib/swagger_rails/testing/example_builder.rb b/lib/swagger_rails/testing/example_builder.rb deleted file mode 100644 index 8cfcbae..0000000 --- a/lib/swagger_rails/testing/example_builder.rb +++ /dev/null @@ -1,85 +0,0 @@ -module SwaggerRails - - class ExampleBuilder - attr_reader :expected_status - - def initialize(path_template, http_method, swagger) - @path_template = path_template - @http_method = http_method - @swagger = swagger - @swagger_operation = find_swagger_operation! - @expected_status = find_swagger_success_status! - @param_values = {} - end - - def expect(status) - @expected_status = status - end - - def set(param_values) - @param_values.merge!(param_values.stringify_keys) - end - - def path - @path_template.dup.tap do |template| - template.prepend(@swagger['basePath'].presence || '') - path_params = param_values_for('path') - path_params.each { |name, value| template.sub!("\{#{name}\}", value) } - end - end - - def params - query_params = param_values_for('query') - body_params = param_values_for('body') - query_params.merge(body_params.values.first || {}) - end - - def headers - param_values_for('header') - end - - private - - def find_swagger_operation! - find_swagger_item!('paths', @path_template, @http_method) - end - - def find_swagger_success_status! - path_keys = [ 'paths', @path_template, @http_method, 'responses' ] - responses = find_swagger_item!(*path_keys) - key = responses.keys.find { |k| k.start_with?('2') } - key ? key.to_i : (raise MetadataError.new(path_keys.concat('2xx'))) - end - - def find_swagger_item!(*path_keys) - item = @swagger - path_keys.each do |key| - item = item[key] || (raise MetadataError.new(*path_keys)) - end - item - end - - def param_values_for(location) - params = (@swagger_operation['parameters'] || []).select { |p| p['in'] == location } - Hash[params.map { |param| [ param['name'], value_for(param) ] }] - 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'] - 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 - end - - class MetadataError < StandardError - def initialize(*path_keys) - path = path_keys.map { |key| "['#{key}']" }.join('') - super("Swagger document is missing expected metadata at #{path}") - end - end -end diff --git a/lib/swagger_rails/testing/test_data_builder.rb b/lib/swagger_rails/testing/test_data_builder.rb new file mode 100644 index 0000000..0433475 --- /dev/null +++ b/lib/swagger_rails/testing/test_data_builder.rb @@ -0,0 +1,92 @@ +module SwaggerRails + + class TestDataBuilder + + 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) + @param_values.merge!(param_values.stringify_keys) + end + + def expect(status) + @expected_status = status + end + + def test_data + operation = find_operation! + parameters = operation['parameters'] || [] + responses = operation['responses'] + { + path: build_path(parameters), + params: build_params(parameters), + headers: build_headers(parameters), + expected_response: build_expected_response(responses) + } + end + + private + + def find_operation! + keys = [ 'paths', @path_template, @http_method ] + operation = find_hash_item!(@swagger, keys) + operation || (raise MetadataError.new(keys)) + end + + def find_hash_item!(hash, keys) + item = hash[keys[0]] || (return nil) + keys.length == 1 ? item : find_hash_item!(item, keys.drop(1)) + end + + def build_path(parameters) + param_values = param_values_for(parameters, 'path') + @path_template.dup.tap do |template| + template.prepend(@swagger['basePath'].presence || '') + param_values.each { |name, value| template.sub!("\{#{name}\}", value) } + end + 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 + end + + def build_headers(parameters) + param_values_for(parameters, 'header') + end + + def build_expected_response(responses) + 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) ] }] + 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'] + 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 + end + + class MetadataError < StandardError + def initialize(*path_keys) + path = path_keys.map { |key| "['#{key}']" }.join('') + super("Swagger document is missing expected metadata at #{path}") + end + end +end diff --git a/lib/swagger_rails/testing/test_visitor.rb b/lib/swagger_rails/testing/test_visitor.rb index 9e74ad8..f024b5a 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/example_builder' +require 'swagger_rails/testing/test_data_builder' module SwaggerRails @@ -9,16 +9,17 @@ module SwaggerRails end def run_test(path_template, http_method, test, &block) - example = ExampleBuilder.new(path_template, http_method, @swagger) - example.instance_exec(&block) if block_given? + builder = TestDataBuilder.new(path_template, http_method, @swagger) + builder.instance_exec(&block) if block_given? + test_data = builder.test_data test.send(http_method, - example.path, - example.params, - example.headers + test_data[:path], + test_data[:params], + test_data[:headers] ) - test.assert_response(example.expected_status) + test.assert_response(test_data[:expected_status]) end end end diff --git a/spec/testing/example_builder_spec.rb b/spec/testing/example_builder_spec.rb deleted file mode 100644 index be8b73b..0000000 --- a/spec/testing/example_builder_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'rails_helper' -require 'swagger_rails/testing/example_builder' - -module SwaggerRails - - describe ExampleBuilder 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 '#path' do - context 'operation with path params' do - let(:path) { '/blogs/{id}' } - let(:method) { 'get' } - - context 'by default' do - it "returns path based on 'default' values" do - expect(subject.path).to eq('/blogs/123') - end - end - - context 'values explicitly set' do - before { subject.set id: '456' } - it 'returns path based on set values' do - expect(subject.path).to eq('/blogs/456') - end - end - end - - context 'swagger includes basePath' do - before { swagger['basePath'] = '/foobar' } - let(:path) { '/blogs' } - let(:method) { 'post' } - - it 'returns path prefixed with basePath' do - expect(subject.path).to eq('/foobar/blogs') - end - end - end - - describe '#params' do - context 'operation with body param' do - let(:path) { '/blogs' } - let(:method) { 'post' } - - context 'by default' do - it "returns schema 'example'" do - expect(subject.params).to eq(swagger['definitions']['Blog']['example']) - end - end - - context 'value explicitly set' do - before { subject.set blog: { 'title' => 'foobar' } } - it 'returns params value' do - expect(subject.params).to eq({ 'title' => 'foobar' }) - end - end - end - - context 'operation with query params' do - let(:path) { '/blogs' } - let(:method) { 'get' } - - context 'by default' do - it "returns query params based on 'default' values" do - expect(subject.params).to eq({ 'published' => 'true', 'keywords' => 'Ruby on Rails' }) - end - end - - context 'values explicitly set' do - before { subject.set keywords: 'Java' } - it 'returns query params based on set values' do - expect(subject.params).to eq({ 'published' => 'true', 'keywords' => 'Java' }) - end - end - end - end - - describe '#headers' do - context 'operation with header params' do - let(:path) { '/blogs' } - let(:method) { 'post' } - - context 'by default' do - it "returns headers based on 'default' values" do - expect(subject.headers).to eq({ 'X-Forwarded-For' => 'client1' }) - end - end - - context 'values explicitly params' do - before { subject.set 'X-Forwarded-For' => '192.168.1.1' } - it 'returns headers based on params values' do - expect(subject.headers).to eq({ 'X-Forwarded-For' => '192.168.1.1' }) - end - end - end - end - - describe '#expected_status' do - let(:path) { '/blogs' } - let(:method) { 'post' } - - context 'by default' do - it "returns first 2xx status in 'responses'" do - expect(subject.expected_status).to eq(200) - end - end - - context 'expected status explicitly params' do - before { subject.expect 400 } - it "returns params status" do - expect(subject.expected_status).to eq(400) - end - end - end - end -end diff --git a/spec/testing/test_data_builder_spec.rb b/spec/testing/test_data_builder_spec.rb new file mode 100644 index 0000000..f403107 --- /dev/null +++ b/spec/testing/test_data_builder_spec.rb @@ -0,0 +1,99 @@ +require 'rails_helper' +require 'swagger_rails/testing/test_data_builder' + +module SwaggerRails + + describe TestDataBuilder 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 params' 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' }) + 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' }) + 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' }) + 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' }) + end + end + end + end + end +end