webvalve 0.9.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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +195 -0
  4. data/Rakefile +22 -0
  5. data/lib/generators/webvalve/fake_service_generator.rb +70 -0
  6. data/lib/generators/webvalve/install_generator.rb +33 -0
  7. data/lib/tasks/webvalve_tasks.rake +0 -0
  8. data/lib/webvalve.rb +40 -0
  9. data/lib/webvalve/engine.rb +26 -0
  10. data/lib/webvalve/fake_service.rb +22 -0
  11. data/lib/webvalve/fake_service_config.rb +53 -0
  12. data/lib/webvalve/fake_service_wrapper.rb +18 -0
  13. data/lib/webvalve/instrumentation.rb +4 -0
  14. data/lib/webvalve/instrumentation/log_subscriber.rb +16 -0
  15. data/lib/webvalve/instrumentation/middleware.rb +24 -0
  16. data/lib/webvalve/manager.rb +76 -0
  17. data/lib/webvalve/rspec.rb +8 -0
  18. data/lib/webvalve/version.rb +3 -0
  19. data/spec/dummy/README.rdoc +28 -0
  20. data/spec/dummy/Rakefile +6 -0
  21. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  22. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  26. data/spec/dummy/bin/bundle +3 -0
  27. data/spec/dummy/bin/rails +4 -0
  28. data/spec/dummy/bin/rake +4 -0
  29. data/spec/dummy/bin/setup +29 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +31 -0
  32. data/spec/dummy/config/boot.rb +5 -0
  33. data/spec/dummy/config/database.yml +25 -0
  34. data/spec/dummy/config/environment.rb +5 -0
  35. data/spec/dummy/config/environments/development.rb +41 -0
  36. data/spec/dummy/config/environments/production.rb +79 -0
  37. data/spec/dummy/config/environments/test.rb +42 -0
  38. data/spec/dummy/config/initializers/assets.rb +11 -0
  39. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  41. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  42. data/spec/dummy/config/initializers/inflections.rb +16 -0
  43. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  44. data/spec/dummy/config/initializers/session_store.rb +3 -0
  45. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  46. data/spec/dummy/config/locales/en.yml +23 -0
  47. data/spec/dummy/config/routes.rb +3 -0
  48. data/spec/dummy/config/secrets.yml +22 -0
  49. data/spec/dummy/db/test.sqlite3 +0 -0
  50. data/spec/dummy/log/development.log +0 -0
  51. data/spec/dummy/log/test.log +3938 -0
  52. data/spec/dummy/public/404.html +67 -0
  53. data/spec/dummy/public/422.html +67 -0
  54. data/spec/dummy/public/500.html +66 -0
  55. data/spec/dummy/public/favicon.ico +0 -0
  56. data/spec/examples.txt +30 -0
  57. data/spec/rails_helper.rb +14 -0
  58. data/spec/spec_helper.rb +22 -0
  59. data/spec/support/helpers.rb +24 -0
  60. data/spec/webvalve/fake_service_config_spec.rb +133 -0
  61. data/spec/webvalve/fake_service_spec.rb +47 -0
  62. data/spec/webvalve/manager_spec.rb +132 -0
  63. data/spec/webvalve_spec.rb +19 -0
  64. metadata +268 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 75f876084825a8cc70b8977e26afa6133f0191a0
