238 lines
6.9 KiB
C++
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));
|
|
};
|