irumugam 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](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
|