4
+ data.tar.gz: acb351fdfc895185361b55e0e756d3488e2a2ea0
5
+ SHA512:
6
+ metadata.gz: 8ed9a0819c09b29f2bfe2fccf03d3234acaf1dec62ef29eeb5387894c2ab20bfa7b6daeeee684ef8b2e044096136fa85cfd7558bba17bcd0ad82ae5434f3b1fa
7
+ data.tar.gz: cf953ff845781e4320197f6a62c76f49b422514cdc73066ebded9fd2d07716865bb4183f346548b3a12ffa855e989d0435f51134f3e67f9c5a18820c8e822243
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Betterment
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,195 @@
1
+ WebValve
2
+ ========
3
+
4
+ [![Build Status](https://travis-ci.com/Betterment/webvalve.svg?token=6b6DErRMUHX47kEoBZ3t&branch=master)](https://travis-ci.com/Betterment/webvalve)
5
+
6
+ WebValve is a tool for defining and registering fake implementations of
7
+ HTTP services and toggling between the real services and the fake ones
8
+ in non-production environments.
9
+
10
+ This library is made possible by the incredible gems
11
+ [WebMock](https://github.com/bblimke/webmock) and
12
+ [Sinatra](https://github.com/sinatra/sinatra).
13
+
14
+ ## Getting Started
15
+
16
+ ### Network connections disabled by default
17
+
18
+ The default mode in development and test is to disallow all HTTP network
19
+ connections. This provides a clean foundation for consuming new
20
+ services. If you add a new service integration, the first thing that you
21
+ will be presented with when you attempt to hit it in development or test
22
+ is a warning that the requested URL was not mocked. This behavior comes
23
+ straight outta WebMock.
24
+
25
+ ```ruby
26
+ irb(main):007:0> Net::HTTP.get(URI('http://bank.dev'))
27
+
28
+ WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request: GET http://bank.dev/ with headers {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}
29
+
30
+ You can stub this request with the following snippet:
31
+
32
+ stub_request(:get, "http://bank.dev/").
33
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
34
+ to_return(:status => 200, :body => "", :headers => {})
35
+
36
+ ============================================================
37
+ ```
38
+
39
+ ### Creating a config file
40
+
41
+ The first thing to do is run the install generator.
42
+
43
+ ```
44
+ $ rails generate webvalve:install
45
+ ```
46
+
47
+ This will drop a new file in your config directory.
48
+
49
+ ```ruby
50
+ # config/webvalve.rb
51
+
52
+ # # register services
53
+ #
54
+ # WebValve.register FakeBank
55
+ # WebValve.register FakeExample, url: 'https://api.example.org'
56
+ #
57
+ # # whitelist urls
58
+ #
59
+ # WebValve.whitelist_url 'https://example.com'
60
+ ```
61
+
62
+ ### Registering a service
63
+
64
+ Next, you will want create a `FakeService` and register
65
+ it with the framework.
66
+
67
+ This can be accomplished by running the fake service generator:
68
+
69
+ ```
70
+ $ rails generate webvalve:fake_service Bank
71
+ ```
72
+
73
+ This will generate a file `fake_bank.rb` in the top-level folder
74
+ `webvalve`. This file will be autoloaded by Rails, so you can
75
+ tweak it as you go without having to restart your application.
76
+
77
+ ```ruby
78
+ # webvalve/fake_bank.rb
79
+
80
+ class FakeBank < WebValve::FakeService
81
+ # # define your routes here
82
+ #
83
+ # get '/widgets' do
84
+ # json result: 'it works!'
85
+ # end
86
+ #
87
+ # # toggle this service on via ENV
88
+ #
89
+ # export BANK_ENABLED=true
90
+ end
91
+ ```
92
+
93
+ And it will automatically register it in `config/webvalve.rb`
94
+
95
+ ```ruby
96
+ # config/webvalve.rb
97
+ WebValve.register FakeBank
98
+ ```
99
+
100
+ You'll also want to define an environment variable for the base url of
101
+ your service.
102
+
103
+ ```bash
104
+ export BANK_API_URL='http://bank.dev'
105
+ ```
106
+
107
+ That's it. Now when you hit your service again, it will route your
108
+ request into the `FakeBank` instance.
109
+
110
+ If you want to connect to the _actual_ service, all you have to do is
111
+ set another environment variable.
112
+
113
+ ```bash
114
+ export BANK_ENABLED=true
115
+ ```
116
+
117
+ You will have to restart your application after making this change
118
+ because service faking is an initialization time concern and not a
119
+ runtime concern.
120
+
121
+ ## Configuring fakes in tests
122
+
123
+ In order to get WebValve fake servies working properly in tests, you
124
+ have to configure WebValve at the beginning of each test. For RSpec, there
125
+ is a configuration provided.
126
+
127
+ ```ruby
128
+ # spec/rails_helper.rb
129
+ require 'webvalve/rspec'
130
+ ```
131
+
132
+ For any other test framework, you will just want to set up a hook before
133
+ each test that will run `WebValve.setup`.
134
+
135
+ ## Setting deterministic fake results in tests
136
+
137
+ Given a scenario where we want to mock a specific behavior for an
138
+ endpoint in a test, we can just use WebMock™.
139
+
140
+ ```ruby
141
+ # in an rspec test...
142
+
143
+ it 'handles 404s by returning nil' do
144
+ fake_req = stub_request('http://bank.dev/some/url/1234')
145
+ .to_return(status: 404, body: nil)
146
+
147
+ response = Faraday.get 'http://bank.dev/some/url/1234'
148
+ expect(response.body).to be_nil
149
+ expect(fake_req).to have_been_requested
150
+ end
151
+ ```
152
+
153
+ In other scenarios where we don't care about the specific response from
154
+ the endpoint, you can just lean into the behavior you've configured for
155
+ that route in your fake service.
156
+
157
+ ## Overriding conventional defaults
158
+
159
+ Sometimes a service integration may want to use an unconventional name
160
+ for its environment variables. In that case, you can register the fake
161
+ service using the optional `url:` argument.
162
+
163
+ ```ruby
164
+ # config/webvalve.rb
165
+
166
+ # using an ENV variable
167
+ WebValve.register FakeBank, url: ENV.fetch("SOME_CUSTOM_API_URL")
168
+
169
+ # or with a constant value
170
+ WebValve.register FakeBank, url: "https://some-service.com"
171
+ ```
172
+
173
+ ## What's in a `FakeService`?
174
+
175
+ The definition of `FakeService` is really simple. It's just a
176
+ `Sinatra::Base` class. It is wired up to support returning JSON
177
+ responses and it will raise when a route is requested but it is
178
+ not registered.
179
+
180
+ ## How to Contribute
181
+
182
+ We would love for you to contribute! Anything that benefits the majority
183
+ of `webvalve` users—from a documentation fix to an entirely new
184
+ feature—is encouraged.
185
+
186
+ Before diving in, [check our issue
187
+ tracker](//github.com/Betterment/webvalve/issues) and consider
188
+ creating a new issue to get early feedback on your proposed change.
189
+
190
+ ### Suggested Workflow
191
+
192
+ * Fork the project and create a new branch for your contribution.
193
+ * Write your contribution (and any applicable test coverage).
194
+ * Make sure all tests pass (`bundle exec rake`).
195
+ * Submit a pull request.
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
8
+ load 'rails/tasks/engine.rake'
9
+
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ if Rails.env.development? || Rails.env.test?
13
+ if defined? Dummy
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new(:spec)
17
+ task default: :spec
18
+ end
19
+ end
20
+
21
+ require 'yard'
22
+ YARD::Rake::YardocTask.new
@@ -0,0 +1,70 @@
1
+ require 'rails/generators/base'
2
+
3
+ module Webvalve
4
+ module Generators
5
+ class FakeServiceGenerator < Rails::Generators::Base
6
+ desc "Creates a WebValve fake service"
7
+
8
+ argument :service_name, required: true
9
+
10
+ def create_webvalve_fake_service_file
11
+ require_config!
12
+ create_fake_service_file
13
+ register_fake_in_config
14
+ end
15
+
16
+ private
17
+
18
+ def create_fake_service_file
19
+ create_file full_file_path, <<-FILE.strip_heredoc
20
+ class #{fake_service_class_name} < WebValve::FakeService
21
+ # # define your routes here
22
+ #
23
+ # get '/widgets' do
24
+ # json result: 'it works!'
25
+ # end
26
+ #
27
+ # # set the base url for this API via ENV
28
+ #
29
+ # export #{parsed_service_name.upcase}_API_URL='http://whatever.dev'
30
+ #
31
+ # # toggle this service on via ENV
32
+ #
33
+ # export #{parsed_service_name.upcase}_ENABLED=true
34
+ end
35
+ FILE
36
+ end
37
+
38
+ def register_fake_in_config
39
+ append_to_file config_file_path do <<~RUBY
40
+ WebValve.register #{fake_service_class_name}
41
+ RUBY
42
+ end
43
+ end
44
+
45
+ def require_config!
46
+ raise 'No WebValve configuration file found. Please run `rails generate webvalve:install` first' unless File.exists?(config_file_path)
47
+ end
48
+
49
+ def config_file_path
50
+ "config/webvalve.rb"
51
+ end
52
+
53
+ def full_file_path
54
+ "webvalve/#{fake_service_filename}.rb"
55
+ end
56
+
57
+ def fake_service_class_name
58
+ fake_service_filename.camelize
59
+ end
60
+
61
+ def fake_service_filename
62
+ "fake_#{parsed_service_name.underscore}"
63
+ end
64
+
65
+ def parsed_service_name
66
+ service_name.sub(/fake/i, '')
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails/generators/base'
2
+
3
+ module Webvalve
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Install WebValve"
7
+
8
+ def create_webvalve_config_file
9
+ create_config_file
10
+ create_file "webvalve/.keep", ""
11
+ end
12
+
13
+ private
14
+
15
+ def create_config_file
16
+ create_file full_file_path, <<-FILE.strip_heredoc
17
+ # # register services
18
+ #
19
+ # WebValve.register FakeThing
20
+ # WebValve.register FakeExample, url: 'https://api.example.org'
21
+ #
22
+ # # whitelist urls
23
+ #
24
+ # WebValve.whitelist_url 'https://example.com'
25
+ FILE
26
+ end
27
+
28
+ def full_file_path
29
+ "config/webvalve.rb"
30
+ end
31
+ end
32
+ end
33
+ end
File without changes
@@ -0,0 +1,40 @@
1
+ require 'set'
2
+
3
+ module WebValve
4
+ extend ActiveSupport::Autoload
5
+ autoload :FakeService, 'webvalve/fake_service'
6
+ autoload :FakeServiceWrapper, 'webvalve/fake_service_wrapper'
7
+ autoload :FakeServiceConfig, 'webvalve/fake_service_config'
8
+ autoload :Manager, 'webvalve/manager'
9
+
10
+ ENABLED_ENVS = %w(development test).freeze
11
+
12
+ class << self
13
+ # @!method setup
14
+ # @see WebValve::Manager#setup
15
+ # @!method register
16
+ # @see WebValve::Manager#register
17
+ # @!method whitelist_url
18
+ # @see WebValve::Manager#whitelist_url
19
+ # @!method reset
20
+ # @see WebValve::Manager#reset
21
+ delegate :setup, :register, :whitelist_url, :reset, to: :manager
22
+
23
+ def enabled?
24
+ Rails.env.in?(ENABLED_ENVS)
25
+ end
26
+
27
+ def config_paths
28
+ @config_paths ||= Set.new
29
+ end
30
+
31
+ private
32
+
33
+ def manager
34
+ WebValve::Manager.instance
35
+ end
36
+ end
37
+ end
38
+
39
+ require 'webvalve/instrumentation'
40
+ require 'webvalve/engine'
@@ -0,0 +1,26 @@
1
+ module WebValve
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace WebValve
4
+
5
+ if WebValve.enabled?
6
+ initializer "webvalve.set_autoload_paths", before: :set_autoload_paths do |app|
7
+ WebValve.config_paths << app.root
8
+
9
+ WebValve.config_paths.each do |root|
10
+ app.config.eager_load_paths << root.join('webvalve').to_s
11
+ end
12
+ end
13
+
14
+ initializer "webvalve.setup" do
15
+ WebValve.config_paths.each do |root|
16
+ path = root.join('config', 'webvalve.rb').to_s
17
+ load path if File.exist?(path)
18
+ end
19
+
20
+ config.after_initialize do
21
+ WebValve.setup
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/json'
3
+ require 'webvalve/instrumentation/middleware'
4
+
5
+ module WebValve
6
+ class FakeService < Sinatra::Base
7
+
8
+ set :dump_errors, false
9
+ set :show_exceptions, false
10
+ set :raise_errors, true
11
+
12
+ configure do
13
+ use Instrumentation::Middleware
14
+ end
15
+
16
+ private
17
+
18
+ def route_missing
19
+ raise "route not defined for #{request.request_method} #{uri} in #{self.class.name}."
20
+ end
21
+ end
22
+ end