webvalve 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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