rublon-ssh/PAM/ssh/tests/http_mock.hpp
rublon-bwi 51b14c57d2
Bwi/memory management (#2)
Improve memory management
2023-09-21 16:52:20 +02:00

238 lines
6.9 KiB
C++

#pragma once
#include <gmock/gmock.h>
#include <set>
#include <string>
#include <rublon/configuration.hpp>
#include <rublon/curl.hpp>
#include "core_response_generator.hpp"
#include "rublon/sign.hpp"
namespace {
rublon::Configuration conf{rublon::Configuration::Parameters{//
"320BAB778C4D4262B54CD243CDEFFAFD",
"39e8d771d83a2ed3cc728811911c25",
"https://staging-core.rublon.net",
1,
true,
true,
false,
false}};
rublon::Configuration confBypass{rublon::Configuration::Parameters{//
"320BAB778C4D4262B54CD243CDEFFAFD",
"39e8d771d83a2ed3cc728811911c25",
"https://staging-core.rublon.net",
1,
true,
true,
false,
true}};
inline std::string randomTID() {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string tmp_s;
const auto len = 32;
tmp_s.resize(len);
std::for_each(tmp_s.begin(), tmp_s.end(), [](auto & chr) { chr = alphanum[rand() % (sizeof(alphanum) - 1)]; });
return tmp_s;
}
std::string join_methods(std::set< std::string > _methods) {
std::string ret;
for(const auto & m : _methods)
ret += "\"" + m + "\",";
ret.pop_back();
return ret;
}
} // namespace
struct ResponseMockOptions {
bool generateBrokenSigneture{false};
bool generateBrokenBody{false};
bool generateBrokenCode{false};
std::string coreException{};
std::set< std::string > methods{};
rublon::Error error;
};
class InitCoreResponse {
static constexpr const char * result_ok_template =
R"json({"status":"OK","result":{"tid":"%s","status":"%s","companyName":"%s","applicationName":"%s","methods":[%s]}})json";
static constexpr const char * result_broken_template = //
R"json({"some":"random","json":"notrublon"})json";
public:
static std::string generate(const ResponseMockOptions & options) {
if(options.generateBrokenBody) {
return result_broken_template;
}
std::array< char, 2048 > _buf;
auto tid = randomTID();
auto status = "pending";
std::string companyName{"rublon"};
std::string applicationName{"test_app"};
std::set< std::string > methods{"email", "totp", "qrcode", "push"};
if(options.methods.size())
methods = options.methods;
io::sprintf(_buf.data(), result_ok_template, tid, status, companyName, applicationName, join_methods(methods));
return _buf.data();
}
};
class MethodSelectCoreResponse {
static constexpr const char * result_ok_template =
R"json({"status":"OK","result":{"tid":"%s","status":"%s","companyName":"%s","applicationName":"%s","methods":[%s]}})json";
static constexpr const char * result_broken_template = //
R"json({"some":"random","json":"notrublon"})json";
public:
static std::string generate(const ResponseMockOptions & options) {
if(options.generateBrokenBody) {
return result_broken_template;
}
std::array< char, 2048 > _buf;
auto tid = randomTID();
auto status = "pending";
std::string companyName{"rublon"};
std::string applicationName{"test_app"};
std::set< std::string > methods{"email", "totp", "qrcode", "push"};
io::sprintf(_buf.data(), result_ok_template, tid, status, companyName, applicationName, join_methods(methods));
return _buf.data();
}
};
class CodeVerificationResponse {
static constexpr const char * wrongPasscode =
R"json({"status":"OK","result":{"error":"Hmm, that's not the right code. Try again."}})json";
static constexpr const char * goodPasscode = R"json({"status":"OK","result":true})json";
public:
static std::string generate(const ResponseMockOptions & options) {
return options.generateBrokenCode ? wrongPasscode : goodPasscode;
}
};
template < typename Generator >
class ResponseBase {
public:
ResponseBase & initMessage() {
_coreGenerator = InitCoreResponse{};
return *this;
}
ResponseBase & selectMethodMessage() {
_coreGenerator = MethodSelectCoreResponse{};
return *this;
}
ResponseBase & codeConfirmationMessage() {
_coreGenerator = CodeVerificationResponse{};
return *this;
}
ResponseBase & withBrokenBody() {
options.generateBrokenBody = true;
return *this;
}
ResponseBase & withCoreException() {
options.coreException = "some exception";
return *this;
}
ResponseBase & withBrokenSignature() {
options.generateBrokenSigneture = true;
return *this;
}
ResponseBase & withWrongCodeResponse() {
options.generateBrokenCode = true;
return *this;
}
ResponseBase & withTimeoutError() {
options.error = rublon::Error{rublon::HttpError{}};
return *this;
}
ResponseBase & withServiceUnavailableError() {
options.error = rublon::Error{rublon::HttpError{rublon::HttpError::Error, 405}};
return *this;
}
template < typename... T >
ResponseBase & withMethods(T... methods) {
(options.methods.insert(methods), ...);
return *this;
}
ResponseBase & withConnectionError() {
// options.error = rublon::Error{rublon::CoreHandlerError{rublon::CoreHandlerError::ConnectionError}};
return *this;
}
operator auto() {
return options.error.category() == rublon::Error::k_None ? static_cast< Generator * >(this)->generate() : tl::unexpected{error()};
}
rublon::Error error() {
return rublon::Error{options.error};
}
std::variant< InitCoreResponse, MethodSelectCoreResponse, CodeVerificationResponse > _coreGenerator;
ResponseMockOptions options;
};
class RawCoreResponse : public ResponseBase< RawCoreResponse > {
public:
tl::expected< rublon::Document, rublon::Error > generate() {
auto jsonString = std::visit([&](const auto generator) { return generator.generate(options); }, _coreGenerator);
rublon::Document doc;
doc.Parse(jsonString.c_str());
return doc;
}
};
class RawHttpResponse : public ResponseBase< RawHttpResponse > {
static auto signResponse(rublon::Response & res, ResponseMockOptions opts) {
const auto & sign =
opts.generateBrokenSigneture ? std::array< char, 65 >{} : rublon::signData(res.body, conf.parameters.secretKey.c_str());
res.headers["x-rublon-signature"] = sign.data();
}
public:
tl::expected< rublon::Response, rublon::Error > generate() {
rublon::Response response{std::pmr::get_default_resource()};
response.body = std::visit([&](const auto generator) { return generator.generate(options); }, _coreGenerator);
signResponse(response, options);
return response;
}
};
class HttpHandlerMock {
public:
template < typename... Args >
HttpHandlerMock(const Args &...) {}
MOCK_METHOD(( tl::expected< rublon::Response, rublon::Error > ), request, ( std::string_view, const rublon::Request & ), (const));
};