diff --git a/.gitignore b/.gitignore index a23bc39..d11cdef 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,8 @@ erl_crash.dump *.ez /tmp /_build -/doc \ No newline at end of file +/doc +.dockerignore +.devcontainer +Dockerfile +docker-compose.yml \ No newline at end of file diff --git a/fixture/vcr_cassettes/ignore_urls_on.json b/fixture/vcr_cassettes/ignore_urls_on.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/fixture/vcr_cassettes/ignore_urls_on.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/fixture/vcr_cassettes/ignore_urls_unset.json b/fixture/vcr_cassettes/ignore_urls_unset.json new file mode 100644 index 0000000..e0e6fe2 --- /dev/null +++ b/fixture/vcr_cassettes/ignore_urls_unset.json @@ -0,0 +1,44 @@ +[ + { + "request": { + "body": "", + "headers": [], + "method": "get", + "options": [], + "request_body": "", + "url": "http://localhost:34006/server" + }, + "response": { + "binary": false, + "body": "test_response_before", + "headers": { + "server": "Cowboy", + "date": "Sun, 08 Apr 2018 12:50:46 GMT", + "content-length": "20" + }, + "status_code": 200, + "type": "ok" + } + }, + { + "request": { + "body": "", + "headers": [], + "method": "get", + "options": [], + "request_body": "", + "url": "http://127.0.0.1:34006/server" + }, + "response": { + "binary": false, + "body": "test_response_before", + "headers": { + "server": "Cowboy", + "date": "Sun, 08 Apr 2018 12:50:46 GMT", + "content-length": "20" + }, + "status_code": 200, + "type": "ok" + } + } +] \ No newline at end of file diff --git a/fixture/vcr_cassettes/ignore_urls_with_headers.json b/fixture/vcr_cassettes/ignore_urls_with_headers.json new file mode 100644 index 0000000..4b1ea71 --- /dev/null +++ b/fixture/vcr_cassettes/ignore_urls_with_headers.json @@ -0,0 +1,25 @@ +[ + { + "request": { + "body": "", + "headers": { + "User-Agent": "ExVCR" + }, + "method": "get", + "options": [], + "request_body": "", + "url": "http://127.0.0.1:34006/server" + }, + "response": { + "binary": false, + "body": "test_response_before", + "headers": { + "server": "Cowboy", + "date": "Tue, 20 Apr 2021 13:54:51 GMT", + "content-length": "20" + }, + "status_code": 200, + "type": "ok" + } + } +] \ No newline at end of file diff --git a/lib/exvcr/config.ex b/lib/exvcr/config.ex index 2be5246..c960aee 100644 --- a/lib/exvcr/config.ex +++ b/lib/exvcr/config.ex @@ -85,6 +85,13 @@ defmodule ExVCR.Config do Setting.set(:ignore_localhost, value) end + @doc """ + Skip recording cassettes for urls requests when set + """ + def ignore_urls(value) do + Setting.set(:ignore_urls, value) + end + @doc """ Throw error if there is no matching cassette for an HTTP request """ diff --git a/lib/exvcr/config_loader.ex b/lib/exvcr/config_loader.ex index d3b55f6..13ef033 100644 --- a/lib/exvcr/config_loader.ex +++ b/lib/exvcr/config_loader.ex @@ -57,6 +57,10 @@ defmodule ExVCR.ConfigLoader do Config.ignore_localhost(env[:ignore_localhost]) end + if env[:ignore_urls] != nil do + Config.ignore_urls(env[:ignore_urls]) + end + if env[:strict_mode] != nil do Config.strict_mode(env[:strict_mode]) end diff --git a/lib/exvcr/handler.ex b/lib/exvcr/handler.ex index 7681a7a..d53245e 100644 --- a/lib/exvcr/handler.ex +++ b/lib/exvcr/handler.ex @@ -3,9 +3,8 @@ defmodule ExVCR.Handler do Provide operations for request/response. """ - alias ExVCR.Recorder + alias ExVCR.{Recorder, Setting, Util} alias ExVCR.Actor.Options - alias ExVCR.Util @doc """ Get response from either server or cache. @@ -175,7 +174,14 @@ defmodule ExVCR.Handler do end defp ignore_request?(request, recorder) do - ignore_localhost = ExVCR.Recorder.options(recorder)[:ignore_localhost] || ExVCR.Setting.get(:ignore_localhost) + ignore_localhost?(request, recorder) || + ignore_urls?(request, recorder) + end + + defp ignore_localhost?(request, recorder) do + ignore_localhost = + Keyword.get(Recorder.options(recorder), :ignore_localhost, Setting.get(:ignore_localhost)) + if ignore_localhost do adapter = ExVCR.Recorder.options(recorder)[:adapter] params = adapter.generate_keys_for_request(request) @@ -187,8 +193,27 @@ defmodule ExVCR.Handler do end end + defp ignore_urls?(request, recorder) do + ignore_urls = ExVCR.Recorder.options(recorder)[:ignore_urls] || ExVCR.Setting.get(:ignore_urls) + + if ignore_urls do + adapter = ExVCR.Recorder.options(recorder)[:adapter] + params = adapter.generate_keys_for_request(request) + + url = to_string(params[:url]) + + Enum.any?(ignore_urls, fn r_url -> + Regex.match?(r_url, url) + end) + else + false + end + end + defp ignore_server_fetch!(request, recorder) do - strict_mode = ExVCR.Recorder.options(recorder)[:strict_mode] || ExVCR.Setting.get(:strict_mode) + strict_mode = + Keyword.get(Recorder.options(recorder), :strict_mode, Setting.get(:strict_mode)) + if strict_mode do message = """ A matching cassette was not found for this request. diff --git a/test/ignore_localhost_test.exs b/test/ignore_localhost_test.exs index 27b5130..6934fac 100644 --- a/test/ignore_localhost_test.exs +++ b/test/ignore_localhost_test.exs @@ -46,4 +46,20 @@ defmodule ExVCR.IgnoreLocalhostTest do HttpServer.stop(@port) end end + + test "it records localhost requests when overrides the config setting" do + ExVCR.Setting.set(:ignore_localhost, true) + + use_cassette "ignore_localhost_unset", ignore_localhost: false do + HttpServer.start(path: "/server", port: @port, response: "test_response_before") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + # this method call should be mocked + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + end + + ExVCR.Setting.set(:strict_mode, false) + end end diff --git a/test/ignore_urls_test.exs b/test/ignore_urls_test.exs new file mode 100644 index 0000000..b914e90 --- /dev/null +++ b/test/ignore_urls_test.exs @@ -0,0 +1,63 @@ +defmodule ExVCR.IgnoreUrlsTest do + use ExVCR.Mock + use ExUnit.Case, async: false + + @port 34006 + @url "http://localhost:#{@port}/server" + @ignore_urls [ + ~r/http:\/\/localhost.*/, + ~r/http:\/\/127\.0\.0\.1.*/ + ] + + setup_all do + HTTPotion.start + :ok + end + + test "it does not record url requests when the config has been set" do + use_cassette "ignore_urls_on", ignore_urls: @ignore_urls do + HttpServer.start(path: "/server", port: @port, response: "test_response_before") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + # this method call should NOT be mocked + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_after/ + HttpServer.stop(@port) + # this method call should NOT be mocked + non_localhost_url = "http://127.0.0.1:#{@port}/server" + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(non_localhost_url, []).body =~ ~r/test_response_after/ + HttpServer.stop(@port) + end + end + + test "it records urls requests when the config has not been set" do + use_cassette "ignore_urls_unset" do + HttpServer.start(path: "/server", port: @port, response: "test_response_before") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + # this method call should be mocked + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(@url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + # this method call should NOT be mocked + non_localhost_url = "http://127.0.0.1:#{@port}/server" + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(non_localhost_url, []).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + end + end + + test "ignore_urls option works with request headers" do + use_cassette "ignore_urls_with_headers", ignore_urls: @ignore_urls do + HttpServer.start(path: "/server", port: @port, response: "test_response_after") + assert HTTPotion.get(@url, headers: ["User-Agent": "ExVCR"]).body =~ ~r/test_response_after/ + HttpServer.stop(@port) + # this method call should be mocked + non_localhost_url = "http://127.0.0.1:#{@port}/server" + HttpServer.start(path: "/server", port: @port, response: "test_response_before") + assert HTTPotion.get(non_localhost_url, headers: ["User-Agent": "ExVCR"]).body =~ ~r/test_response_before/ + HttpServer.stop(@port) + end + end +end diff --git a/test/strict_mode_test.exs b/test/strict_mode_test.exs index 1510efd..a07e4b4 100644 --- a/test/strict_mode_test.exs +++ b/test/strict_mode_test.exs @@ -52,4 +52,22 @@ defmodule ExVCR.StrictModeTest do assert HTTPotion.get(@url, []).body =~ ~r/test_response/ end end + + test "it does not uses a cassette when override the defaut config" do + ExVCR.Setting.set(:strict_mode, true) + + use_cassette "strict_mode_cassette", strict_mode: false do + assert HTTPotion.get(@url, []).body =~ ~r/test_response/ + end + + use_cassette "strict_mode_cassette" do + assert HTTPotion.get(@url, []).body =~ ~r/test_response/ + end + + use_cassette "strict_mode_cassette", strict_mode: true do + assert HTTPotion.get(@url, []).body =~ ~r/test_response/ + end + + ExVCR.Setting.set(:strict_mode, false) + end end