tests added Model::replicate tests

- also extracted common code to
   AttributeUtils::exceptAttributesForReplicate
This commit is contained in:
silverqx
2022-08-21 14:17:41 +02:00
parent 4fd32ed830
commit 39e1c7f921
5 changed files with 172 additions and 30 deletions

View File

@@ -944,33 +944,9 @@ namespace Orm::Tiny
Derived Model<Derived, AllRelations...>::replicate(
const std::unordered_set<QString> &except)
{
std::unordered_set<QString> defaults {
getKeyName(),
this->getCreatedAtColumn(),
this->getUpdatedAtColumn(),
};
// Remove empty attribute names
std::erase_if(defaults, [](const auto &attribute)
{
return attribute.isEmpty();
});
// Merge defaults into except
std::unordered_set<QString> exceptMerged(defaults.size() + except.size());
if (!except.empty()) {
exceptMerged = except;
exceptMerged.merge(defaults);
}
else
exceptMerged = std::move(defaults);
// Get all attributes excluding those in the exceptMerged set
auto attributes = this->getAttributes()
| ranges::views::filter([&exceptMerged](const AttributeItem &attribute)
{
return !exceptMerged.contains(attribute.key);
})
| ranges::to<QVector<AttributeItem>>();
/* Get all attributes excluding the primary key, created_at, and updated_at
attributes and those in the except set. */
auto attributes = AttributeUtils::exceptAttributesForReplicate(*this, except);
/* Create a new instance (with correctly set a table and connection names),
set obtained attributes and relations. */

View File

@@ -9,7 +9,6 @@ TINY_SYSTEM_HEADER
#include <QtSql/QSqlRecord>
#include <range/v3/algorithm/sort.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/set_algorithm.hpp>
#include "orm/exceptions/domainerror.hpp"

View File

@@ -5,6 +5,11 @@
#include "orm/macros/systemheader.hpp"
TINY_SYSTEM_HEADER
#include <unordered_set>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/filter.hpp>
#include "orm/tiny/tinytypes.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -49,8 +54,51 @@ namespace Orm::Tiny::Utils
joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
const QVector<AttributeItem> &values,
const QString &keyName);
/*! Remove a given attributes from the model attributes vector and return
a copy. */
template<typename Model>
static QVector<AttributeItem>
exceptAttributesForReplicate(const Model &model,
const std::unordered_set<QString> &except = {});
};
/* public */
template<typename Model>
QVector<AttributeItem>
Attribute::exceptAttributesForReplicate(const Model &model,
const std::unordered_set<QString> &except)
{
std::unordered_set<QString> defaults {
model.getKeyName(),
model.getCreatedAtColumn(),
model.getUpdatedAtColumn(),
};
// Remove empty attribute names
std::erase_if(defaults, [](const auto &attribute)
{
return attribute.isEmpty();
});
// Merge defaults into except
std::unordered_set<QString> exceptMerged(defaults.size() + except.size());
if (!except.empty()) {
exceptMerged = except;
exceptMerged.merge(defaults);
}
else
exceptMerged = std::move(defaults);
// Get all attributes excluding those in the exceptMerged set
return model.getAttributes()
| ranges::views::filter([&exceptMerged](const AttributeItem &attribute)
{
return !exceptMerged.contains(attribute.key);
})
| ranges::to<QVector<AttributeItem>>();
}
} // namespace Orm::Tiny::Utils
TINYORM_END_COMMON_NAMESPACE

View File

@@ -3,8 +3,6 @@
#include <unordered_set>
#include <range/v3/algorithm/contains.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/remove_if.hpp>
#include <range/v3/view/transform.hpp>
TINYORM_BEGIN_COMMON_NAMESPACE

View File

