#include "catch2/catch_test_macros.hpp" #include "SessionFixture.hpp" #include "connection.hpp" #include "matador/query/session.hpp" #include "models/recipe.hpp" #include "models/model_metas.hpp" using namespace matador; using namespace matador::object; using namespace matador::test; using namespace matador::query::meta; namespace matador::test { template struct recipe_pk_generator; template struct ingredient_pk_generator { unsigned int id{}; std::string name; collection>> recipes{}; ingredient_pk_generator() = default; explicit ingredient_pk_generator(std::string name) : name(std::move(name)) { } template void process(Operator &op) { namespace field = matador::access; field::primary_key(op, "id", id, PkAttribute); field::attribute(op, "name", name, UniqueVarChar255); field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager); } }; template struct recipe_pk_generator { unsigned int id{}; std::string name; collection>> ingredients{}; recipe_pk_generator() = default; explicit recipe_pk_generator(std::string name) : name(std::move(name)) { } recipe_pk_generator(std::string name, std::vector>> ings) : name(std::move(name)) , ingredients(std::move(ings)){ } template void process(Operator &op) { namespace field = matador::access; field::primary_key(op, "id", id, PkAttribute); field::attribute(op, "name", name, UniqueVarChar255); field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy); } }; using ingredient_identity = ingredient_pk_generator; using ingredient_table = ingredient_pk_generator; using ingredient_sequence = ingredient_pk_generator; using recipe_identity = recipe_pk_generator; using recipe_table = recipe_pk_generator; using recipe_sequence = recipe_pk_generator; } TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation", "[session][insert][has_many_to_many]") { auto result = schema.attach("recipes") .and_then( [this] { return schema.attach("ingredients"); } ) .and_then([this] { return schema.create(db); } ); query::session ses({bus, connection::dns, 4}, schema); std::vector ingredients { make_object(1, "Apple"), make_object(2, "Strawberry"), make_object(3, "Pineapple"), make_object(4, "Sugar"), make_object(5, "Flour"), make_object(6, "Butter"), make_object(7, "Beans") }; std::vector recipes { make_object(1, "Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}), make_object(2, "Strawberry Cake", std::vector{ingredients[5], ingredients[6]}), make_object(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]}) }; for (auto &r: recipes) { REQUIRE(r.is_transient()); auto res = ses.insert(r); REQUIRE(res.is_ok()); REQUIRE(res->is_persistent()); } auto recipe_result = ses.find(1); REQUIRE(recipe_result.is_ok()); REQUIRE(recipe_result->is_persistent()); REQUIRE(recipe_result.value()->ingredients.size() == 3); } TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation identity", "[session][insert][identity][has_many_to_many]") { auto result = schema.attach("recipes") .and_then( [this] { return schema.attach("ingredients"); } ) .and_then([this] { return schema.create(db); } ); query::session ses({bus, connection::dns, 4}, schema); std::vector ingredients { make_object("Apple"), make_object("Strawberry"), make_object("Pineapple"), make_object("Sugar"), make_object("Flour"), make_object("Butter"), make_object("Beans") }; std::vector recipes { make_object("Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}), make_object("Strawberry Cake", std::vector{ingredients[5], ingredients[6]}), make_object("Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]}) }; for (auto &r: recipes) { REQUIRE(r.is_transient()); auto res = ses.insert(r); REQUIRE(res.is_ok()); REQUIRE(res->is_persistent()); } auto recipe_result = ses.find(1); REQUIRE(recipe_result.is_ok()); REQUIRE(recipe_result->is_persistent()); REQUIRE(recipe_result.value()->ingredients.size() == 3); } TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation table sequence", "[session][insert][table_sequence][has_many_to_many]") { auto result = schema.attach("recipes") .and_then( [this] { return schema.attach("ingredients"); } ) .and_then([this] { return schema.create(db); } ); query::session ses({bus, connection::dns, 4}, schema); std::vector ingredients { make_object("Apple"), make_object("Strawberry"), make_object("Pineapple"), make_object("Sugar"), make_object("Flour"), make_object("Butter"), make_object("Beans") }; std::vector recipes { make_object("Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}), make_object("Strawberry Cake", std::vector{ingredients[5], ingredients[6]}), make_object("Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]}) }; for (auto &r: recipes) { REQUIRE(r.is_transient()); auto res = ses.insert(r); REQUIRE(res.is_ok()); REQUIRE(res->is_persistent()); } auto recipe_result = ses.find(1); REQUIRE(recipe_result.is_ok()); REQUIRE(recipe_result->is_persistent()); REQUIRE(recipe_result.value()->ingredients.size() == 3); } TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation sequence", "[session][insert][sequence][has_many_to_many]") { auto result = schema.attach("recipes") .and_then( [this] { return schema.attach("ingredients"); } ) .and_then([this] { return schema.create(db); } ); query::session ses({bus, connection::dns, 4}, schema); std::vector ingredients { make_object("Apple"), make_object("Strawberry"), make_object("Pineapple"), make_object("Sugar"), make_object("Flour"), make_object("Butter"), make_object("Beans") }; std::vector recipes { make_object("Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}), make_object("Strawberry Cake", std::vector{ingredients[5], ingredients[6]}), make_object("Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]}) }; for (auto &r: recipes) { REQUIRE(r.is_transient()); auto res = ses.insert(r); REQUIRE(res.is_ok()); REQUIRE(res->is_persistent()); } auto recipe_result = ses.find(1); REQUIRE(recipe_result.is_ok()); REQUIRE(recipe_result->is_persistent()); REQUIRE(recipe_result.value()->ingredients.size() == 3); const auto ing_result = ses.find(ingredients[0]->id); REQUIRE(ing_result.is_ok()); REQUIRE(ing_result.value()->recipes.size() == 2); }