Browse files

Initial Commit

  • Loading branch information...
0 parents commit bf113e044c65119cc49af5ec55eb7cbe87291867 @tomas-stefano committed Jul 16, 2012
Showing with 1,190 additions and 0 deletions.
  1. +17 −0 .gitignore
  2. +1 −0 .rspec
  3. +1 −0 .rvmrc
  4. +4 −0 Gemfile
  5. +5 −0 History.markdown
  6. +22 −0 LICENSE
  7. +129 −0 README.markdown
  8. +2 −0 Rakefile
  9. +21 −0 api_matchers.gemspec
  10. +45 −0 lib/api_matchers.rb
  11. +25 −0 lib/api_matchers/core/find_in_json.rb
  12. +49 −0 lib/api_matchers/core/rspec_matchers.rb
  13. +58 −0 lib/api_matchers/core/setup.rb
  14. +14 −0 lib/api_matchers/headers/base.rb
  15. +17 −0 lib/api_matchers/headers/be_json.rb
  16. +17 −0 lib/api_matchers/headers/be_xml.rb
  17. +32 −0 lib/api_matchers/http_status_code/base.rb
  18. +17 −0 lib/api_matchers/http_status_code/be_bad_request.rb
  19. +17 −0 lib/api_matchers/http_status_code/be_internal_server_error.rb
  20. +17 −0 lib/api_matchers/http_status_code/be_unauthorized.rb
  21. +17 −0 lib/api_matchers/http_status_code/create_resource.rb
  22. +46 −0 lib/api_matchers/response_body/base.rb
  23. +25 −0 lib/api_matchers/response_body/have_json_node.rb
  24. +6 −0 lib/api_matchers/response_body/have_node.rb
  25. +19 −0 lib/api_matchers/response_body/have_xml_node.rb
  26. +3 −0 lib/api_matchers/version.rb
  27. +27 −0 spec/api_matchers/core/find_in_json_spec.rb
  28. +4 −0 spec/api_matchers/core/setup_spec.rb
  29. +12 −0 spec/api_matchers/headers/base_spec.rb
  30. +23 −0 spec/api_matchers/headers/be_json_spec.rb
  31. +27 −0 spec/api_matchers/headers/be_xml_spec.rb
  32. +12 −0 spec/api_matchers/http_status_code/base_spec.rb
  33. +43 −0 spec/api_matchers/http_status_code/be_bad_request_spec.rb
  34. +43 −0 spec/api_matchers/http_status_code/be_internal_server_error_spec.rb
  35. +43 −0 spec/api_matchers/http_status_code/be_unauthorized_spec.rb
  36. +43 −0 spec/api_matchers/http_status_code/create_resource_spec.rb
  37. +47 −0 spec/api_matchers/response_body/base_spec.rb
  38. +101 −0 spec/api_matchers/response_body/have_json_node_spec.rb
  39. +27 −0 spec/api_matchers/response_body/have_node_spec.rb
  40. +102 −0 spec/api_matchers/response_body/have_xml_node_spec.rb
  41. +10 −0 spec/spec_helper.rb
