client_ut.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #include <library/cpp/testing/unittest/registar.h>
  2. #include "client.h"
  3. #include "http_code_extractor.h"
  4. #include <library/cpp/messagebus/rain_check/test/ut/test.h>
  5. #include <library/cpp/messagebus/test/helper/fixed_port.h>
  6. #include <library/cpp/http/io/stream.h>
  7. #include <library/cpp/neh/rpc.h>
  8. #include <util/generic/cast.h>
  9. #include <util/generic/ptr.h>
  10. #include <util/generic/strbuf.h>
  11. #include <util/generic/string.h>
  12. #include <util/generic/vector.h>
  13. #include <util/network/ip.h>
  14. #include <util/stream/str.h>
  15. #include <util/string/printf.h>
  16. #include <util/system/defaults.h>
  17. #include <util/system/yassert.h>
  18. #include <cstdlib>
  19. #include <utility>
  20. using namespace NRainCheck;
  21. using namespace NBus::NTest;
  22. namespace {
  23. class THttpClientEnv: public TTestEnvTemplate<THttpClientEnv> {
  24. public:
  25. THttpClientService HttpClientService;
  26. };
  27. const TString TEST_SERVICE = "test-service";
  28. const TString TEST_GET_PARAMS = "p=GET";
  29. const TString TEST_POST_PARAMS = "p=POST";
  30. const TString TEST_POST_HEADERS = "Content-Type: application/json\r\n";
  31. const TString TEST_GET_RECV = "GET was ok.";
  32. const TString TEST_POST_RECV = "POST was ok.";
  33. TString BuildServiceLocation(ui32 port) {
  34. return Sprintf("http://*:%" PRIu32 "/%s", port, TEST_SERVICE.data());
  35. }
  36. TString BuildPostServiceLocation(ui32 port) {
  37. return Sprintf("post://*:%" PRIu32 "/%s", port + 1, TEST_SERVICE.data());
  38. }
  39. TString BuildGetTestRequest(ui32 port) {
  40. return BuildServiceLocation(port) + "?" + TEST_GET_PARAMS;
  41. }
  42. class TSimpleServer {
  43. public:
  44. inline void ServeRequest(const NNeh::IRequestRef& req) {
  45. NNeh::TData response;
  46. if (req->Data() == TEST_GET_PARAMS) {
  47. response.assign(TEST_GET_RECV.begin(), TEST_GET_RECV.end());
  48. } else {
  49. response.assign(TEST_POST_RECV.begin(), TEST_POST_RECV.end());
  50. }
  51. req->SendReply(response);
  52. }
  53. };
  54. NNeh::IServicesRef RunServer(ui32 port, TSimpleServer& server) {
  55. NNeh::IServicesRef runner = NNeh::CreateLoop();
  56. runner->Add(BuildServiceLocation(port), server);
  57. runner->Add(BuildPostServiceLocation(port), server);
  58. try {
  59. const int THR_POOL_SIZE = 2;
  60. runner->ForkLoop(THR_POOL_SIZE);
  61. } catch (...) {
  62. Y_ABORT("Can't run server: %s", CurrentExceptionMessage().data());
  63. }
  64. return runner;
  65. }
  66. enum ERequestType {
  67. RT_HTTP_GET = 0,
  68. RT_HTTP_POST = 1
  69. };
  70. using TTaskParam = std::pair<TIpPort, ERequestType>;
  71. class THttpClientTask: public ISimpleTask {
  72. public:
  73. THttpClientTask(THttpClientEnv* env, TTaskParam param)
  74. : Env(env)
  75. , ServerPort(param.first)
  76. , ReqType(param.second)
  77. {
  78. }
  79. TContinueFunc Start() override {
  80. switch (ReqType) {
  81. case RT_HTTP_GET: {
  82. TString getRequest = BuildGetTestRequest(ServerPort);
  83. for (size_t i = 0; i < 3; ++i) {
  84. Requests.push_back(new THttpFuture());
  85. Env->HttpClientService.Send(getRequest, Requests[i].Get());
  86. }
  87. break;
  88. }
  89. case RT_HTTP_POST: {
  90. TString servicePath = BuildPostServiceLocation(ServerPort);
  91. TStringInput headersText(TEST_POST_HEADERS);
  92. THttpHeaders headers(&headersText);
  93. for (size_t i = 0; i < 3; ++i) {
  94. Requests.push_back(new THttpFuture());
  95. Env->HttpClientService.SendPost(servicePath, TEST_POST_PARAMS, headers, Requests[i].Get());
  96. }
  97. break;
  98. }
  99. }
  100. return &THttpClientTask::GotReplies;
  101. }
  102. TContinueFunc GotReplies() {
  103. const TString& TEST_OK_RECV = (ReqType == RT_HTTP_GET) ? TEST_GET_RECV : TEST_POST_RECV;
  104. for (size_t i = 0; i < Requests.size(); ++i) {
  105. UNIT_ASSERT_EQUAL(Requests[i]->GetHttpCode(), 200);
  106. UNIT_ASSERT_EQUAL(Requests[i]->GetResponseBody(), TEST_OK_RECV);
  107. }
  108. Env->TestSync.CheckAndIncrement(0);
  109. return nullptr;
  110. }
  111. THttpClientEnv* const Env;
  112. const TIpPort ServerPort;
  113. const ERequestType ReqType;
  114. TVector<TSimpleSharedPtr<THttpFuture>> Requests;
  115. };
  116. } // anonymous namespace
  117. Y_UNIT_TEST_SUITE(RainCheckHttpClient) {
  118. static const TIpPort SERVER_PORT = 4000;
  119. Y_UNIT_TEST(Simple) {
  120. // TODO: randomize port
  121. if (!IsFixedPortTestAllowed()) {
  122. return;
  123. }
  124. TSimpleServer server;
  125. NNeh::IServicesRef runner = RunServer(SERVER_PORT, server);
  126. THttpClientEnv env;
  127. TIntrusivePtr<TSimpleTaskRunner> task = env.SpawnTask<THttpClientTask>(TTaskParam(SERVER_PORT, RT_HTTP_GET));
  128. env.TestSync.WaitForAndIncrement(1);
  129. }
  130. Y_UNIT_TEST(SimplePost) {
  131. // TODO: randomize port
  132. if (!IsFixedPortTestAllowed()) {
  133. return;
  134. }
  135. TSimpleServer server;
  136. NNeh::IServicesRef runner = RunServer(SERVER_PORT, server);
  137. THttpClientEnv env;
  138. TIntrusivePtr<TSimpleTaskRunner> task = env.SpawnTask<THttpClientTask>(TTaskParam(SERVER_PORT, RT_HTTP_POST));
  139. env.TestSync.WaitForAndIncrement(1);
  140. }
  141. Y_UNIT_TEST(HttpCodeExtraction) {
  142. // Find "request failed(" string, then copy len("HTTP/1.X NNN") chars and try to convert NNN to HTTP code.
  143. #define CHECK_VALID_LINE(line, code) \
  144. UNIT_ASSERT_NO_EXCEPTION(TryGetHttpCodeFromErrorDescription(line)); \
  145. UNIT_ASSERT(!!TryGetHttpCodeFromErrorDescription(line)); \
  146. UNIT_ASSERT_EQUAL(*TryGetHttpCodeFromErrorDescription(line), code)
  147. CHECK_VALID_LINE(TStringBuf("library/cpp/neh/http.cpp:<LINE>: request failed(HTTP/1.0 200 Some random message"), 200);
  148. CHECK_VALID_LINE(TStringBuf("library/cpp/neh/http.cpp:<LINE>: request failed(HTTP/1.0 404 Some random message"), 404);
  149. CHECK_VALID_LINE(TStringBuf("request failed(HTTP/1.0 100 Some random message"), 100);
  150. CHECK_VALID_LINE(TStringBuf("request failed(HTTP/1.0 105)"), 105);
  151. CHECK_VALID_LINE(TStringBuf("request failed(HTTP/1.1 2004 Some random message"), 200);
  152. #undef CHECK_VALID_LINE
  153. #define CHECK_INVALID_LINE(line) \
  154. UNIT_ASSERT_NO_EXCEPTION(TryGetHttpCodeFromErrorDescription(line)); \
  155. UNIT_ASSERT(!TryGetHttpCodeFromErrorDescription(line))
  156. CHECK_INVALID_LINE(TStringBuf("library/cpp/neh/http.cpp:<LINE>: request failed(HTTP/1.1 1 Some random message"));
  157. CHECK_INVALID_LINE(TStringBuf("request failed(HTTP/1.0 asdf Some random message"));
  158. CHECK_INVALID_LINE(TStringBuf("HTTP/1.0 200 Some random message"));
  159. CHECK_INVALID_LINE(TStringBuf("request failed(HTTP/1.0 2x00 Some random message"));
  160. CHECK_INVALID_LINE(TStringBuf("HTTP/1.0 200 Some random message"));
  161. CHECK_INVALID_LINE(TStringBuf("HTTP/1.0 200"));
  162. CHECK_INVALID_LINE(TStringBuf("request failed(HTTP/1.1 3334 Some random message"));
  163. #undef CHECK_INVALID_LINE
  164. }
  165. }