207 lines
5.7 KiB
C++
207 lines
5.7 KiB
C++
#define DROGON_TEST_MAIN
|
|
#include <drogon/HttpClient.h>
|
|
#include <drogon/drogon.h>
|
|
#include <drogon/drogon_test.h>
|
|
|
|
#include "common/GeoJson.h"
|
|
#include "common/Crypto.h"
|
|
#include "common/Http.h"
|
|
#include "common/Validation.h"
|
|
#include "repositories/Sql.h"
|
|
|
|
#include <future>
|
|
|
|
using namespace drogon;
|
|
|
|
namespace
|
|
{
|
|
std::pair<ReqResult, HttpResponsePtr> sendRequest(const HttpRequestPtr &request)
|
|
{
|
|
auto client = HttpClient::newHttpClient("http://127.0.0.1:15555",
|
|
app().getLoop());
|
|
auto promise =
|
|
std::make_shared<std::promise<std::pair<ReqResult, HttpResponsePtr>>>();
|
|
auto future = promise->get_future();
|
|
|
|
client->sendRequest(
|
|
request,
|
|
[promise](ReqResult result, const HttpResponsePtr &response) {
|
|
promise->set_value({result, response});
|
|
});
|
|
|
|
return future.get();
|
|
}
|
|
} // namespace
|
|
|
|
DROGON_TEST(HealthEndpoint)
|
|
{
|
|
auto request = HttpRequest::newHttpRequest();
|
|
request->setMethod(Get);
|
|
request->setPath("/api/v1/health");
|
|
|
|
auto [result, response] = sendRequest(request);
|
|
|
|
REQUIRE(result == ReqResult::Ok);
|
|
REQUIRE(response != nullptr);
|
|
CHECK(response->getStatusCode() == k200OK);
|
|
REQUIRE(response->getJsonObject() != nullptr);
|
|
CHECK((*response->getJsonObject())["status"].asString() == "ok");
|
|
CHECK((*response->getJsonObject())["service"].asString() ==
|
|
"visione-backend");
|
|
}
|
|
|
|
DROGON_TEST(ValidationRejectsMalformedIdentity)
|
|
{
|
|
CHECK(visione::validation::isValidEmail("[email protected]"));
|
|
CHECK(!visione::validation::isValidEmail("not-an-email"));
|
|
CHECK(visione::validation::isValidFiscalCode("RSSMRA80A01H501U"));
|
|
CHECK(visione::validation::isValidFiscalCode("VSSGNR94M08A662P"));
|
|
CHECK(!visione::validation::isValidFiscalCode("short"));
|
|
CHECK(!visione::validation::isValidFiscalCode("VSSGNR94M08A662A"));
|
|
}
|
|
|
|
DROGON_TEST(SqlScalarSerializationKeepsTypes)
|
|
{
|
|
const auto json = []() {
|
|
Json::Value value(Json::objectValue);
|
|
value["id"] = Json::UInt64(7);
|
|
value["banned"] = false;
|
|
value["points"] = Json::Int64(42);
|
|
value["phone"] = "380 6180216";
|
|
return value;
|
|
}();
|
|
|
|
CHECK(json["id"].isUInt64());
|
|
CHECK(json["banned"].isBool());
|
|
CHECK(json["points"].isInt64());
|
|
CHECK(json["phone"].isString());
|
|
}
|
|
|
|
DROGON_TEST(CryptoHmacIsDeterministic)
|
|
{
|
|
const auto digest = visione::crypto::hmacSha256Hex("secret", "{\"a\":1}");
|
|
CHECK(digest == "AA9E2E3575F5D7098B6CACCDB4486EA647EFDC3E2CA6A5CEE762FCAFD14E3F14");
|
|
CHECK(visione::crypto::constantTimeEquals(digest, digest));
|
|
CHECK(!visione::crypto::constantTimeEquals(digest, "different"));
|
|
}
|
|
|
|
DROGON_TEST(ParseJsonBodyRejectsInvalidPayload)
|
|
{
|
|
const auto parsed = visione::http::parseJsonBody("{\"ok\":true}");
|
|
CHECK(parsed.isObject());
|
|
CHECK(parsed["ok"].asBool());
|
|
|
|
const auto invalid = visione::http::parseJsonBody("{oops");
|
|
CHECK(invalid.isNull());
|
|
}
|
|
|
|
DROGON_TEST(GeoJsonMatchesPointInsidePolygon)
|
|
{
|
|
Json::Value polygon;
|
|
polygon["type"] = "Polygon";
|
|
Json::Value ring(Json::arrayValue);
|
|
|
|
Json::Value a(Json::arrayValue);
|
|
a.append(12.0);
|
|
a.append(41.0);
|
|
Json::Value b(Json::arrayValue);
|
|
b.append(13.0);
|
|
b.append(41.0);
|
|
Json::Value c(Json::arrayValue);
|
|
c.append(13.0);
|
|
c.append(42.0);
|
|
Json::Value d(Json::arrayValue);
|
|
d.append(12.0);
|
|
d.append(42.0);
|
|
|
|
ring.append(a);
|
|
ring.append(b);
|
|
ring.append(c);
|
|
ring.append(d);
|
|
ring.append(a);
|
|
polygon["coordinates"].append(ring);
|
|
|
|
CHECK(visione::geojson::pointInPolygon(41.5, 12.5, polygon));
|
|
CHECK(!visione::geojson::pointInPolygon(43.0, 12.5, polygon));
|
|
CHECK(visione::geojson::pointInGeometry(41.5, 12.5, polygon));
|
|
CHECK(!visione::geojson::pointInGeometry(43.0, 12.5, polygon));
|
|
}
|
|
|
|
DROGON_TEST(GeoJsonAcceptsContainedPolygon)
|
|
{
|
|
Json::Value container;
|
|
container["type"] = "Polygon";
|
|
Json::Value outer(Json::arrayValue);
|
|
|
|
Json::Value a(Json::arrayValue);
|
|
a.append(12.0);
|
|
a.append(41.0);
|
|
Json::Value b(Json::arrayValue);
|
|
b.append(13.0);
|
|
b.append(41.0);
|
|
Json::Value c(Json::arrayValue);
|
|
c.append(13.0);
|
|
c.append(42.0);
|
|
Json::Value d(Json::arrayValue);
|
|
d.append(12.0);
|
|
d.append(42.0);
|
|
outer.append(a);
|
|
outer.append(b);
|
|
outer.append(c);
|
|
outer.append(d);
|
|
outer.append(a);
|
|
container["coordinates"].append(outer);
|
|
|
|
Json::Value candidate;
|
|
candidate["type"] = "Polygon";
|
|
Json::Value inner(Json::arrayValue);
|
|
Json::Value p1(Json::arrayValue);
|
|
p1.append(12.2);
|
|
p1.append(41.2);
|
|
Json::Value p2(Json::arrayValue);
|
|
p2.append(12.8);
|
|
p2.append(41.2);
|
|
Json::Value p3(Json::arrayValue);
|
|
p3.append(12.8);
|
|
p3.append(41.8);
|
|
Json::Value p4(Json::arrayValue);
|
|
p4.append(12.2);
|
|
p4.append(41.8);
|
|
inner.append(p1);
|
|
inner.append(p2);
|
|
inner.append(p3);
|
|
inner.append(p4);
|
|
inner.append(p1);
|
|
candidate["coordinates"].append(inner);
|
|
|
|
CHECK(visione::geojson::polygonInsideGeometry(candidate, container));
|
|
|
|
candidate["coordinates"][0][3][0] = 14.0;
|
|
CHECK(!visione::geojson::polygonInsideGeometry(candidate, container));
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
std::promise<void> startedPromise;
|
|
auto startedFuture = startedPromise.get_future();
|
|
|
|
app().addListener("127.0.0.1", 15555);
|
|
|
|
std::thread serverThread([&startedPromise]() {
|
|
app().getLoop()->queueInLoop([&startedPromise]() {
|
|
startedPromise.set_value();
|
|
});
|
|
app().run();
|
|
});
|
|
|
|
startedFuture.get();
|
|
const int status = test::run(argc, argv);
|
|
|
|
app().getLoop()->queueInLoop([]() {
|
|
app().quit();
|
|
});
|
|
serverThread.join();
|
|
|
|
return status;
|
|
}
|