diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb69b6..242aba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- Use `PATCH` not `POST` when enabling or disabling proxies when Toxiproxy supports `PATCH`. + ([#186](https://github.com/Shopify/toxiproxy-ruby/pull/186), @brendo) +- Set HTTP timeout of 5s when communicating with Toxiproxy server. + ([#85](https://github.com/Shopify/toxiproxy-ruby/pull/85), @casperisfine) + ## [2.0.2] - 2022-09-02 ### Fixed - Fix uninitialized instance variable warning. diff --git a/README.md b/README.md index 38f71af..057636e 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![Gem Version](https://badge.fury.io/rb/toxiproxy.svg)](https://badge.fury.io/rb/toxiproxy) [![Test](https://github.com/Shopify/toxiproxy-ruby/actions/workflows/test.yml/badge.svg)](https://github.com/Shopify/toxiproxy-ruby/actions/workflows/test.yml) -`toxiproxy-ruby` `>= 1.x` is compatible with the Toxiproxy `2.x` series. -`toxiproxy-ruby` `0.x` is compatible with the Toxiproxy `1.x` series. +- `toxiproxy-ruby` `>= 1.x` is compatible with the Toxiproxy `2.x` series. +- `toxiproxy-ruby` `0.x` is compatible with the Toxiproxy `1.x` series. [Toxiproxy](https://github.com/shopify/toxiproxy) is a proxy to simulate network and system conditions. The Ruby API aims to make it simple to write tests that diff --git a/bin/start-toxiproxy.sh b/bin/start-toxiproxy.sh index 27a5261..2a35237 100755 --- a/bin/start-toxiproxy.sh +++ b/bin/start-toxiproxy.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION='v2.4.0' +VERSION='v2.12.0' if [[ "$OSTYPE" == "linux"* ]]; then DOWNLOAD_TYPE="linux-amd64" diff --git a/lib/toxiproxy.rb b/lib/toxiproxy.rb index 11c0bbb..8121434 100644 --- a/lib/toxiproxy.rb +++ b/lib/toxiproxy.rb @@ -212,7 +212,11 @@ def down(&block) # Disables a Toxiproxy. This will drop all active connections and stop the proxy from listening. def disable - request = Net::HTTP::Post.new("/proxies/#{name}") + request = if server_supports_patch? + Net::HTTP::Patch.new("/proxies/#{name}") + else + Net::HTTP::Post.new("/proxies/#{name}") + end request["Content-Type"] = "application/json" hash = { enabled: false } @@ -225,7 +229,11 @@ def disable # Enables a Toxiproxy. This will cause the proxy to start listening again. def enable - request = Net::HTTP::Post.new("/proxies/#{name}") + request = if server_supports_patch? + Net::HTTP::Patch.new("/proxies/#{name}") + else + Net::HTTP::Post.new("/proxies/#{name}") + end request["Content-Type"] = "application/json" hash = { enabled: true } @@ -283,6 +291,35 @@ def toxics private + def version_string + return @version_string if @version_string + + version_response = self.class.version + return false if version_response == false + + @version_string = begin + JSON.parse(version_response)["version"] + rescue JSON::ParserError + false + end + end + + # Check if the toxiproxy server version supports PATCH for enable/disable + def server_supports_patch? + version_str = version_string + return false if version_str == false + + begin + # Use Gem::Version for proper version comparison + current_version = Gem::Version.new(version_str.sub(/^v/, "")) # Remove 'v' prefix if present + required_version = Gem::Version.new("2.6.0") + current_version >= required_version + rescue ArgumentError + # Invalid version format + false + end + end + def http_request(request) self.class.http_request(request) end diff --git a/test/toxiproxy_test.rb b/test/toxiproxy_test.rb index 2a11613..c4a8b0b 100644 --- a/test/toxiproxy_test.rb +++ b/test/toxiproxy_test.rb @@ -387,6 +387,144 @@ def test_version assert_instance_of(String, Toxiproxy.version) end + def test_server_supports_patch_with_version_2_6_0 + Toxiproxy.stub(:version, '{"version": "2.6.0"}') do + assert(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_server_supports_patch_with_version_2_7_0 + Toxiproxy.stub(:version, '{"version": "2.7.0"}') do + assert(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_server_supports_patch_with_version_3_0_0 + Toxiproxy.stub(:version, '{"version": "3.0.0"}') do + assert(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_does_not_support_patch_for_enable_disable_with_version_below_2_6_0 + Toxiproxy.stub(:version, '{"version": "2.5.0"}') do + refute(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_does_not_support_patch_for_enable_disable_when_not_running + Toxiproxy.stub(:running?, false) do + refute(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_does_not_support_patch_for_enable_disable_with_invalid_version + Toxiproxy.stub(:version, "invalid") do + refute(Toxiproxy.new(upstream: "localhost:3306", name: "test").send(:server_supports_patch?)) + end + end + + def test_disable_uses_patch_when_version_supports_it + proxy = Toxiproxy.new(upstream: "localhost:3306", name: "test_proxy_patch") + + # Mock version to return JSON with 2.6.0 (supports PATCH) + Toxiproxy.stub(:version, '{"version": "2.6.0"}') do + # Mock the http_request method to capture the request type + request_captured = nil + proxy.stub(:http_request, ->(req) { + request_captured = req + double = Object.new + double.define_singleton_method(:value) do + nil + end + double + }) do + proxy.disable + end + + # Verify PATCH was used + assert_instance_of(Net::HTTP::Patch, request_captured) + assert_equal("/proxies/test_proxy_patch", request_captured.path) + assert_equal({ enabled: false }.to_json, request_captured.body) + end + end + + def test_enable_uses_patch_when_version_supports_it + proxy = Toxiproxy.new(upstream: "localhost:3306", name: "test_proxy_patch_enable") + + # Mock version to return JSON with 2.6.0 (supports PATCH) + Toxiproxy.stub(:version, '{"version": "2.6.0"}') do + # Mock the http_request method to capture the request type + request_captured = nil + proxy.stub(:http_request, ->(req) { + request_captured = req + double = Object.new + double.define_singleton_method(:value) do + nil + end + double + }) do + proxy.enable + end + + # Verify PATCH was used + assert_instance_of(Net::HTTP::Patch, request_captured) + assert_equal("/proxies/test_proxy_patch_enable", request_captured.path) + assert_equal({ enabled: true }.to_json, request_captured.body) + end + end + + def test_disable_uses_post_when_version_does_not_support_patch + proxy = Toxiproxy.new(upstream: "localhost:3306", name: "test_proxy_post") + + # Mock version to return JSON with 2.5.0 (does not support PATCH) + + Toxiproxy.stub(:version, '{"version": "2.5.0"}') do + # Mock the http_request method to capture the request type + request_captured = nil + proxy.stub(:http_request, ->(req) { + request_captured = req + double = Object.new + double.define_singleton_method(:value) do + nil + end + double + }) do + proxy.disable + end + + # Verify POST was used + assert_instance_of(Net::HTTP::Post, request_captured) + assert_equal("/proxies/test_proxy_post", request_captured.path) + assert_equal({ enabled: false }.to_json, request_captured.body) + end + end + + def test_enable_uses_post_when_version_does_not_support_patch + proxy = Toxiproxy.new(upstream: "localhost:3306", name: "test_proxy_post_enable") + + # Mock version to return JSON with 2.5.0 (does not support PATCH) + + Toxiproxy.stub(:version, '{"version": "2.5.0"}') do + # Mock the http_request method to capture the request type + request_captured = nil + proxy.stub(:http_request, ->(req) { + request_captured = req + double = Object.new + double.define_singleton_method(:value) do + nil + end + double + }) do + proxy.enable + end + + # Verify POST was used + assert_instance_of(Net::HTTP::Post, request_captured) + assert_equal("/proxies/test_proxy_post_enable", request_captured.path) + assert_equal({ enabled: true }.to_json, request_captured.body) + end + end + def test_multiple_of_same_toxic_type with_tcpserver(receive: true) do |port| proxy = Toxiproxy.create(upstream: "localhost:#{port}", name: "test_proxy")