irumugam 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +67 -0
- data/Rakefile +5 -0
- data/irumugam.gemspec +30 -0
- data/lib/irumugam.rb +20 -0
- data/lib/irumugam/contract.rb +74 -0
- data/lib/irumugam/server.rb +11 -0
- data/lib/irumugam/service.rb +42 -0
- data/lib/irumugam/service_registry.rb +38 -0
- data/lib/irumugam/spec_writer.rb +52 -0
- data/lib/irumugam/version.rb +3 -0
- data/spec/contract_spec.rb +38 -0
- data/spec/helper/spec_helper.rb +49 -0
- data/spec/server_spec.rb +48 -0
- data/spec/service_registry_spec.rb +27 -0
- data/spec/service_spec.rb +56 -0
- data/spec/spec_writer_spec.rb +89 -0
- metadata +225 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@irumugam --create
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Irumugam
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/vagmi/irumugam.png?branch=master)](http://travis-ci.org/vagmi/irumugam)
|
4
|
+
|
5
|
+
Irumugam - The two faced framework helps you setup a DSL for describing services. The one face helps you test the services using RSpec while the other runs it as a mock for consuming them.
|
6
|
+
|
7
|
+
To include irumugam in your project, add it to your `Gemfile`
|
8
|
+
|
9
|
+
gem 'irumugam'
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
To you can describe a service this way using irumugam
|
14
|
+
|
15
|
+
# service.rb
|
16
|
+
include Irumugam
|
17
|
+
|
18
|
+
describe_service "My Glorious Service" do
|
19
|
+
host "http://testserver"
|
20
|
+
end_point "/cart-service"
|
21
|
+
|
22
|
+
before(:each) do
|
23
|
+
#do setup stuff
|
24
|
+
@test_order = FactoryGirl.create(:order)
|
25
|
+
end
|
26
|
+
|
27
|
+
after(:each) do
|
28
|
+
DatabaseCleaner.clean
|
29
|
+
end
|
30
|
+
|
31
|
+
get "/orders" do
|
32
|
+
status 200
|
33
|
+
body [FactoryGirl.attributes_for(:order)].to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
get "/orders/1" do
|
37
|
+
status 200
|
38
|
+
json {id:42, name: "Some Name", quantity: 500}, :ignore=>[:id]
|
39
|
+
end
|
40
|
+
|
41
|
+
put "/orders/:id", :body=>{:description=>"asdf"}, :type=>:json do
|
42
|
+
status 200
|
43
|
+
path_object { @test_order.attributes }
|
44
|
+
json {id: 42, description: "asdf", count: 50}, :ignore=>[:id]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
register_with_rspec!
|
49
|
+
|
50
|
+
You can now run this as a spec as.
|
51
|
+
|
52
|
+
$ rspec service.rb
|
53
|
+
|
54
|
+
You can also run this as a mock server by wiring it with config.ru
|
55
|
+
|
56
|
+
# config.ru
|
57
|
+
require './service.rb'
|
58
|
+
|
59
|
+
run Irumugam::Server.new
|
60
|
+
|
61
|
+
You can run the server with a rack compatible server like `thin`.
|
62
|
+
|
63
|
+
$ thin -R config.ru start
|
64
|
+
|
65
|
+
You can then access the mock service with the following.
|
66
|
+
|
67
|
+
$ curl http://localhost:3000/cart-service/orders/1
|
data/Rakefile
ADDED
data/irumugam.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "irumugam/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "irumugam"
|
7
|
+
s.version = Irumugam::VERSION
|
8
|
+
s.authors = ["Shakir Shakiel", "Vagmi Mudumbai", "Lokesh D"]
|
9
|
+
s.email = ["shakiras@thoughtworks.com", "vagmimud@thoughtworks.com", "lokeshd@thoughtworks.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Describe service contracts that act both as a spec and a mock}
|
12
|
+
s.description = %q{Describe service contracts that act both as a spec and a mock}
|
13
|
+
|
14
|
+
s.rubyforge_project = "irumugam"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "webmock"
|
22
|
+
s.add_development_dependency "factory_girl"
|
23
|
+
s.add_development_dependency "rack-test"
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
s.add_runtime_dependency "rspec"
|
26
|
+
s.add_runtime_dependency "rack"
|
27
|
+
s.add_runtime_dependency "httparty"
|
28
|
+
s.add_runtime_dependency "json"
|
29
|
+
s.add_runtime_dependency "activesupport"
|
30
|
+
end
|
data/lib/irumugam.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'httparty'
|
3
|
+
require 'rack'
|
4
|
+
require 'active_support/core_ext/hash/keys'
|
5
|
+
|
6
|
+
require "irumugam/version"
|
7
|
+
require "irumugam/contract"
|
8
|
+
require "irumugam/service"
|
9
|
+
require "irumugam/service_registry"
|
10
|
+
require "irumugam/spec_writer"
|
11
|
+
require "irumugam/server"
|
12
|
+
|
13
|
+
module Irumugam
|
14
|
+
def describe_service(*args, &block)
|
15
|
+
ServiceRegistry.desc(*args,&block)
|
16
|
+
end
|
17
|
+
def register_with_rspec!
|
18
|
+
SpecWriter.create_example_groups
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Irumugam
|
2
|
+
class Contract
|
3
|
+
attr_accessor :method, :path, :service, :accept,:block, :contract_status, :contract_body, :test_host, :params, :contract_json, :contract_json_ignore, :request_body, :request_type, :path_object_block
|
4
|
+
def initialize(options={})
|
5
|
+
@method = options[:method]
|
6
|
+
@path = options[:path]
|
7
|
+
@test_host = options[:test_host]
|
8
|
+
@params = options[:params]
|
9
|
+
@request_body = options[:body]
|
10
|
+
@request_type = options[:type]
|
11
|
+
@accept = options[:accept]
|
12
|
+
@service = options[:service]
|
13
|
+
instance_eval &(options[:block])
|
14
|
+
end
|
15
|
+
|
16
|
+
def status(val)
|
17
|
+
@contract_status = val
|
18
|
+
end
|
19
|
+
|
20
|
+
def body(val)
|
21
|
+
@contract_body = val
|
22
|
+
end
|
23
|
+
|
24
|
+
def json(val,options={})
|
25
|
+
@contract_json = val
|
26
|
+
@contract_json_ignore = options.delete(:ignore) || []
|
27
|
+
end
|
28
|
+
|
29
|
+
def path_object(&po_block)
|
30
|
+
@path_object_block = po_block
|
31
|
+
end
|
32
|
+
|
33
|
+
def json_for_spec
|
34
|
+
prepare_response(contract_json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepare_response(response)
|
38
|
+
response = JSON.parse(response) if response.is_a?(String)
|
39
|
+
spec_json = response.clone
|
40
|
+
spec_json.map{ |hsh| process_hash(hsh) } if spec_json.is_a?(Array)
|
41
|
+
process_hash(spec_json) if spec_json.is_a?(Hash)
|
42
|
+
spec_json
|
43
|
+
end
|
44
|
+
def path_match?(request)
|
45
|
+
match_pattern = /(\:\w+)/
|
46
|
+
replace_pattern = /(\w+)/
|
47
|
+
path_patterns = self.path.split("/").map { |part|
|
48
|
+
part.index(":") ? Regexp.new(part.gsub(match_pattern, replace_pattern.source)) : /#{part}/
|
49
|
+
}
|
50
|
+
return false unless request.path.split("/").count==path_patterns.count
|
51
|
+
path_patterns.each_index do |idx|
|
52
|
+
return false unless path_patterns[idx]=~request.path.split("/")[idx]
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
def matches?(request, req_body)
|
57
|
+
return false if !req_body.empty? ^ !self.request_body.nil?
|
58
|
+
request_json = JSON.parse(req_body) if (!req_body.empty? && request.content_type=="application/json")
|
59
|
+
result = path_match?(request) &&
|
60
|
+
request.request_method==self.method &&
|
61
|
+
request.params==self.params &&
|
62
|
+
(request_json.nil? ? true : (request_json == self.request_body.stringify_keys))
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
private
|
68
|
+
def process_hash(hsh)
|
69
|
+
hsh.stringify_keys!
|
70
|
+
self.contract_json_ignore.map(&:to_s).each { |key| hsh.delete(key) }
|
71
|
+
hsh
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Irumugam
|
2
|
+
class Server
|
3
|
+
def call(env)
|
4
|
+
request = Rack::Request.new(env)
|
5
|
+
contract = ServiceRegistry.find(request)
|
6
|
+
body = contract.contract_body if contract.contract_body
|
7
|
+
body ||= contract.contract_json.to_json
|
8
|
+
[contract.contract_status, {}, body]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Irumugam
|
2
|
+
class Service
|
3
|
+
attr_accessor :name, :block, :rest_end_point, :paths, :test_host, :before_blocks, :after_blocks
|
4
|
+
def initialize(name, service_block)
|
5
|
+
@name, @block = name, service_block
|
6
|
+
@before_blocks = {}
|
7
|
+
@after_blocks = {}
|
8
|
+
@paths = ServiceRegistry.instance.paths
|
9
|
+
instance_eval &service_block
|
10
|
+
end
|
11
|
+
def before(type=:each, &before_block)
|
12
|
+
before_blocks[type] = before_block
|
13
|
+
end
|
14
|
+
def after(type=:each, &after_block)
|
15
|
+
after_blocks[type] = after_block
|
16
|
+
end
|
17
|
+
def end_point(name)
|
18
|
+
@rest_end_point = name
|
19
|
+
end
|
20
|
+
def host(val)
|
21
|
+
@test_host=val
|
22
|
+
end
|
23
|
+
["GET", "POST", "PUT", "DELETE"].each do |verb|
|
24
|
+
define_method(verb.downcase) do |path, options={}, &block|
|
25
|
+
http_method(verb, @rest_end_point+path, options, block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def http_method(method,path,options,contract_block)
|
30
|
+
defaults = {:accept=>"application/json",
|
31
|
+
:method=>method,
|
32
|
+
:path=>path,
|
33
|
+
:test_host => @test_host,
|
34
|
+
:params=>{},
|
35
|
+
:block=>contract_block,
|
36
|
+
:service=>self}
|
37
|
+
defaults.merge!(options)
|
38
|
+
paths[path] ||= []
|
39
|
+
paths[path] << Contract.new(defaults)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module Irumugam
|
3
|
+
class ServiceRegistry
|
4
|
+
include Singleton
|
5
|
+
attr_accessor :services, :paths
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@paths ||= {}
|
9
|
+
@services ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def cleanup!
|
13
|
+
@paths = {}
|
14
|
+
@services = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def find(request)
|
18
|
+
req_body = request.body.read
|
19
|
+
paths.values.flatten.select { |c| c.matches?(request, req_body) }.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def paths_for(service_name)
|
23
|
+
paths.values.flatten.select { |c| c.service.name==service_name }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.cleanup!
|
27
|
+
instance.cleanup!
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.desc(name,&block)
|
31
|
+
instance.services[name] = Service.new(name, block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.find(request)
|
35
|
+
instance.find(request)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
module Irumugam
|
3
|
+
|
4
|
+
module RequestMethods
|
5
|
+
def perform_request(options)
|
6
|
+
path_object = options.delete(:path_object)
|
7
|
+
if(path_object)
|
8
|
+
path_object.each do |k,v|
|
9
|
+
self.path.gsub!(":#{k}",v.to_s)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
options[:query]=self.params if self.params
|
13
|
+
if self.request_body
|
14
|
+
if self.request_type == :json
|
15
|
+
options[:body]=self.request_body.to_json
|
16
|
+
options[:headers]={"Content-Type"=>"application/json"}
|
17
|
+
else
|
18
|
+
options[:body]=self.request_body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
response = HTTParty.send(self.method.downcase.to_sym,self.test_host + self.path, options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Contract.class_eval { include RequestMethods }
|
26
|
+
|
27
|
+
class SpecWriter
|
28
|
+
def self.create_example_groups
|
29
|
+
ServiceRegistry.instance.services.values.each do |service|
|
30
|
+
|
31
|
+
eg = RSpec::Core::ExampleGroup.describe("#{service.name} specs") do
|
32
|
+
before :all, &(service.before_blocks[:all]) if service.before_blocks[:all]
|
33
|
+
before :each, &(service.before_blocks[:each]) if service.before_blocks[:each]
|
34
|
+
after :all, &(service.after_blocks[:all]) if service.after_blocks[:all]
|
35
|
+
after :each, &(service.after_blocks[:each]) if service.after_blocks[:each]
|
36
|
+
|
37
|
+
ServiceRegistry.instance.paths_for(service.name).each do |contract|
|
38
|
+
it "#{contract.method} #{contract.path} should return with #{contract.contract_status}" do
|
39
|
+
path_object = self.instance_eval(&contract.path_object_block) if contract.path_object_block
|
40
|
+
response = contract.perform_request(:path_object=>path_object)
|
41
|
+
response.code.should==contract.contract_status
|
42
|
+
contract.prepare_response(response.body).should == contract.json_for_spec if contract.contract_json
|
43
|
+
response.body.should == contract.contract_body if contract.contract_body
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
eg.register
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'helper/spec_helper'
|
2
|
+
|
3
|
+
describe Service do
|
4
|
+
before(:each) do
|
5
|
+
json_hash = {id: 42, name: "Varadha", email: "varadha@thoughtworks.com" }
|
6
|
+
@expected_hash = json_hash.stringify_keys
|
7
|
+
@expected_hash.delete("id")
|
8
|
+
|
9
|
+
array_with_symbols_block = Proc.new {
|
10
|
+
status 200
|
11
|
+
json [json_hash], :ignore=>[:id]
|
12
|
+
}
|
13
|
+
array_with_strings_block = Proc.new {
|
14
|
+
status 200
|
15
|
+
json [json_hash.stringify_keys], :ignore=>[:id]
|
16
|
+
}
|
17
|
+
object_with_symbols_block = Proc.new {
|
18
|
+
status 200
|
19
|
+
json json_hash, :ignore=>[:id]
|
20
|
+
}
|
21
|
+
object_with_strings_block = Proc.new {
|
22
|
+
status 200
|
23
|
+
json json_hash.stringify_keys, :ignore=>[:id]
|
24
|
+
}
|
25
|
+
|
26
|
+
@array_symbols_contract = Contract.new({block: array_with_symbols_block})
|
27
|
+
@array_strings_contract = Contract.new({block: array_with_strings_block})
|
28
|
+
@object_symbols_contract = Contract.new({block: object_with_symbols_block})
|
29
|
+
@object_strings_contract = Contract.new({block: object_with_strings_block})
|
30
|
+
end
|
31
|
+
|
32
|
+
it "json_spec should delete ignored attributes" do
|
33
|
+
@array_symbols_contract.json_for_spec.should == [@expected_hash]
|
34
|
+
@array_strings_contract.json_for_spec.should == [@expected_hash]
|
35
|
+
@object_symbols_contract.json_for_spec.should == @expected_hash
|
36
|
+
@object_strings_contract.json_for_spec.should == @expected_hash
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path("../../../lib/irumugam",__FILE__)
|
2
|
+
require 'factory_girl'
|
3
|
+
require 'webmock/rspec'
|
4
|
+
require 'rack/test'
|
5
|
+
include Rack::Test::Methods
|
6
|
+
include ::Irumugam
|
7
|
+
|
8
|
+
FactoryGirl.define do
|
9
|
+
factory :user, :class=>Object do
|
10
|
+
name "Some Name"
|
11
|
+
email "someemail@example.com"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_reporter(formatter)
|
16
|
+
RSpec::Core::Reporter.new(formatter)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sandboxed(&block)
|
20
|
+
@orig_config = RSpec.configuration
|
21
|
+
@orig_world = RSpec.world
|
22
|
+
new_config = RSpec::Core::Configuration.new
|
23
|
+
new_world = RSpec::Core::World.new(new_config)
|
24
|
+
RSpec.instance_variable_set(:@configuration, new_config)
|
25
|
+
RSpec.instance_variable_set(:@world, new_world)
|
26
|
+
object = Object.new
|
27
|
+
object.extend(RSpec::Core::SharedExampleGroup)
|
28
|
+
(class << RSpec::Core::ExampleGroup; self; end).class_eval do
|
29
|
+
alias_method :orig_run, :run
|
30
|
+
def run(reporter=nil)
|
31
|
+
@orig_mock_space = RSpec::Mocks::space
|
32
|
+
RSpec::Mocks::space = RSpec::Mocks::Space.new
|
33
|
+
orig_run(reporter || NullObject.new)
|
34
|
+
ensure
|
35
|
+
RSpec::Mocks::space = @orig_mock_space
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
object.instance_eval(&block)
|
40
|
+
ensure
|
41
|
+
(class << RSpec::Core::ExampleGroup; self; end).class_eval do
|
42
|
+
remove_method :run
|
43
|
+
alias_method :run, :orig_run
|
44
|
+
remove_method :orig_run
|
45
|
+
end
|
46
|
+
|
47
|
+
RSpec.instance_variable_set(:@configuration, @orig_config)
|
48
|
+
RSpec.instance_variable_set(:@world, @orig_world)
|
49
|
+
end
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'helper/spec_helper'
|
2
|
+
|
3
|
+
describe Server do
|
4
|
+
def app
|
5
|
+
Server.new
|
6
|
+
end
|
7
|
+
before do
|
8
|
+
ServiceRegistry.cleanup!
|
9
|
+
varadha = {name: "Varadha", email:"varadha@thoughtworks.com"}
|
10
|
+
@varadha = varadha
|
11
|
+
describe_service "My Glorious Service" do
|
12
|
+
host "http://testservice"
|
13
|
+
end_point "/glorious_service"
|
14
|
+
get "/users" do
|
15
|
+
status 200
|
16
|
+
json [FactoryGirl.attributes_for(:user)]
|
17
|
+
end
|
18
|
+
get "/users/:id" do
|
19
|
+
status 200
|
20
|
+
body ({name: "Some Other Name", email: "email@example.com"}).to_json
|
21
|
+
end
|
22
|
+
post "/users.json", :body=>{name: "vagmi", email: "vagmi@thoughtworks.com"}, :type=>:json do
|
23
|
+
status 201
|
24
|
+
json({name: "vagmi", email: "vagmi@thoughtworks.com"}.merge(:id=>23), :ignore=>[:id])
|
25
|
+
end
|
26
|
+
post "/users.json", :body=>{name: "Varadha", email:"varadha@thoughtworks.com"}, :type=>:json do
|
27
|
+
status 201
|
28
|
+
json(varadha.merge(:id=>23), :ignore=>[:id])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
it "should serve rack requests" do
|
34
|
+
get "/glorious_service/users"
|
35
|
+
last_response.ok?.should be_true
|
36
|
+
last_response.body.should == [FactoryGirl.attributes_for(:user)].to_json
|
37
|
+
end
|
38
|
+
it "should serve post requests with a matching body" do
|
39
|
+
post "/glorious_service/users.json", @varadha.to_json, {'CONTENT_TYPE'=>'application/json'}
|
40
|
+
last_response.status.should == 201
|
41
|
+
JSON.parse(last_response.body).should == @varadha.stringify_keys.merge("id"=>23)
|
42
|
+
end
|
43
|
+
it "should serve a non-json request" do
|
44
|
+
get "/glorious_service/users/1"
|
45
|
+
last_response.ok?.should be_true
|
46
|
+
last_response.body.should == ({name: "Some Other Name", email: "email@example.com"}).to_json
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'helper/spec_helper'
|
2
|
+
|
3
|
+
describe ServiceRegistry do
|
4
|
+
before(:each) do
|
5
|
+
ServiceRegistry.cleanup!
|
6
|
+
ServiceRegistry.desc "Auth Service" do
|
7
|
+
end_point "/mount_point"
|
8
|
+
host "http://testhost"
|
9
|
+
get "/path" do
|
10
|
+
#expectations
|
11
|
+
end
|
12
|
+
get "/some_other/path" do
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
it "should have a service called Auth Service" do
|
17
|
+
service = ServiceRegistry.instance.services["Auth Service"]
|
18
|
+
service.should_not be_nil
|
19
|
+
service.test_host.should == "http://testhost"
|
20
|
+
service.rest_end_point.should == "/mount_point"
|
21
|
+
end
|
22
|
+
it "should find a contract given a url pattern" do
|
23
|
+
contract = ServiceRegistry.find(Rack::Request.new(Rack::MockRequest.env_for("http://testhost/mount_point/path")))
|
24
|
+
contract.should_not be_nil
|
25
|
+
contract.path.should == "/mount_point/path"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'helper/spec_helper'
|
2
|
+
|
3
|
+
describe Service do
|
4
|
+
before(:each) do
|
5
|
+
ServiceRegistry.cleanup!
|
6
|
+
@block = Proc.new {
|
7
|
+
end_point "/end_point"
|
8
|
+
before(:each) do
|
9
|
+
DB.create_record
|
10
|
+
end
|
11
|
+
before(:all) do
|
12
|
+
DB.create_record
|
13
|
+
end
|
14
|
+
after(:all) do
|
15
|
+
DB.create_record
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:each) do
|
19
|
+
DB.create_record
|
20
|
+
end
|
21
|
+
}
|
22
|
+
@service = Service.new("Test Service",@block)
|
23
|
+
end
|
24
|
+
it "should add contracts" do
|
25
|
+
@service.get "/users" do
|
26
|
+
status 200
|
27
|
+
body ({msg: "Hello world"}.to_json)
|
28
|
+
end
|
29
|
+
@service.post "/users" do
|
30
|
+
status 200
|
31
|
+
body ({msg: "Hello world"}.to_json)
|
32
|
+
end
|
33
|
+
@service.put "/users" do
|
34
|
+
status 200
|
35
|
+
body ({msg: "Hello world"}.to_json)
|
36
|
+
end
|
37
|
+
@service.delete "/users" do
|
38
|
+
status 200
|
39
|
+
body ({msg: "Hello world"}.to_json)
|
40
|
+
end
|
41
|
+
@service.paths["/end_point/users"].count.should==4
|
42
|
+
@service.paths["/end_point/users"].map(&:method).should =~ ["GET", "POST", "PUT", "DELETE"]
|
43
|
+
end
|
44
|
+
it "should have set the before and after hooks" do
|
45
|
+
@service.before_blocks[:each].should_not be_nil
|
46
|
+
@service.before_blocks[:all].should_not be_nil
|
47
|
+
@service.after_blocks[:each].should_not be_nil
|
48
|
+
@service.after_blocks[:all].should_not be_nil
|
49
|
+
end
|
50
|
+
it "should accept json in the contract content" do
|
51
|
+
@service.get "/users.json" do
|
52
|
+
status 200
|
53
|
+
json [{:name=>"name"}], :ignore=>[:id]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'helper/spec_helper'
|
2
|
+
describe SpecWriter do
|
3
|
+
before do
|
4
|
+
varadha = {name: "Varadha", email: "varadha@thoughtworks.com"}
|
5
|
+
stub_request(:get,"http://testservice/glorious_service/users").with(:query=>{}).to_return(:body=>[FactoryGirl.attributes_for(:user)].to_json)
|
6
|
+
stub_request(:get,"http://testservice/glorious_service/users/1").with(:query=>{}).to_return(:body=>FactoryGirl.attributes_for(:user).to_json)
|
7
|
+
stub_request(:get,"http://testservice/glorious_service/users/1.json").with(:query=>{}).to_return(:body=>FactoryGirl.attributes_for(:user).to_json)
|
8
|
+
stub_request(:post,"http://testservice/glorious_service/users.json").with(:query=>{},:body=>varadha).to_return(:body=>varadha.merge({"id"=>22}).to_json, :status=>201)
|
9
|
+
stub_request(:put,"http://testservice/glorious_service/users/42.json").with(:query=>{},:body=>varadha).to_return(:body=>varadha.merge({"id"=>42}).to_json, :status=>201)
|
10
|
+
$test_object = dup()
|
11
|
+
$test_object.should_receive(:teardown).exactly(6).times
|
12
|
+
$test_object.should_receive(:setup).exactly(6).times
|
13
|
+
$test_object.should_receive(:setup_once).once
|
14
|
+
$test_object.should_receive(:teardown_once).once
|
15
|
+
|
16
|
+
ServiceRegistry.cleanup!
|
17
|
+
|
18
|
+
describe_service "My Glorious Service" do
|
19
|
+
host "http://testservice"
|
20
|
+
end_point "/glorious_service"
|
21
|
+
|
22
|
+
before :all do
|
23
|
+
$test_object.setup_once
|
24
|
+
end
|
25
|
+
|
26
|
+
after :all do
|
27
|
+
$test_object.teardown_once
|
28
|
+
end
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
$test_object.setup
|
32
|
+
@path_object = {:id=>42}
|
33
|
+
end
|
34
|
+
|
35
|
+
after(:each) do
|
36
|
+
$test_object.teardown
|
37
|
+
end
|
38
|
+
|
39
|
+
get "/users" do
|
40
|
+
status 200
|
41
|
+
json [FactoryGirl.attributes_for(:user)]
|
42
|
+
end
|
43
|
+
|
44
|
+
get "/users/1.json" do
|
45
|
+
status 200
|
46
|
+
json({id: 43, name: "Some Name", email: "someemail@example.com"}, :ignore=>[:id])
|
47
|
+
end
|
48
|
+
|
49
|
+
get "/users/1.json" do
|
50
|
+
status 200
|
51
|
+
json({"id" => 42, "name"=>"Some Name", "email"=>"someemail@example.com"}, :ignore=>["id"])
|
52
|
+
end
|
53
|
+
|
54
|
+
post "/users.json", :body=>varadha, :type=>:json do
|
55
|
+
status 201
|
56
|
+
json varadha.merge(:id=>42), :ignore=>[:id]
|
57
|
+
end
|
58
|
+
|
59
|
+
put "/users/:id.json", :body=>varadha, :type=>:json do
|
60
|
+
status 201
|
61
|
+
json varadha.merge(:id=>42), :ignore=>[:id]
|
62
|
+
path_object { @path_object }
|
63
|
+
end
|
64
|
+
|
65
|
+
get "/users/1" do
|
66
|
+
status 200
|
67
|
+
body ({name: "Some Other Name", email: "email@example.com"}).to_json
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should define specs for the above services" do
|
75
|
+
sandboxed do
|
76
|
+
formatter = RSpec::Core::Formatters::BaseFormatter.new(nil)
|
77
|
+
reporter = get_reporter(formatter)
|
78
|
+
register_with_rspec!
|
79
|
+
RSpec.world.example_groups.count.should == 1
|
80
|
+
RSpec.world.example_groups.first.run(reporter)
|
81
|
+
#formatter.failed_examples.each do |fe|
|
82
|
+
#puts fe.metadata[:description_args]
|
83
|
+
#puts fe.metadata[:execution_result]
|
84
|
+
#end
|
85
|
+
formatter.examples.count.should == 6
|
86
|
+
formatter.failed_examples.count.should == 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irumugam
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Shakir Shakiel
|
9
|
+
- Vagmi Mudumbai
|
10
|
+
- Lokesh D
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2012-07-31 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: webmock
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: factory_girl
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rack-test
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: rake
|
66
|
+
requirement: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
type: :development
|
73
|
+
prerelease: false
|
74
|
+
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rspec
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rack
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: httparty
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
type: :runtime
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
name: json
|
130
|
+
requirement: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
type: :runtime
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
- !ruby/object:Gem::Dependency
|
145
|
+
name: activesupport
|
146
|
+
requirement: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :runtime
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
description: Describe service contracts that act both as a spec and a mock
|
161
|
+
email:
|
162
|
+
- shakiras@thoughtworks.com
|
163
|
+
- vagmimud@thoughtworks.com
|
164
|
+
- lokeshd@thoughtworks.com
|
165
|
+
executables: []
|
166
|
+
extensions: []
|
167
|
+
extra_rdoc_files: []
|
168
|
+
files:
|
169
|
+
- .gitignore
|
170
|
+
- .rvmrc
|
171
|
+
- .travis.yml
|
172
|
+
- Gemfile
|
173
|
+
- README.md
|
174
|
+
- Rakefile
|
175
|
+
- irumugam.gemspec
|
176
|
+
- lib/irumugam.rb
|
177
|
+
- lib/irumugam/contract.rb
|
178
|
+
- lib/irumugam/server.rb
|
179
|
+
- lib/irumugam/service.rb
|
180
|
+
- lib/irumugam/service_registry.rb
|
181
|
+
- lib/irumugam/spec_writer.rb
|
182
|
+
- lib/irumugam/version.rb
|
183
|
+
- spec/contract_spec.rb
|
184
|
+
- spec/helper/spec_helper.rb
|
185
|
+
- spec/server_spec.rb
|
186
|
+
- spec/service_registry_spec.rb
|
187
|
+
- spec/service_spec.rb
|
188
|
+
- spec/spec_writer_spec.rb
|
189
|
+
homepage: ''
|
190
|
+
licenses: []
|
191
|
+
post_install_message:
|
192
|
+
rdoc_options: []
|
193
|
+
require_paths:
|
194
|
+
- lib
|
195
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
196
|
+
none: false
|
197
|
+
requirements:
|
198
|
+
- - ! '>='
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
segments:
|
202
|
+
- 0
|
203
|
+
hash: 1712470912957311514
|
204
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
|
+
none: false
|
206
|
+
requirements:
|
207
|
+
- - ! '>='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0'
|
210
|
+
segments:
|
211
|
+
- 0
|
212
|
+
hash: 1712470912957311514
|
213
|
+
requirements: []
|
214
|
+
rubyforge_project: irumugam
|
215
|
+
rubygems_version: 1.8.24
|
216
|
+
signing_key:
|
217
|
+
specification_version: 3
|
218
|
+
summary: Describe service contracts that act both as a spec and a mock
|
219
|
+
test_files:
|
220
|
+
- spec/contract_spec.rb
|
221
|
+
- spec/helper/spec_helper.rb
|
222
|
+
- spec/server_spec.rb
|
223
|
+
- spec/service_registry_spec.rb
|
224
|
+
- spec/service_spec.rb
|
225
|
+
- spec/spec_writer_spec.rb
|