diff --git a/bazel/envoy_build_system.bzl b/bazel/envoy_build_system.bzl index e47592fcb006d..61fdfbc557d94 100644 --- a/bazel/envoy_build_system.bzl +++ b/bazel/envoy_build_system.bzl @@ -443,7 +443,8 @@ def envoy_cc_test( args = [], shard_count = None, coverage = True, - local = False): + local = False, + size = "medium"): test_lib_tags = [] if coverage: test_lib_tags.append("coverage_test_lib") @@ -472,6 +473,7 @@ def envoy_cc_test( tags = tags + ["coverage_test"], local = local, shard_count = shard_count, + size = size, ) # Envoy C++ related test infrastructure (that want gtest, gmock, but may be diff --git a/test/extensions/filters/network/redis_proxy/BUILD b/test/extensions/filters/network/redis_proxy/BUILD index bcc221a83f22d..2ae0acae9a7b0 100644 --- a/test/extensions/filters/network/redis_proxy/BUILD +++ b/test/extensions/filters/network/redis_proxy/BUILD @@ -105,3 +105,14 @@ envoy_extension_cc_test_binary( "//test/test_common:simulated_time_system_lib", ], ) + +envoy_extension_cc_test( + name = "redis_proxy_integration_test", + size = "small", + srcs = ["redis_proxy_integration_test.cc"], + extension_name = "envoy.filters.network.redis_proxy", + deps = [ + "//source/extensions/filters/network/redis_proxy:config", + "//test/integration:integration_lib", + ], +) diff --git a/test/extensions/filters/network/redis_proxy/redis_proxy_integration_test.cc b/test/extensions/filters/network/redis_proxy/redis_proxy_integration_test.cc new file mode 100644 index 0000000000000..53b62dbb2da43 --- /dev/null +++ b/test/extensions/filters/network/redis_proxy/redis_proxy_integration_test.cc @@ -0,0 +1,137 @@ +#include +#include + +#include "extensions/filters/network/redis_proxy/command_splitter_impl.h" + +#include "test/integration/integration.h" + +#include "gtest/gtest.h" + +namespace RedisCmdSplitter = Envoy::Extensions::NetworkFilters::RedisProxy::CommandSplitter; + +namespace Envoy { +namespace { + +const std::string REDIS_PROXY_CONFIG = R"EOF( +admin: + access_log_path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 0 +static_resources: + clusters: + name: cluster_0 + hosts: + socket_address: + address: 127.0.0.1 + port_value: 0 + listeners: + name: listener_0 + address: + socket_address: + address: 127.0.0.1 + port_value: 0 + filter_chains: + filters: + name: envoy.redis_proxy + config: + stat_prefix: redis_stats + cluster: cluster_0 + settings: + op_timeout: 5s +)EOF"; + +std::string makeBulkStringArray(std::vector&& command_strings) { + std::stringstream result; + + result << "*" << command_strings.size() << "\r\n"; + for (uint64_t i = 0; i < command_strings.size(); i++) { + result << "$" << command_strings[i].size() << "\r\n"; + result << command_strings[i] << "\r\n"; + } + + return result.str(); +} + +class RedisProxyIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + RedisProxyIntegrationTest() : BaseIntegrationTest(GetParam(), REDIS_PROXY_CONFIG) {} + + ~RedisProxyIntegrationTest() override { + test_server_.reset(); + fake_upstreams_.clear(); + } + + void initialize() override; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, RedisProxyIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +void RedisProxyIntegrationTest::initialize() { + config_helper_.renameListener("redis_proxy"); + BaseIntegrationTest::initialize(); +} + +// This test sends a simple "get foo" command from a fake +// downstream client through the proxy to a fake upstream +// Redis server. The fake server sends a valid response +// back to the client. The request and response should +// make it through the envoy proxy server code unchanged. + +TEST_P(RedisProxyIntegrationTest, SimpleRequestAndResponse) { + initialize(); + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("redis_proxy")); + + std::string client_to_proxy = makeBulkStringArray({"get", "foo"}); + std::string proxy_to_server; + + EXPECT_TRUE(client_to_proxy.size() > 0); + EXPECT_TRUE(client_to_proxy.find("get") != std::string::npos); + EXPECT_TRUE(client_to_proxy.find("foo") != std::string::npos); + tcp_client->write(client_to_proxy); + + FakeRawConnectionPtr fake_upstream_connection; + EXPECT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + EXPECT_TRUE(fake_upstream_connection->waitForData(client_to_proxy.size(), &proxy_to_server)); + EXPECT_EQ(client_to_proxy, proxy_to_server); + + std::string server_to_proxy = "$3\r\nbar\r\n"; // bulkstring reply of "bar" + + EXPECT_TRUE(fake_upstream_connection->write(server_to_proxy)); + tcp_client->waitForData(server_to_proxy); + EXPECT_EQ(server_to_proxy, tcp_client->data()); + + tcp_client->close(); + EXPECT_TRUE(fake_upstream_connection->close()); +} + +// This test sends an invalid Redis command from a fake +// downstream client to the envoy proxy. Envoy will respond +// with an invalid request error. + +TEST_P(RedisProxyIntegrationTest, InvalidRequest) { + initialize(); + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("redis_proxy")); + + std::string client_to_proxy = makeBulkStringArray({"foo"}); + + EXPECT_TRUE(client_to_proxy.size() > 0); + EXPECT_TRUE(client_to_proxy.find("foo") != std::string::npos); + tcp_client->write(client_to_proxy); + + std::stringstream error_response; + error_response << "-" << RedisCmdSplitter::Response::get().InvalidRequest << "\r\n"; + std::string proxy_to_client = error_response.str(); + + tcp_client->waitForData(proxy_to_client); + EXPECT_EQ(proxy_to_client, tcp_client->data()); + + tcp_client->close(); +} + +} // namespace +} // namespace Envoy