mock5 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5cc09164060a34461288bd4e5613a57179adfbd9
4
+ data.tar.gz: 65a3f5617db8ee8a94529e1017025a182bc2b631
5
+ SHA512:
6
+ metadata.gz: e8823f3731b6c641e1cc9d4b6a773bf02f6bb44c974146bba2139e4823b0d74b3b3c4c6cde579ae1b5ad3bacdc97f7f51e7ea090f9c3f128911f38ef680817b0
7
+ data.tar.gz: c6bddb69871f5d54b5d35cd3ad6dc2ec2b04408b4546f31bfaad900cd396ee34dd8cfccae4f0386101d021dc74a14139c2c10eab13079bf1999e0ddbf2aa1457
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.1
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mock5.gemspec
4
+ gemspec
5
+
6
+ gem "bundler", "~> 1.5"
7
+ gem "rspec", "~> 2.14"
8
+ gem "rake", "~> 10.1"
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Pavel Pravosud
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Mock5
2
+ [![Gem Version](https://img.shields.io/gem/v/mock5.svg)][gem]
3
+ [![Build Status](https://img.shields.io/travis/rwz/mock5.svg)][travis]
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/rwz/mock5.svg)][codeclimate]
5
+ [gem]: https://rubygems.org/gems/mock5
6
+ [travis]: http://travis-ci.org/rwz/mock5
7
+ [codeclimate]: https://codeclimate.com/github/rwz/mock5
8
+
9
+ Mock5 allows to mock external APIs with simple Sinatra Rake apps.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem "mock5"
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install mock5
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ SuccessfulRegistration = Mock5.mock("http://example.com") do
29
+ post "/users" do
30
+ # emulate successful registration
31
+ MultiJson.dump(
32
+ first_name: "Zapp",
33
+ last_name: "Brannigan",
34
+ email: "zapp@planetexpress.com"
35
+ )
36
+ end
37
+ end
38
+
39
+ UnsuccessfulRegistration = Mock5.mock("http://example.com") do
40
+ post "/users" do
41
+ halt 406, MultiJson.dump(
42
+ first_name: ["is too lame"],
43
+ email: ["is not unique"]
44
+ )
45
+ end
46
+ end
47
+
48
+ describe MyApi do
49
+ describe "successfull" do
50
+ around do |example|
51
+ Mock5.with_mounted SuccessfulRegistration do
52
+ example.call
53
+ end
54
+ end
55
+
56
+ it "allows user registration" do
57
+ expect{ MyApi.register_user }.not_to raise_error
58
+ end
59
+ end
60
+
61
+ describe "validation errors" do
62
+ around do |example|
63
+ Mock5.with_mounted UnsuccessfulRegistration do
64
+ example.call
65
+ end
66
+ end
67
+
68
+ it "returns errors" do
69
+ expect{ MyApi.register_user }.to raise_error(MyApi::ValidationError)
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task default: :spec
data/lib/mock5.rb ADDED
@@ -0,0 +1,54 @@
1
+ require "mock5/version"
2
+ require "mock5/api"
3
+ require "set"
4
+
5
+ module Mock5
6
+ extend self
7
+
8
+ def mounted_apis
9
+ @_mounted_apis ||= Set.new
10
+ end
11
+
12
+ def mock(*args, &block)
13
+ Api.new(*args, &block)
14
+ end
15
+
16
+ def mount(*apis)
17
+ apis.each do |api|
18
+ if mounted_apis.add?(api)
19
+ registry.register_request_stub api.request_stub
20
+ end
21
+ end
22
+ end
23
+
24
+ def unmount(*apis)
25
+ apis.each do |api|
26
+ if mounted_apis.delete?(api)
27
+ registry.remove_request_stub api.request_stub
28
+ end
29
+ end
30
+ end
31
+
32
+ def mounted?(*apis)
33
+ apis.to_set.subset?(mounted_apis)
34
+ end
35
+
36
+ def with_mounted(*apis)
37
+ mount *apis
38
+ yield
39
+ ensure
40
+ unmount *apis
41
+ end
42
+
43
+ def unmount_all!
44
+ unmount *mounted_apis
45
+ end
46
+
47
+ alias_method :reset!, :unmount_all!
48
+
49
+ private
50
+
51
+ def registry
52
+ WebMock::StubRegistry.instance
53
+ end
54
+ end
data/lib/mock5/api.rb ADDED
@@ -0,0 +1,59 @@
1
+ require "uri"
2
+ require "sinatra"
3
+ require "webmock"
4
+
5
+ module Mock5
6
+ class Api
7
+ attr_reader :endpoint, :app
8
+
9
+ def initialize(endpoint=nil, &block)
10
+ @app = Sinatra.new(&block)
11
+ @endpoint = normalize_endpoint(endpoint)
12
+ end
13
+
14
+ def request_stub
15
+ @request_stub ||= WebMock::RequestStub.new(:any, endpoint).tap{ |s| s.to_rack(app) }
16
+ end
17
+
18
+ private
19
+
20
+ def normalize_endpoint(endpoint)
21
+ case endpoint
22
+ when nil
23
+ /.*/
24
+ when String
25
+ normalize_string_endpoint(endpoint)
26
+ when Regexp
27
+ endpoint
28
+ else
29
+ raise ArgumentError, "Endpoint should hbe string or regexp"
30
+ end
31
+ end
32
+
33
+ def normalize_string_endpoint(endpoint)
34
+ uri = URI.parse(endpoint)
35
+
36
+ if uri.scheme !~ /\Ahttps?/
37
+ raise ArgumentError, "Endpoint should be a valid URL"
38
+ elsif uri.path != ?/ && !uri.path.empty?
39
+ raise ArgumentError, "Endpoint URL should not include path"
40
+ end
41
+
42
+ uri.path = ""
43
+ endpoint = Regexp.escape(uri.to_s)
44
+
45
+ Regexp.new("\\A#{endpoint}\/#{app_paths_regex}\\z")
46
+ end
47
+
48
+ def app_paths_regex
49
+ regexes = app.routes.values.flatten.select{ |v| Regexp === v }
50
+ paths = regexes.map{ |regex| regex.source[3..-3] }
51
+
52
+ return ".*" if paths.empty?
53
+
54
+ paths = paths.one?? paths.first : %{(?:#{paths.join("|")})}
55
+
56
+ "#{paths}(?:\\?.*)?"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Mock5
2
+ VERSION = "1.0.1".freeze
3
+ end
data/mock5.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "mock5/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mock5"
8
+ spec.version = Mock5::VERSION
9
+ spec.authors = ["Pavel Pravosud"]
10
+ spec.email = ["pavel@pravosud.com"]
11
+ spec.summary = "Mock APIs using Sinatra"
12
+ spec.description = "Create and manage API mocks with Sinatra"
13
+ spec.homepage = "https://github.com/rwz/mock5"
14
+ spec.license = "MIT"
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.test_files = spec.files.grep(/^spec/)
17
+ spec.require_path = "lib"
18
+ spec.required_ruby_version = ">= 1.9.3"
19
+
20
+ spec.add_dependency "webmock", "~> 1.15"
21
+ spec.add_dependency "sinatra", "~> 1.4"
22
+ end
@@ -0,0 +1,71 @@
1
+ require "mock5/api"
2
+ require "rack/mock"
3
+
4
+ describe Mock5::Api do
5
+ describe "#endpoint" do
6
+ it "matches all by default" do
7
+ expect(subject.endpoint).to eq(/.*/)
8
+ end
9
+
10
+ it "can be specified as a regex" do
11
+ api = described_class.new(/foo/)
12
+ expect(api.endpoint).to eq(/foo/)
13
+ end
14
+
15
+ it "can be specified as a valid url without path" do
16
+ api = described_class.new("http://example.com")
17
+ expect(api.endpoint).to eq(%r(\Ahttp://example\.com/.*\z))
18
+ end
19
+
20
+ it "can not be specified as a valid url with path" do
21
+ expect{ described_class.new("http://example.com/foo") }
22
+ .to raise_error(ArgumentError, "Endpoint URL should not include path")
23
+ end
24
+
25
+ it "can not be specified as a invalid url string" do
26
+ expect{ described_class.new("foo") }
27
+ .to raise_error(ArgumentError, "Endpoint should be a valid URL")
28
+ end
29
+
30
+ it "can not be specified by anything else" do
31
+ [false, :foo, 123].each do |invalid_endpoint|
32
+ expect{ described_class.new(invalid_endpoint) }
33
+ .to raise_error(ArgumentError)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#app" do
39
+ it "is a Class" do
40
+ expect(subject.app).to be_kind_of(Class)
41
+ end
42
+
43
+ it "is a Sinatra Rack app" do
44
+ expect(subject.app.superclass).to eq(Sinatra::Base)
45
+ end
46
+
47
+ describe "configuration" do
48
+ subject do
49
+ described_class.new do
50
+ get "/hello/:what" do |what|
51
+ "Hello, #{what.capitalize}"
52
+ end
53
+ end
54
+ end
55
+
56
+ let(:server){ Rack::Server.new(app: subject.app) }
57
+ let(:mock_request){ Rack::MockRequest.new(server.app) }
58
+
59
+ it "can be configures by a block" do
60
+ response = mock_request.get("/hello/world")
61
+ expect(response.body.to_s).to eq("Hello, World")
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#request_stub" do
67
+ it "returns a request stub" do
68
+ expect(subject.request_stub).to be_kind_of(WebMock::RequestStub)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,167 @@
1
+ require "mock5"
2
+
3
+ describe Mock5 do
4
+ describe ".mock" do
5
+ it "creates an Api" do
6
+ block = ->{}
7
+ expect(described_class::Api).to receive(:new).with(/foo/, &block)
8
+ described_class.mock /foo/, &block
9
+ end
10
+
11
+ it "returns an Api" do
12
+ expect(described_class.mock).to be_kind_of(described_class::Api)
13
+ end
14
+ end
15
+
16
+ describe "API mgmt" do
17
+ before do
18
+ WebMock::StubRegistry.instance.reset!
19
+ described_class.instance_exec do
20
+ if instance_variable_defined?(:@_mounted_apis)
21
+ remove_instance_variable(:@_mounted_apis)
22
+ end
23
+ end
24
+ end
25
+
26
+ let(:mounted_apis){ described_class.mounted_apis }
27
+ let(:api){ described_class.mock }
28
+ let(:another_api){ described_class.mock }
29
+
30
+ describe ".mount" do
31
+ it "mounts an api" do
32
+ described_class.mount api
33
+ expect(mounted_apis).to include(api)
34
+ end
35
+
36
+ it "mounts an api only once" do
37
+ 10.times{ described_class.mount api }
38
+ expect(mounted_apis).to have(1).item
39
+ end
40
+
41
+ it "mounts several APIs at once" do
42
+ described_class.mount api, another_api
43
+ expect(mounted_apis).to include(api)
44
+ expect(mounted_apis).to include(another_api)
45
+ end
46
+ end
47
+
48
+ describe ".unmount" do
49
+ before{ described_class.mount api }
50
+
51
+ it "unmounts mounted api" do
52
+ described_class.unmount api
53
+ expect(mounted_apis).to be_empty
54
+ end
55
+
56
+ it "unmounts api only once" do
57
+ 10.times{ described_class.unmount api }
58
+ expect(mounted_apis).to be_empty
59
+ end
60
+
61
+ it "unmounts several APIs at once" do
62
+ described_class.mount another_api
63
+ expect(mounted_apis).to have(2).apis
64
+ described_class.unmount api, another_api
65
+ expect(mounted_apis).to be_empty
66
+ end
67
+
68
+ it "only unmount specified api" do
69
+ described_class.mount another_api
70
+ described_class.unmount api
71
+ expect(mounted_apis).to include(another_api)
72
+ end
73
+ end
74
+
75
+ describe ".unmount_all!" do
76
+ before do
77
+ 3.times{ described_class.mount described_class.mock }
78
+ end
79
+
80
+ it "unmounts all currently mounted apis" do
81
+ expect(mounted_apis).to have(3).apis
82
+ described_class.unmount_all!
83
+ expect(mounted_apis).to be_empty
84
+ end
85
+
86
+ it "has .reset! alias" do
87
+ expect(mounted_apis).to have(3).apis
88
+ described_class.reset!
89
+ expect(mounted_apis).to be_empty
90
+ end
91
+ end
92
+
93
+ describe ".mounted?" do
94
+ before{ described_class.mount api }
95
+
96
+ it "returns true if api is currently mounted" do
97
+ expect(described_class.mounted?(api)).to be_true
98
+ end
99
+
100
+ it "returns false if api is currently not mounted" do
101
+ expect(described_class.mounted?(another_api)).to be_false
102
+ end
103
+
104
+ it "returns true only when ALL api are mounted" do
105
+ action = ->{ described_class.mount another_api }
106
+ result = ->{ described_class.mounted? api, another_api }
107
+ expect(&action).to change(&result).from(false).to(true)
108
+ end
109
+ end
110
+
111
+ describe ".with_mounted" do
112
+ it "temporary mounts an API" do
113
+ action = -> do
114
+ described_class.with_mounted api do
115
+ expect(mounted_apis).to include(api)
116
+ end
117
+ end
118
+
119
+ expect(mounted_apis).to be_empty
120
+ expect(&action).not_to change(mounted_apis, :empty?)
121
+ end
122
+ end
123
+
124
+ describe "stubbing" do
125
+ def get(url)
126
+ Net::HTTP.get(URI(url))
127
+ end
128
+
129
+ def post(url, params={})
130
+ Net::HTTP.post_form(URI(url), params).body
131
+ end
132
+
133
+ let(:api) do
134
+ described_class.mock "http://example.com" do
135
+ get "/index.html" do
136
+ "index.html"
137
+ end
138
+
139
+ post "/submit/here" do
140
+ "submit"
141
+ end
142
+ end
143
+ end
144
+
145
+ let(:another_api) do
146
+ described_class.mock "http://example.com" do
147
+ post "/foo/:foo" do
148
+ params["foo"]
149
+ end
150
+
151
+ get "/bar/:bar" do
152
+ params["bar"]
153
+ end
154
+ end
155
+ end
156
+
157
+ before{ described_class.mount api, another_api }
158
+
159
+ it "stubs remote apis" do
160
+ expect(get("http://example.com/index.html?foo=bar")).to eq("index.html")
161
+ expect(post("http://example.com/submit/here?foo=bar")).to eq("submit")
162
+ expect(post("http://example.com/foo/bar?fizz=buzz")).to eq("bar")
163
+ expect(get("http://example.com/bar/foo")).to eq("foo")
164
+ end
165
+ end
166
+ end
167
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mock5
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pavel Pravosud
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: webmock
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ description: Create and manage API mocks with Sinatra
42
+ email:
43
+ - pavel@pravosud.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".travis.yml"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - lib/mock5.rb
55
+ - lib/mock5/api.rb
56
+ - lib/mock5/version.rb
57
+ - mock5.gemspec
58
+ - spec/mock5_api_spec.rb
59
+ - spec/mock5_spec.rb
60
+ homepage: https://github.com/rwz/mock5
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.9.3
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Mock APIs using Sinatra
84
+ test_files:
85
+ - spec/mock5_api_spec.rb
86
+ - spec/mock5_spec.rb