17 .gitignore
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
1 .rspec
@@ -0,0 +1 @@
+--color -f d
1 .rvmrc
@@ -0,0 +1 @@
+rvm 1.9.2@api_matchers --create
4 Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in api_matchers.gemspec
+gemspec
5 History.markdown
@@ -0,0 +1,5 @@
+## development
+
+1) Headers Matchers: be_xml, be_json (**OBS:** Need to think about the setup!)
+2) HTTP Status Matchers: be_a_bad_request, be_internal_server_error, be_unauthorized, create_resource
+3) Response body Matchers: have_node, have_json_node, have_xml_node
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Tomas D'Stefano
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
129 README.markdown
@@ -0,0 +1,129 @@
+# API Matchers
+
+Collection of RSpec matchers for create your API.
+
+## Matchers
+
+* be_in_xml
+* be_a_json
+* create_resource
+* be_a_bad_request
+* be_unauthorized
+* be_internal_server_error
+* have_node
+* have_json_node
+* have_xml_node
+* be_json_eql
+* be_xml_eql
+
+## Install
+
+ gem install api_matchers
+
+## Usage
+
+### Have Node Matcher
+
+ "{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:transaction)
+
+ "{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:id).with(54)
+
+ "{ 'error': '', 'transaction': { 'id': '55', 'status': 'waiting_payment' } }".should have_node(:error).with('not_authorized')
+
+If you want to configure to make all **searches inside a root element**, you can do this:
+
+ APIMatchers.setup do |config|
+ config.root_element = :transaction
+ end
+
+ "{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:id).with(54) # WILL PASS
+
+ "{ 'error': '', 'transaction': { 'id': '55', 'status': 'waiting_payment' } }".should have_node(:error).with('not_authorized') # WILL NOT PASS BECAUSE THE ERROR NODE ISN'T INSIDE THE TRANSACTION NODE
+
+### HAVE NODE Matcher Configuration
+
+You can configure if you want xml or json(**JSON is the default**):
+
+ APIMatchers.setup do |config|
+ config.content_type = :xml
+ end
+
+ '<transaction><id>200</id><status>paid</status></transaction>'.should have_node(:status).with('paid')
+
+**Observation: You can use the *have_xml_node* or *have_json_node* if you don't want to configure everytime.**
+
+You can configure the name of the method for example:
+
+ ## Instead of this
+ response.body.should have_node(:foo)
+
+ ## YOU can do this
+ APIMatchers.setup do |config|
+ config.body_method = :body
+ end
+
+Then you can use without call the **#body** method:
+
+ response.should have_node(:foo).with('bar')
+
+### Create Resource Matcher
+
+This matchers see the HTTP STATUS CODE is equal to 201.
+
+ response.status.should create_resource
+
+### BAD REQUEST Matcher
+
+This BAD REQUEST is a matcher that see if the HTTP STATUS code is equal to 400.
+
+ response.status.should be_a_bad_request
+ response.status.should be_bad_request
+
+### UNAUTHORIZED Matcher
+
+This UNAUTHORIZED is a matcher that see if the HTTP STATUS code is equal to 401.
+
+ response.status.should be_unauthorized
+ response.body.should have_node(:message).with('Invalid Credentials')
+
+### INTERNAL SERVER ERROR Matcher
+
+This INTERNAL SERVER Error is a matcher that see if the HTTP STATUS code is equal to 500.
+
+ response.status.should be_internal_server_error
+ response.body.should have_node(:message).with('An Internal Error Occurs in our precious app. :S')
+
+### HTTP STATUS CODE Configuration
+
+You can configure the name method to call the http status code:
+
+ APIMatchers.setup do |config|
+ config.http_status_method = :status
+ end
+
+Then you can use without call the **#status** method:
+
+ response.should create_resource
+
+This configurations affects this matchers:
+
+* create_resource
+* be_a_bad_request
+* be_internal_server_error
+* be_unauthorized
+
+### Be in XML Matcher
+
+This is a matcher that see if the content type is xml:
+
+ response.content_type.should be_in_xml
+
+### Be in JSON Matcher
+
+This is a matcher that see if the content type is in JSON:
+
+ response.content_type.should be_in_json
+
+### Acknowlegments
+
+* Special thanks to Daniel Konishi to contribute in the product that I extracted the matchers to this gem.
2 Rakefile
@@ -0,0 +1,2 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
21 api_matchers.gemspec
@@ -0,0 +1,21 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/api_matchers/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Tomas D'Stefano"]
+ gem.email = ["tomas_stefano@successoft.com"]
+ gem.description = %q{Collection of RSpec matchers for create your API.}
+ gem.summary = %q{Collection of RSpec matchers for create your API.}
+ gem.homepage = "https://github.com/tomas-stefano/api_matchers"
+
+ gem.files = `git ls-files`.split($\)
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
+ gem.name = "api_matchers"
+ gem.require_paths = ["lib"]
+ gem.version = APIMatchers::VERSION
+
+ gem.add_dependency 'rspec', '>= 2.11.0'
+ gem.add_dependency 'activesupport', '>= 3.2.6'
+ gem.add_dependency 'nokogiri', '>= 1.5.5'
+end
45 lib/api_matchers.rb
@@ -0,0 +1,45 @@
+require "api_matchers/version"
+require "active_support/core_ext/object"
+require "active_support/core_ext/class"
+
+module APIMatchers
+ autoload :RSpecMatchers, 'api_matchers/core/rspec_matchers'
+
+ # HTTP Status Code Matchers
+ #
+ module HTTPStatusCode
+ autoload :Base, 'api_matchers/http_status_code/base'
+ autoload :BeBadRequest, 'api_matchers/http_status_code/be_bad_request'
+ autoload :BeInternalServerError, 'api_matchers/http_status_code/be_internal_server_error'
+ autoload :BeUnauthorized, 'api_matchers/http_status_code/be_unauthorized'
+ autoload :CreateResource, 'api_matchers/http_status_code/create_resource'
+ end
+
+ # Content Type Matchers
+ #
+ module Headers
+ autoload :Base, 'api_matchers/headers/base'
+ autoload :BeXML, 'api_matchers/headers/be_xml'
+ autoload :BeJSON, 'api_matchers/headers/be_json'
+ end
+
+ # Response Body Matchers
+ #
+ module ResponseBody
+ autoload :Base, 'api_matchers/response_body/base'
+ autoload :HaveJsonNode, 'api_matchers/response_body/have_json_node'
+ autoload :HaveXmlNode, 'api_matchers/response_body/have_xml_node'
+ autoload :HaveNode, 'api_matchers/response_body/have_node'
+ end
+
+ # Core
+ #
+ module Core
+ autoload :FindInJSON, 'api_matchers/core/find_in_json'
+ autoload :Setup, 'api_matchers/core/setup'
+ end
+
+ def self.setup
+ yield(::APIMatchers::Core::Setup)
+ end
+end
25 lib/api_matchers/core/find_in_json.rb
@@ -0,0 +1,25 @@
+module APIMatchers
+ module Core
+ class FindInJSON
+ attr_reader :json
+
+ def initialize(json)
+ @json = json
+ end
+
+ def find(options={})
+ expected_key = options.fetch(:node).to_s
+
+ @json.each do |key, value|
+ if key == expected_key
+ return value
+ end
+ if value.is_a? Hash
+ return FindInJSON.new(value).find(node: expected_key)
+ end
+ end
+ nil # Don't find anything!
+ end
+ end
+ end
+end
49 lib/api_matchers/core/rspec_matchers.rb
@@ -0,0 +1,49 @@
+module APIMatchers
+ module RSpecMatchers
+ def be_bad_request
+ ::APIMatchers::HTTPStatusCode::BeBadRequest.new(::APIMatchers::Core::Setup)
+ end
+ alias :be_a_bad_request :be_bad_request
+
+ def be_internal_server_error
+ ::APIMatchers::HTTPStatusCode::BeInternalServerError.new(::APIMatchers::Core::Setup)
+ end
+ alias :be_an_internal_server_error :be_internal_server_error
+
+ def be_unauthorized
+ ::APIMatchers::HTTPStatusCode::BeUnauthorized.new(::APIMatchers::Core::Setup)
+ end
+
+ def create_resource
+ ::APIMatchers::HTTPStatusCode::CreateResource.new(::APIMatchers::Core::Setup)
+ end
+ alias :created_resource :create_resource
+
+ def be_xml
+ ::APIMatchers::Headers::BeXML.new(::APIMatchers::Core::Setup)
+ end
+ alias :be_in_xml :be_xml
+
+ def be_json
+ ::APIMatchers::Headers::BeJSON.new(::APIMatchers::Core::Setup)
+ end
+ alias :be_in_json :be_json
+ alias :be_a_json :be_json
+
+ def have_json_node(expected_node)
+ ::APIMatchers::ResponseBody::HaveJsonNode.new(expected_node: expected_node, setup: ::APIMatchers::Core::Setup)
+ end
+
+ def have_xml_node(expected_node)
+ ::APIMatchers::ResponseBody::HaveXmlNode.new(expected_node: expected_node, setup: ::APIMatchers::Core::Setup)
+ end
+
+ def have_node(expected_node)
+ if ::APIMatchers::Core::Setup.have_node_matcher.equal?(:json)
+ have_json_node(expected_node)
+ else
+ have_xml_node(expected_node)
+ end
+ end
+ end
+end
58 lib/api_matchers/core/setup.rb
@@ -0,0 +1,58 @@
+module APIMatchers
+ module Core
+ class Setup
+ # The http status method that will be called when you call http status matchers
+ #
+ # ==== Examples
+ #
+ # response.status.should create_resource
+ # response.status.should be_bad_request
+ # response.status.should be_unauthorized
+ #
+ # # Instead calling #status everytime, you can configure:
+ #
+ # APIMatchers.setup do |config|
+ # config.http_status_method = :status
+ # end
+ #
+ # Then:
+ #
+ # response.should create_resource
+ # response.should be_bad_request
+ # response.should be_unauthorized
+ #
+ cattr_accessor :http_status_method
+
+ # The response body method that will be called when you call the have_node matchers
+ #
+ # ==== Examples
+ #
+ # response.body.should have_node(:foo)
+ # response.body.should have_node(:bar)
+ # response.body.should have_node(:baz)
+ #
+ # # Instead calling #body everytime, you can configure:
+ #
+ # APIMatchers.setup do |config|
+ # config.http_status_method = :body
+ # end
+ #
+ # Then:
+ #
+ # response.should have_node(:foo)
+ # response.should have_node(:bar)
+ # response.should have_node(:baz)
+ #
+ cattr_accessor :response_body_method
+
+ # The default have node matcher that will be used.
+ # This have_node matcher is useful when you just work with one content type in your API.
+ # Change to :xml if you want that have_node works ONLY with XML.
+ # If you work with xml and json in the same API, I recommend that you check the
+ # have_json_node and have_xml_node matchers.
+ #
+ cattr_accessor :have_node_matcher
+ self.have_node_matcher = :json
+ end
+ end
+end
14 lib/api_matchers/headers/base.rb
@@ -0,0 +1,14 @@
+module APIMatchers
+ module Headers
+ class Base
+ def matches?(actual)
+ @actual = actual
+ actual.eql?(expected_content_type)
+ end
+
+ def expected_content_type
+ raise NotImplementedError, "not implemented on #{self}"
+ end
+ end
+ end
+end
17 lib/api_matchers/headers/be_json.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module Headers
+ class BeJSON < Base
+ def expected_content_type
+ 'application/json; charset=utf-8'
+ end
+
+ def failure_message_for_should
+ %Q{expected a JSON response with '#{expected_content_type}'. Got: '#{@actual}'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected to not be a JSON response. Got: '#{expected_content_type}'.}
+ end
+ end
+ end
+end
17 lib/api_matchers/headers/be_xml.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module Headers
+ class BeXML < Base
+ def expected_content_type
+ 'application/xml; charset=utf-8'
+ end
+
+ def failure_message_for_should
+ %Q{expected a XML response with '#{expected_content_type}'. Got: '#{@actual}'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected to not be a XML response. Got: '#{expected_content_type}'.}
+ end
+ end
+ end
+end
32 lib/api_matchers/http_status_code/base.rb
@@ -0,0 +1,32 @@
+module APIMatchers
+ module HTTPStatusCode
+ class Base
+ attr_reader :setup
+
+ def initialize(setup)
+ @setup = setup
+ end
+
+ # Matches the actual with the expected http status code
+ #
+ def matches?(actual)
+ @http_status_code = find_http_status_code(actual)
+ @http_status_code.equal?(expected_status_code)
+ end
+
+ def expected_status_code
+ raise NotImplementedError, "not implemented on #{self}"
+ end
+
+ # If have some configuration about the method to call on actual that method will be called.
+ #
+ def find_http_status_code(actual)
+ if @setup.http_status_method.present?
+ actual.send(@setup.http_status_method)
+ else
+ actual
+ end
+ end
+ end
+ end
+end
17 lib/api_matchers/http_status_code/be_bad_request.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module HTTPStatusCode
+ class BeBadRequest < Base
+ def expected_status_code
+ 400
+ end
+
+ def failure_message_for_should
+ %Q{expected that '#{@http_status_code}' to be a Bad Request with the status '400'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected that '#{@http_status_code}' to NOT be a Bad Request with the status '400'.}
+ end
+ end
+ end
+end
17 lib/api_matchers/http_status_code/be_internal_server_error.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module HTTPStatusCode
+ class BeInternalServerError < Base
+ def expected_status_code
+ 500
+ end
+
+ def failure_message_for_should
+ %Q{expected that '#{@http_status_code}' to be Internal Server Error with the status '500'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected that '#{@http_status_code}' to NOT be Internal Server Error.}
+ end
+ end
+ end
+end
17 lib/api_matchers/http_status_code/be_unauthorized.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module HTTPStatusCode
+ class BeUnauthorized < Base
+ def expected_status_code
+ 401
+ end
+
+ def failure_message_for_should
+ %Q{expected that '#{@http_status_code}' to be Unauthorized with the status '401'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected that '#{@http_status_code}' to NOT be Unauthorized.}
+ end
+ end
+ end
+end
17 lib/api_matchers/http_status_code/create_resource.rb
@@ -0,0 +1,17 @@
+module APIMatchers
+ module HTTPStatusCode
+ class CreateResource < Base
+ def expected_status_code
+ 201
+ end
+
+ def failure_message_for_should
+ %Q{expected that '#{@http_status_code}' to be Created Resource with the status '201'.}
+ end
+
+ def failure_message_for_should_not
+ %Q{expected that '#{@http_status_code}' to NOT be Created Resource.}
+ end
+ end
+ end
+end
46 lib/api_matchers/response_body/base.rb
@@ -0,0 +1,46 @@
+module APIMatchers
+ module ResponseBody
+ class Base
+ attr_reader :setup, :expected_node, :actual
+ attr_writer :actual
+
+ def initialize(options={})
+ @expected_node = options.fetch(:expected_node)
+ @setup = options.fetch(:setup)
+ end
+
+ def matches?(actual)
+ raise NotImplementedError, "not implemented on #{self}"
+ end
+
+ def with(expected_value)
+ @with_value = expected_value
+ self
+ end
+
+ def response_body
+ if @setup.response_body_method.present?
+ @actual.send(@setup.response_body_method)
+ else
+ @actual
+ end
+ end
+
+ def failure_message_for_should
+ "expected to have node called: '#{@expected_node}'" << added_message << ". Got: '#{response_body}'"
+ end
+
+ def failure_message_for_should_not
+ "expected to NOT have node called: '#{@expected_node}'" << added_message << ". Got: '#{response_body}'"
+ end
+
+ def added_message
+ if @with_value
+ " with value: '#{@with_value}'"
+ else
+ ""
+ end
+ end
+ end
+ end
+end
25 lib/api_matchers/response_body/have_json_node.rb
@@ -0,0 +1,25 @@
+require 'json'
+require 'active_support/core_ext/hash'
+
+module APIMatchers
+ module ResponseBody
+ class HaveJsonNode < Base
+ def matches?(actual)
+ @actual = actual
+ json = begin
+ JSON.parse(response_body)
+ rescue
+ {}
+ end
+
+ node = Core::FindInJSON.new(json).find(node: @expected_node.to_s)
+
+ if @with_value
+ node.to_s == @with_value.to_s
+ else
+ node.present?
+ end
+ end
+ end
+ end
+end
6 lib/api_matchers/response_body/have_node.rb
@@ -0,0 +1,6 @@
+module APIMatchers
+ module ResponseBody
+ class HaveNode
+ end
+ end
+end
19 lib/api_matchers/response_body/have_xml_node.rb
@@ -0,0 +1,19 @@
+require 'nokogiri'
+
+module APIMatchers
+ module ResponseBody
+ class HaveXmlNode < Base
+ def matches?(actual)
+ @actual = actual
+ xml = Nokogiri::XML(response_body)
+ node = xml.xpath("//#{@expected_node}").text
+
+ if @with_value
+ node == @with_value.to_s
+ else
+ node.present?
+ end
+ end
+ end
+ end
+end
3 lib/api_matchers/version.rb
@@ -0,0 +1,3 @@
+module APIMatchers
+ VERSION = "0.0.1"
+end
27 spec/api_matchers/core/find_in_json_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+module APIMatchers::Core
+ describe FindInJSON do
+ describe "#find" do
+ context 'when node exists' do
+ it "should return the value of the expected key" do
+ FindInJSON.new('product' => 'gateway').find(node: 'product').should eql 'gateway'
+ end
+
+ it "should return the value of the deep expected key in the json" do
+ FindInJSON.new('transaction' => { 'error' => { 'code' => '999' } }).find(node: 'code').should eql '999'
+ end
+ end
+
+ context 'when node do not exists' do
+ it "should return nil if don't find the expected node" do
+ FindInJSON.new('product' => 'pabx').find(node: 'developers').should be nil
+ end
+
+ it "should return nil if don't find the expected node in the deep JSON" do
+ FindInJSON.new('transaction' => { 'id' => 150, 'error' => {} }).find(node: 'code').should be nil
+ end
+ end
+ end
+ end
+end
4 spec/api_matchers/core/setup_spec.rb
@@ -0,0 +1,4 @@
+require 'spec_helper'
+
+describe APIMatchers::Core::Setup do
+end
12 spec/api_matchers/headers/base_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe APIMatchers::Headers::Base do
+ let(:setup) { OpenStruct.new }
+ subject { APIMatchers::Headers::Base.new(setup) }
+
+ describe "#matches?" do
+ it "should raise Not Implement Exception" do
+ expect { subject.matches?('application/xml') }.to raise_error(NotImplementedError, "not implemented on #{subject}")
+ end
+ end
+end
23 spec/api_matchers/headers/be_json_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe APIMatchers::Headers::BeJSON do
+ describe "actual.should be_json" do
+ it "should pass when the actual is json response" do
+ "application/json; charset=utf-8".should be_json
+ end
+
+ it "should not pass when the actual is not a json response" do
+ expect { "application/xml; charset=utf-8".should be_json }.to fail_with(%Q{expected a JSON response with 'application/json; charset=utf-8'. Got: 'application/xml; charset=utf-8'.})
+ end
+ end
+
+ describe "actual.should_not be_json" do
+ it "should pass when the actual is not a json response" do
+ "application/xml; charset=utf-8".should_not be_json
+ end
+
+ it "should not pass when the actual is a json response" do
+ expect { "application/json; charset=utf-8".should_not be_json }.to fail_with(%Q{expected to not be a JSON response. Got: 'application/json; charset=utf-8'.})
+ end
+ end
+end
27 spec/api_matchers/headers/be_xml_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe APIMatchers::Headers::BeXML do
+ describe "actual.should be_xml" do
+ it "should pass when the actual is json response" do
+ "application/xml; charset=utf-8".should be_xml
+ end
+
+ it "should not pass when the actual is not a json response" do
+ expect {
+ "application/json; charset=utf-8".should be_xml
+ }.to fail_with(%Q{expected a XML response with 'application/xml; charset=utf-8'. Got: 'application/json; charset=utf-8'.})
+ end
+ end
+
+ describe "actual.should_not be_xml" do
+ it "should pass when the actual is not a json response" do
+ "application/json; charset=utf-8".should_not be_xml
+ end
+
+ it "should not pass when the actual is a json response" do
+ expect {
+ "application/xml; charset=utf-8".should_not be_xml
+ }.to fail_with(%Q{expected to not be a XML response. Got: 'application/xml; charset=utf-8'.})
+ end
+ end
+end
12 spec/api_matchers/http_status_code/base_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe APIMatchers::HTTPStatusCode::Base do
+ let(:setup) { OpenStruct.new }
+ subject { APIMatchers::HTTPStatusCode::Base.new(setup) }
+
+ describe "#matches?" do
+ it "should raise Not Implement Exception" do
+ expect { subject.matches?(302) }.to raise_error(NotImplementedError, "not implemented on #{subject}")
+ end
+ end
+end
43 spec/api_matchers/http_status_code/be_bad_request_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe APIMatchers::HTTPStatusCode::BeBadRequest do
+ describe "should be_bad_request" do
+ it "should passes if the actual is equal to 400" do
+ 400.should be_bad_request
+ end
+
+ it "should fails if the actual is not equal to 400" do
+ expect { 401.should be_bad_request }.to fail_with(%Q{expected that '401' to be a Bad Request with the status '400'.})
+ end
+ end
+
+ describe "should_not be_bad_request" do
+ it "should passes if the actual is not equal to 400" do
+ 401.should_not be_bad_request
+ end
+
+ it "should fail if the actual is equal to 400" do
+ expect { 400.should_not be_bad_request }.to fail_with(%Q{expected that '400' to NOT be a Bad Request with the status '400'.})
+ end
+ end
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.http_status_method = :http_status }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.http_status_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 400" do
+ response = OpenStruct.new(:http_status => 400)
+ response.should be_bad_request
+ end
+
+ it "should fail if the actual.http_status is not equal to 400" do
+ response = OpenStruct.new(:http_status => 500)
+ expect { response.should be_bad_request }.to fail_with(%Q{expected that '500' to be a Bad Request with the status '400'.})
+ end
+ end
+end
43 spec/api_matchers/http_status_code/be_internal_server_error_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe APIMatchers::HTTPStatusCode::BeInternalServerError do
+ describe "should be_internal_server_error" do
+ it "should passes if the actual is equal to 500" do
+ 500.should be_internal_server_error
+ end
+
+ it "should fails if the actual is not equal to 500" do
+ expect { 401.should be_internal_server_error }.to fail_with(%Q{expected that '401' to be Internal Server Error with the status '500'.})
+ end
+ end
+
+ describe "should_not be_internal_server_error" do
+ it "should passes if the actual is not equal to 500" do
+ 400.should_not be_internal_server_error
+ end
+
+ it "should fail if the actual is equal to 500" do
+ expect { 500.should_not be_internal_server_error }.to fail_with(%Q{expected that '500' to NOT be Internal Server Error.})
+ end
+ end
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.http_status_method = :http_status }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.http_status_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 500" do
+ response = OpenStruct.new(:http_status => 500)
+ response.should be_internal_server_error
+ end
+
+ it "should fail if the actual.http_status is not equal to 500" do
+ response = OpenStruct.new(:http_status => 402)
+ expect { response.should be_internal_server_error }.to fail_with(%Q{expected that '402' to be Internal Server Error with the status '500'.})
+ end
+ end
+end
43 spec/api_matchers/http_status_code/be_unauthorized_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe APIMatchers::HTTPStatusCode::BeUnauthorized do
+ describe "should be_unauthorized" do
+ it "should passes if the actual is equal to 401" do
+ 401.should be_unauthorized
+ end
+
+ it "should fails if the actual is not equal to 401" do
+ expect { 400.should be_unauthorized }.to fail_with(%Q{expected that '400' to be Unauthorized with the status '401'.})
+ end
+ end
+
+ describe "should_not be_unauthorized" do
+ it "should passes if the actual is not equal to 401" do
+ 201.should_not be_unauthorized
+ end
+
+ it "should fail if the actual is equal equal to 401" do
+ expect { 401.should_not be_unauthorized }.to fail_with(%Q{expected that '401' to NOT be Unauthorized.})
+ end
+ end
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.http_status_method = :http_status }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.http_status_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 401" do
+ response = OpenStruct.new(:http_status => 401)
+ response.should be_unauthorized
+ end
+
+ it "should fail if the actual.http_status is not equal to 401" do
+ response = OpenStruct.new(:http_status => 402)
+ expect { response.should be_unauthorized }.to fail_with(%Q{expected that '402' to be Unauthorized with the status '401'.})
+ end
+ end
+end
43 spec/api_matchers/http_status_code/create_resource_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe APIMatchers::HTTPStatusCode::CreateResource do
+ describe "should create_resource" do
+ it "should passes if the actual is equal to 201" do
+ 201.should create_resource
+ end
+
+ it "should fails if the actual is not equal to 201" do
+ expect { 200.should create_resource }.to fail_with(%Q{expected that '200' to be Created Resource with the status '201'.})
+ end
+ end
+
+ describe "should_not create_resource" do
+ it "should passes if the actual is not equal to 201" do
+ 401.should_not create_resource
+ end
+
+ it "should fail if the actual is equal equal to 201" do
+ expect { 201.should_not create_resource }.to fail_with(%Q{expected that '201' to NOT be Created Resource.})
+ end
+ end
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.http_status_method = :http_status }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.http_status_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 201" do
+ response = OpenStruct.new(:http_status => 201)
+ response.should create_resource
+ end
+
+ it "should fail if the actual.http_status is not equal to 201" do
+ response = OpenStruct.new(:http_status => 402)
+ expect { response.should create_resource }.to fail_with(%Q{expected that '402' to be Created Resource with the status '201'.})
+ end
+ end
+end
47 spec/api_matchers/response_body/base_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe APIMatchers::ResponseBody::Base do
+ let(:setup) { OpenStruct.new(:response_body_method => :body) }
+ subject { APIMatchers::ResponseBody::Base.new(setup: setup, expected_node: :status) }
+
+ describe "#matches?" do
+ it "should raise Not Implemented Error" do
+ expect {
+ subject.matches?("foo")
+ }.to raise_error(NotImplementedError, "not implemented on #{subject}")
+ end
+ end
+
+ describe "#setup" do
+ it "should read from the initialize" do
+ subject.setup.should equal setup
+ end
+ end
+
+ describe "#expected_node" do
+ it "should read from the initialize" do
+ subject.expected_node.should equal :status
+ end
+ end
+
+ describe "#response_body" do
+ let(:body) { { :foo => :bar}.to_json }
+
+ context 'when have configuration' do
+ it "should call the method when is config" do
+ subject.actual = OpenStruct.new(:body => body)
+ subject.response_body.should eql body
+ end
+ end
+
+ context 'when dont have configuration' do
+ let(:setup) { OpenStruct.new(:response_body_method => nil) }
+ subject { APIMatchers::ResponseBody::Base.new(setup: setup, expected_node: :status) }
+
+ it "should return the actual when do not have config" do
+ subject.actual = body
+ subject.response_body.should eql body
+ end
+ end
+ end
+end
101 spec/api_matchers/response_body/have_json_node_spec.rb
@@ -0,0 +1,101 @@
+require 'spec_helper'
+
+describe APIMatchers::ResponseBody::HaveJsonNode do
+ describe "actual.should have_json_node" do
+ context 'expected key and value in top level' do
+ it "should pass when the expected key exist" do
+ { :product => 'gateway' }.to_json.should have_json_node(:product)
+ end
+
+ it "should fail when the expected key does not exist" do
+ expect {
+ { :product => 'pabx' }.to_json.should have_json_node(:developers)
+ }.to fail_with(%Q{expected to have node called: 'developers'. Got: '{"product":"pabx"}'})
+ end
+
+ it "should pass when the expected key exist with the expected value" do
+ { :product => 'payment-gateway' }.to_json.should have_json_node(:product).with('payment-gateway')
+ end
+
+ it "should fail when the expected key exist but the expected value don't exist" do
+ expect {
+ { :product => 'payment-gateway' }.to_json.should have_json_node(:product).with('email-marketing')
+ }.to fail_with(%Q{expected to have node called: 'product' with value: 'email-marketing'. Got: '{"product":"payment-gateway"}'})
+ end
+
+ it "should not parse the matcher for json when you pass a xml" do
+ expect {
+ "<product><name>webdesk</name></product>".should have_json_node(:name).with('webdesk')
+ }.to fail_with(%Q{expected to have node called: 'name' with value: 'webdesk'. Got: '<product><name>webdesk</name></product>'})
+ end
+ end
+
+ context 'expected key and value in more deep in the JSON' do
+ it "should pass when the expected key exist" do
+ { :transaction => { :id => 150 } }.to_json.should have_json_node(:id)
+ end
+
+ it "should pass when the expected key and expected value exist" do
+ { :transaction => { :error => { :code => '999' } } }.to_json.should have_json_node(:code).with('999')
+ end
+
+ it "should fail when the expected key does not exist" do
+ expect {
+ { :transaction => { :id => 150, :error => {} } }.to_json.should have_json_node(:code)
+ }.to fail_with(%Q{expected to have node called: 'code'. Got: '{"transaction":{"id":150,"error":{}}}'})
+ end
+
+ it "should fail when the expected key exist but don't exist the expected value" do
+ expect {
+ { :transaction => { :id => 150, :error => { :code => '999' } } }.to_json.should have_json_node(:code).with('001')
+ }.to fail_with(%Q{expected to have node called: 'code' with value: '001'. Got: '{"transaction":{"id":150,"error":{"code":"999"}}}'})
+ end
+ end
+ end
+
+ describe "actual.should_not have_json_node" do
+ it "should pass when don't have the expected node in root level" do
+ { :product => 'gateway' }.to_json.should_not have_json_node(:status)
+ end
+
+ it "should pass when don't have the expected node in any level" do
+ { :transaction => { :id => 12, :status => 'paid' } }.to_json.should_not have_json_node(:error)
+ end
+
+ it "should fail when the expected key exist" do
+ expect {
+ { :status => 'paid' }.to_json.should_not have_json_node(:status)
+ }.to fail_with(%Q{expected to NOT have node called: 'status'. Got: '{"status":"paid"}'})
+ end
+
+ it "should pass when have the expected key but have a different value" do
+ { :status => 'paid' }.to_json.should_not have_json_node(:status).with('not_authorized')
+ end
+
+ it "should fail when have the expected key and have the expected value" do
+ expect {
+ { :status => 'paid' }.to_json.should_not have_json_node(:status).with('paid')
+ }.to fail_with(%Q{expected to NOT have node called: 'status' with value: 'paid'. Got: '{"status":"paid"}'})
+ end
+ end
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.response_body_method = :response_body }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.response_body_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 400" do
+ response = OpenStruct.new(:response_body => { :foo => :bar }.to_json)
+ response.should have_json_node(:foo).with('bar')
+ end
+
+ it "should fail if the actual.http_status is not equal to 400" do
+ response = OpenStruct.new(:response_body => { :baz => :foo}.to_json)
+ expect { response.should have_json_node(:bar) }.to fail_with(%Q{expected to have node called: 'bar'. Got: '{"baz":"foo"}'})
+ end
+ end
+end
27 spec/api_matchers/response_body/have_node_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe APIMatchers::ResponseBody::HaveNode do
+ describe "in json" do
+ before do
+ APIMatchers.setup { |config| config.have_node_matcher = :json }
+ end
+
+ it "should parse the matcher for json" do
+ { :product => 'chat' }.to_json.should have_node(:product).with('chat')
+ end
+ end
+
+ describe "in xml" do
+ before do
+ APIMatchers.setup { |config| config.have_node_matcher = :xml }
+ end
+
+ it "should parse the matcher for xml" do
+ "<product>chat</product>".should have_node(:product).with('chat')
+ end
+
+ after do
+ APIMatchers.setup { |config| config.have_node_matcher = :json }
+ end
+ end
+end
102 spec/api_matchers/response_body/have_xml_node_spec.rb
@@ -0,0 +1,102 @@
+require 'spec_helper'
+
+describe APIMatchers::ResponseBody::HaveXmlNode do
+ describe "actual.should have_xml_node" do
+ context 'expected key and value in top level' do
+ it "should pass when the expected key exist" do
+ "<product>gateway</product>".should have_xml_node(:product)
+ end
+
+ it "should fail when the expected key does not exist" do
+ expect {
+ "<product>pabx</product>".should have_xml_node(:developers)
+ }.to fail_with(%Q{expected to have node called: 'developers'. Got: '<product>pabx</product>'})
+ end
+
+ it "should pass when the expected key exist with the expected value" do
+ "<product>payment-gateway</product>".should have_xml_node(:product).with('payment-gateway')
+ end
+
+ it "should fail when the expected key exist but the expected value don't exist" do
+ expect {
+ "<product>payment-gateway</product>".should have_xml_node(:product).with('email-marketing')
+ }.to fail_with(%Q{expected to have node called: 'product' with value: 'email-marketing'. Got: '<product>payment-gateway</product>'})
+ end
+
+ it "should not parse the matcher for xml when you pass a json" do
+ expect {
+ { :name => 'webdesk'}.to_json.should have_xml_node(:name).with('webdesk')
+ }.to fail_with(%Q{expected to have node called: 'name' with value: 'webdesk'. Got: '{"name":"webdesk"}'})
+ end
+ end
+
+ context 'expected key and value in more deep in the JSON' do
+ it "should pass when the expected key exist" do
+ "<transaction><id>150</id></transaction>".should have_xml_node(:id)
+ end
+
+ it "should pass when the expected key and expected value exist" do
+ "<transaction><error><code>999</code></error></transaction>".should have_xml_node(:code).with('999')
+ end
+
+ it "should fail when the expected key does not exist" do
+ expect {
+ "<transaction><error></error></transaction>".should have_xml_node(:code)
+ }.to fail_with(%Q{expected to have node called: 'code'. Got: '<transaction><error></error></transaction>'})
+ end
+
+ it "should fail when the expected key exist but don't exist the expected value" do
+ expect {
+ "<transaction><error><code>999</code></error></transaction>".should have_xml_node(:code).with('001')
+ }.to fail_with(%Q{expected to have node called: 'code' with value: '001'. Got: '<transaction><error><code>999</code></error></transaction>'})
+ end
+ end
+ end
+
+ describe "actual.should_not have_xml_node" do
+ it "should pass when don't have the expected node in root level" do
+ "<product>gateway</product>".should_not have_xml_node(:status)
+ end
+
+ it "should pass when don't have the expected node in any level" do
+ "<transaction><id>12</id><status>paid</status></transaction>".should_not have_xml_node(:error)
+ end
+
+ it "should fail when the expected key exist" do
+ expect {
+ "<transaction><status>paid</status></transaction>".should_not have_xml_node(:status)
+ }.to fail_with(%Q{expected to NOT have node called: 'status'. Got: '<transaction><status>paid</status></transaction>'})
+ end
+
+ it "should pass when have the expected key but have a different value" do
+ "<status>paid</status>".should_not have_xml_node(:status).with('not_authorized')
+ end
+
+ it "should fail when have the expected key and have the expected value" do
+ expect {
+ "<transaction><status>paid</status></transaction>".should_not have_xml_node(:status).with('paid')
+ }.to fail_with(%Q{expected to NOT have node called: 'status' with value: 'paid'. Got: '<transaction><status>paid</status></transaction>'})
+ end
+ end
+
+
+ describe "with change configuration" do
+ before do
+ APIMatchers.setup { |config| config.response_body_method = :response_body }
+ end
+
+ after do
+ APIMatchers.setup { |config| config.response_body_method = nil }
+ end
+
+ it "should pass if the actual.http_status is equal to 400" do
+ response = OpenStruct.new(:response_body => "<foo>bar</foo>")
+ response.should have_xml_node(:foo).with('bar')
+ end
+
+ it "should fail if the actual.http_status is not equal to 400" do
+ response = OpenStruct.new(:response_body => "<baz>bar</baz>")
+ expect { response.should have_xml_node(:bar) }.to fail_with(%Q{expected to have node called: 'bar'. Got: '<baz>bar</baz>'})
+ end
+ end
+end
10 spec/spec_helper.rb
@@ -0,0 +1,10 @@
+require 'api_matchers'
+require 'ostruct'
+
+RSpec.configure do |config|
+ config.include APIMatchers::RSpecMatchers
+
+ def fail_with(message)
+ raise_error(RSpec::Expectations::ExpectationNotMetError, message)
+ end
+end

0 comments on commit bf113e0

Please sign in to comment.