From bcf405b05e78c5c83f74a2949b87eed3cdcb0165 Mon Sep 17 00:00:00 2001 From: smudge Date: Fri, 17 Apr 2020 19:39:46 -0400 Subject: [PATCH 1/5] Add test cases for when API_URL includes a path --- spec/webvalve/fake_service_spec.rb | 43 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/spec/webvalve/fake_service_spec.rb b/spec/webvalve/fake_service_spec.rb index 3fe1c0c..dc5edf6 100644 --- a/spec/webvalve/fake_service_spec.rb +++ b/spec/webvalve/fake_service_spec.rb @@ -26,21 +26,44 @@ def self.name WebValve.reset end - it 'raise a useful error when an unmapped route is requested' do - with_env 'DUMMY_API_URL' => 'http://dummy.dev' do - WebValve.register subject.name - WebValve.setup + context 'when the service is at a root path' do + it 'raise a useful error when an unmapped route is requested' do + with_env 'DUMMY_API_URL' => 'http://dummy.dev' do + WebValve.register subject.name + WebValve.setup - expect { Net::HTTP.get(URI('http://dummy.dev/foos')) }.to raise_error(RuntimeError, /route not defined for GET/) + expect { Net::HTTP.get(URI('http://dummy.dev/foos')) }.to raise_error(RuntimeError, /route not defined for GET/) + end + end + + it 'returns the result from the fake when a mapped route is requested' do + with_env 'DUMMY_API_URL' => 'http://dummy.dev' do + WebValve.register subject.name + WebValve.setup + + expect(Net::HTTP.get(URI('http://dummy.dev/widgets'))).to eq({ result: 'it works!' }.to_json) + end end end - it 'returns the result from the fake when a mapped route is requested' do - with_env 'DUMMY_API_URL' => 'http://dummy.dev' do - WebValve.register subject.name - WebValve.setup + context 'when the service lives at a non-root path' do + it 'raise a useful error when the route is requested at the root' do + with_env 'DUMMY_API_URL' => 'http://dummy.dev/gg' do + WebValve.register subject.name + WebValve.setup + + expect { Net::HTTP.get(URI('http://dummy.dev/widgets')) } + .to raise_error(WebMock::NetConnectNotAllowedError, /Real HTTP connections are disabled/) + end + end + + it 'returns the result from the fake when a mapped route is requested' do + with_env 'DUMMY_API_URL' => 'http://dummy.dev/gg' do + WebValve.register subject.name + WebValve.setup - expect(Net::HTTP.get(URI('http://dummy.dev/widgets'))).to eq({ result: 'it works!' }.to_json) + expect(Net::HTTP.get(URI('http://dummy.dev/gg/widgets'))).to eq({ result: 'it works!' }.to_json) + end end end end From 1d6b8b278f34585e147399bde3c4f8a9f721ce02 Mon Sep 17 00:00:00 2001 From: smudge Date: Fri, 17 Apr 2020 19:40:32 -0400 Subject: [PATCH 2/5] Allow FakeServiceWrapper to strip out prefix before passing through to fake service --- lib/webvalve/fake_service_wrapper.rb | 11 ++++++++--- lib/webvalve/manager.rb | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/webvalve/fake_service_wrapper.rb b/lib/webvalve/fake_service_wrapper.rb index bce277a..237ff43 100644 --- a/lib/webvalve/fake_service_wrapper.rb +++ b/lib/webvalve/fake_service_wrapper.rb @@ -1,18 +1,23 @@ module WebValve class FakeServiceWrapper # lazily resolve the app constant to leverage rails class reloading - def initialize(app_class_name) - @app_class_name = app_class_name + def initialize(service_config) + @service_config = service_config end def call(env) + env['PATH_INFO'] = env['PATH_INFO'].gsub(/^#{path_prefix}/, '') app.call(env) end private def app - @app_class_name.constantize + @service_config.service_class_name.constantize + end + + def path_prefix + @path_prefix ||= URI::parse(@service_config.service_url).path end end end diff --git a/lib/webvalve/manager.rb b/lib/webvalve/manager.rb index 5b9890c..14c92b4 100644 --- a/lib/webvalve/manager.rb +++ b/lib/webvalve/manager.rb @@ -132,7 +132,7 @@ def webmock_service(config) WebMock.stub_request( :any, url_to_regexp(config.service_url) - ).to_rack(FakeServiceWrapper.new(config.service_class_name)) + ).to_rack(FakeServiceWrapper.new(config)) end def allowlist_service(config) From 997adcabce699f4f39673774e5fd015a3f52fe6c Mon Sep 17 00:00:00 2001 From: smudge Date: Fri, 17 Apr 2020 19:56:31 -0400 Subject: [PATCH 3/5] Reset webmock stubs when resetting WebValve manager --- lib/webvalve/manager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/webvalve/manager.rb b/lib/webvalve/manager.rb index 14c92b4..5f9f581 100644 --- a/lib/webvalve/manager.rb +++ b/lib/webvalve/manager.rb @@ -70,6 +70,7 @@ def reset allowlisted_urls.clear fake_service_configs.clear stubbed_urls.clear + WebMock.reset! end # @api private From 951762951469f94c7b54bd39b95aa136a31041ba Mon Sep 17 00:00:00 2001 From: smudge Date: Mon, 20 Apr 2020 18:20:53 -0400 Subject: [PATCH 4/5] Use safer \A instead of ^ in regex Even though I swear URL paths cannot have newlines ;-) --- lib/webvalve/fake_service_wrapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/webvalve/fake_service_wrapper.rb b/lib/webvalve/fake_service_wrapper.rb index 237ff43..7bd244a 100644 --- a/lib/webvalve/fake_service_wrapper.rb +++ b/lib/webvalve/fake_service_wrapper.rb @@ -6,7 +6,7 @@ def initialize(service_config) end def call(env) - env['PATH_INFO'] = env['PATH_INFO'].gsub(/^#{path_prefix}/, '') + env['PATH_INFO'] = env['PATH_INFO'].gsub(/\A#{path_prefix}/, '') app.call(env) end From d33a8830446a33b2396a37ff2549a47bf4b3f7af Mon Sep 17 00:00:00 2001 From: smudge Date: Tue, 21 Apr 2020 18:05:29 -0400 Subject: [PATCH 5/5] Move 'path_prefix' to public API of FakeServiceConfig (and test it) --- lib/webvalve/fake_service_config.rb | 4 ++++ lib/webvalve/fake_service_wrapper.rb | 6 +----- spec/webvalve/fake_service_config_spec.rb | 24 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/webvalve/fake_service_config.rb b/lib/webvalve/fake_service_config.rb index cb2c813..09aa631 100644 --- a/lib/webvalve/fake_service_config.rb +++ b/lib/webvalve/fake_service_config.rb @@ -23,6 +23,10 @@ def service_url end end + def path_prefix + @path_prefix ||= URI::parse(service_url).path + end + private attr_reader :custom_service_url diff --git a/lib/webvalve/fake_service_wrapper.rb b/lib/webvalve/fake_service_wrapper.rb index 7bd244a..52b82c4 100644 --- a/lib/webvalve/fake_service_wrapper.rb +++ b/lib/webvalve/fake_service_wrapper.rb @@ -6,7 +6,7 @@ def initialize(service_config) end def call(env) - env['PATH_INFO'] = env['PATH_INFO'].gsub(/\A#{path_prefix}/, '') + env['PATH_INFO'] = env['PATH_INFO'].gsub(/\A#{@service_config.path_prefix}/, '') app.call(env) end @@ -15,9 +15,5 @@ def call(env) def app @service_config.service_class_name.constantize end - - def path_prefix - @path_prefix ||= URI::parse(@service_config.service_url).path - end end end diff --git a/spec/webvalve/fake_service_config_spec.rb b/spec/webvalve/fake_service_config_spec.rb index 450aa9c..d1acb71 100644 --- a/spec/webvalve/fake_service_config_spec.rb +++ b/spec/webvalve/fake_service_config_spec.rb @@ -88,4 +88,28 @@ def self.name end end end + + describe '.path_prefix' do + it 'raises if the url is not present' do + expect { subject.path_prefix }.to raise_error(/There is no URL defined for FakeDummy/) + end + + it 'returns root when there is no path in the service URL' do + with_env 'DUMMY_API_URL' => 'http://bananas.test/' do + expect(subject.path_prefix).to eq '/' + end + with_env 'DUMMY_API_URL' => 'https://some:auth@bananas.test//' do + expect(subject.path_prefix).to eq '/' # Parses funkier URL + end + end + + it 'returns the path when there is one in the service URL' do + with_env 'DUMMY_API_URL' => 'http://zombo.com/welcome' do + expect(subject.path_prefix).to eq '/welcome' + end + with_env 'DUMMY_API_URL' => 'http://zombo.com/welcome/' do + expect(subject.path_prefix).to eq '/welcome' # Ignores trailing '/' + end + end + end end