1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/*
* (C) Copyright 1996- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#include <algorithm>
#include <array>
#include <cstdlib>
#include <string>
#include <thread>
#include "eckit/io/EasyCURL.h"
#include "eckit/value/Value.h"
#include "eckit/testing/Test.h"
using namespace std;
using namespace eckit;
using namespace eckit::testing;
/*
*
* The following tests take advantage of the Python-based Mock REST API -- see mock/MockREST.py.
* The Mock API is launched during test setup and serves as target for EasyCURL GET/POST/PUT/DELETE calls.
*
*/
namespace {
const std::string BASE_URL = "http://127.0.0.1:49111";
class MockREST {
public:
MockREST() {
// Launch REST API in a (detached) thread
std::thread api(MockREST::launch_rest_api);
api.detach();
// ... give the service some lead time
std::this_thread::sleep_for(std::chrono::seconds(2));
// ... and way for the service initialization
MockREST::wait_for_running_api();
}
~MockREST() {
// Gracefully, request REST API to shut down
MockREST::shutdown_running_api();
}
private:
static void launch_rest_api() { std::system("MockREST.launcher.sh"); }
static void wait_for_running_api() {
auto curl = EasyCURL();
curl.verbose(true);
for (size_t attempt = 0; attempt < 30; ++attempt) {
try {
auto response = curl.GET(BASE_URL + "/ping");
if (response.code() == 200) {
// Got a ping! We're done...
return;
}
}
catch (const SeriousBug& error) {
// Unable to connect to server! Must retry...
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Enough! Let's give up...
throw std::runtime_error("Unable to start Mock REST API");
}
static void shutdown_running_api() {
auto curl = EasyCURL();
try {
auto response = curl.GET(BASE_URL + "/shutdown");
}
catch (const SeriousBug& error) {
// Nothing to do...
}
}
};
namespace impl {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N> make_array(T (&a)[N], std::index_sequence<I...>) {
return {{std::move(a[I])...}};
}
template <typename T, size_t N>
std::array<T, N> make_array(const T (&values)[N]) {
return make_array(values, std::make_index_sequence<N>{});
}
} // namespace impl
std::string make_random_token(size_t length) {
auto randomize = []() -> char {
static std::array selected = impl::make_array("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
return selected[rand() % (selected.size() - 1)];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randomize);
return str;
}
std::string make_blob(std::string_view key, std::string_view value) {
std::ostringstream oss;
oss << R"({"key": ")" << key << R"(", "value": ")" << value << R"("})";
return oss.str();
}
} // namespace
namespace eckit {
namespace test {
//----------------------------------------------------------------------------------------------------------------------
CASE("EasyCURL GET (Successful operation)") {
auto curl = EasyCURL();
auto response = curl.GET(BASE_URL + "/ping");
EXPECT(response.code() == 200);
EXPECT(response.body() == "Hi!");
}
CASE("EasyCURL GET (Not Found)") {
auto curl = EasyCURL();
auto response = curl.GET(BASE_URL + "/ping__NOT-FOUND");
EXPECT(response.code() == 404);
EXPECT(response.body().find("Not Found") != std::string::npos);
}
CASE("EasyCURL GET (REST API, Successful operation)") {
auto curl = EasyCURL();
auto response = curl.GET(BASE_URL + "/blobs");
EXPECT(response.code() == 200);
EXPECT(response.body() == "{}\n");
}
CASE("EasyCURL GET (REST API, Insufficient permissions)") {
auto curl = EasyCURL();
auto response = curl.GET(BASE_URL + "/insufficient-permissions");
EXPECT(response.code() == 403);
EXPECT(response.body() == "Forbidden");
}
CASE("EasyCURL PUT (REST API, Create+Update+Delete new entity)") {
EasyCURLHeaders headers;
headers["content-type"] = "application/json";
auto curl = EasyCURL();
curl.headers(headers);
std::string key = make_random_token(8);
std::string original_value = make_random_token(64);
{
auto blob = make_blob(key, original_value);
auto response = curl.POST(BASE_URL + "/blob", blob);
EXPECT(response.code() == 201);
EXPECT(response.body().find(original_value) != std::string::npos);
}
{
auto response = curl.GET(BASE_URL + "/blob/" + key + "/content");
EXPECT(response.code() == 200);
EXPECT(response.body() == original_value);
}
std::string updated_value = make_random_token(64);
{
auto blob = make_blob(key, updated_value);
auto response = curl.PUT(BASE_URL + "/blob", blob);
EXPECT(response.code() == 200);
EXPECT(response.body().find(updated_value) != std::string::npos);
}
{
auto response = curl.GET(BASE_URL + "/blob/" + key + "/content");
EXPECT(response.code() == 200);
EXPECT(response.body() == updated_value);
}
{
auto response = curl.DELETE(BASE_URL + "/blob/" + key);
EXPECT(response.code() == 204);
EXPECT(response.body().empty());
}
{
auto response = curl.GET(BASE_URL + "/blob/" + key + "/content");
EXPECT(response.code() == 404);
EXPECT(response.body().find("Not Found") != std::string::npos);
}
}
//----------------------------------------------------------------------------------------------------------------------
} // namespace test
} // namespace eckit
int main(int argc, char** argv) {
MockREST mock_rest_api_running_in_background;
return run_tests(argc, argv);
}
|