@@ -21,10 +21,12 @@ using Orm::Constants::SIZE;
using Orm::DB;
using Orm::Exceptions::MultipleRecordsFoundError;
using Orm::Exceptions::RecordsNotFoundError;
using Orm::One;
using Orm::Tiny::ConnectionOverride;
using Orm::Tiny::Exceptions::MassAssignmentError;
using AttributeUtils = Orm::Tiny::Utils::Attribute;
using TypeUtils = Orm::Utils::Type;
using TestUtils::Databases;
@@ -36,6 +38,8 @@ using Models::Torrent_AllowedMassAssignment;
using Models::Torrent_GuardedAttribute;
using Models::Torrent_TotallyGuarded;
using Models::TorrentEager;
using Models::TorrentPreviewableFileProperty;
using Models::User;
class tst_Model_Connection_Independent : public QObject // clazy:exclude=ctor-missing-parent-argument
{
@@ -56,6 +60,10 @@ private slots:
void equalComparison() const;
void notEqualComparison() const;
void replicate() const;
void replicate_WithCreate() const;
void replicate_WithRelations() const;
void defaultAttributeValues() const;
void massAssignment_Fillable() const;
@@ -359,6 +367,119 @@ void tst_Model_Connection_Independent::notEqualComparison() const
}
}
void tst_Model_Connection_Independent::replicate() const
{
auto user1 = User::find(1);
QVERIFY(user1);
QVERIFY(user1->exists);
auto user1Replicated = user1->replicate();
auto user1Attributes = AttributeUtils::exceptAttributesForReplicate(*user1);
auto user1ReplicatedAttributes = AttributeUtils::exceptAttributesForReplicate(
user1Replicated);
QVERIFY(!user1Replicated.exists);
QVERIFY(user1Attributes == user1ReplicatedAttributes);
QVERIFY(user1->getRelations().empty());
QVERIFY(user1Replicated.getRelations().empty());
}
void tst_Model_Connection_Independent::replicate_WithCreate() const
{
// Following is the most used case for the replicate method so I will test it
auto user = User::create({{"name", "xyz"},
{"is_banned", true},
{"note", "test"}});
QVERIFY(user.exists);
std::unordered_set except {NAME};
auto userReplicated = user.replicate(except);
auto userAttributes = AttributeUtils::exceptAttributesForReplicate(user, except);
auto userReplicatedAttributes = AttributeUtils::exceptAttributesForReplicate(
userReplicated);
QVERIFY(!userReplicated.exists);
QVERIFY(userAttributes == userReplicatedAttributes);
QCOMPARE(user.getAttribute(NAME), QVariant("xyz"));
QVERIFY(!userReplicated.getAttribute(NAME).isValid());
QVERIFY(user.getRelations().empty());
QVERIFY(userReplicated.getRelations().empty());
// Restore db
QVERIFY(user.remove());
}
void tst_Model_Connection_Independent::replicate_WithRelations() const
{
auto torrent2 = Torrent::with("torrentFiles.fileProperty")->find(2);
QVERIFY(torrent2);
QVERIFY(torrent2->exists);
auto torrent2Replicated = torrent2->replicate();
auto torrent2Attributes = AttributeUtils::exceptAttributesForReplicate(*torrent2);
auto torrent2ReplicatedAttributes = AttributeUtils::exceptAttributesForReplicate(
torrent2Replicated);
const auto &torrent2Relations = torrent2->getRelations();
const auto &torrent2ReplicatedRelations = torrent2Replicated.getRelations();
/* Crazy, but I'm going to check all relations 😮😎, yeah it's definitely crazy and
overmotivated, but it checks almost everything I wanted. 🙃🤙 */
// Torrent
QVERIFY(!torrent2Replicated.exists);
QVERIFY(torrent2Attributes == torrent2ReplicatedAttributes);
QVERIFY(torrent2Relations.size() == torrent2ReplicatedRelations.size());
QVERIFY(torrent2->relationLoaded("torrentFiles"));
QVERIFY(torrent2Replicated.relationLoaded("torrentFiles"));
// The Model::operator== was needed to make this real
QVERIFY(torrent2Relations == torrent2ReplicatedRelations);
// TorrentPreviewableFile
const auto torrentFiles2 =
torrent2->getRelationValue<TorrentPreviewableFile>("torrentFiles");
const auto torrentFiles2Replicated =
torrent2Replicated.getRelationValue<TorrentPreviewableFile>("torrentFiles");
QVERIFY(torrentFiles2.size() == torrentFiles2Replicated.size());
for (std::remove_cvref_t<decltype (torrentFiles2)>::size_type i = 0;
torrentFiles2.size() < i; ++i
) {
auto *torrentFile = torrentFiles2.value(i);
QVERIFY(torrentFile->exists);
QVERIFY(torrentFile->relationLoaded("fileProperty"));
auto *torrentFileReplicated = torrentFiles2Replicated.value(i);
QVERIFY(torrentFileReplicated->exists);
QVERIFY(torrentFileReplicated->relationLoaded("fileProperty"));
QVERIFY(torrentFile->getAttributes() == torrentFileReplicated->getAttributes());
// The Model::operator== was needed to make this real
QVERIFY(torrentFile->getRelations() == torrentFileReplicated->getRelations());
// TorrentPreviewableFileProperty
auto *fileProperty =
torrentFile->getRelationValue<TorrentPreviewableFileProperty, One>(
"fileProperty");
auto *filePropertyReplicated =
torrentFileReplicated->getRelationValue<TorrentPreviewableFileProperty,
One>("fileProperty");
QVERIFY(fileProperty->exists);
QVERIFY(fileProperty->getRelations().empty());
QVERIFY(filePropertyReplicated->exists);
QVERIFY(filePropertyReplicated->getRelations().empty());
QVERIFY(fileProperty->getAttributes() ==
filePropertyReplicated->getAttributes());
}
}
void tst_Model_Connection_Independent::defaultAttributeValues() const
{
{