Mercurial > malikania
changeset 137:0f9a8ddee022
Server: rework storage of test_database, closes #714
Do not store any objects in maps, instead store a unique nlohmann::json object
in the test_database class, update that object from any model object.
Unify access to the objects with the following functions:
- test_database::ref, access the JSON object,
- test_database::dict, access the parent JSON object.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 05 Oct 2017 08:38:47 +0200 |
parents | 835c8ee3f9e5 |
children | 532f259557dd |
files | libserver-test/malikania/server/db/test_account.cpp libserver-test/malikania/server/db/test_account.hpp libserver-test/malikania/server/db/test_character.cpp libserver-test/malikania/server/db/test_character.hpp libserver-test/malikania/server/db/test_database.cpp libserver-test/malikania/server/db/test_database.hpp libserver-test/malikania/server/db/test_spell.cpp libserver-test/malikania/server/db/test_spell.hpp tests/libserver/db/account/main.cpp |
diffstat | 9 files changed, 251 insertions(+), 243 deletions(-) [+] |
line wrap: on
line diff
--- a/libserver-test/malikania/server/db/test_account.cpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_account.cpp Thu Oct 05 08:38:47 2017 +0200 @@ -26,47 +26,60 @@ void test_account::do_save() { - assert(db_.test_account_dao().accounts().count(id_) == 0); + assert(!db_.exists(*this)); - id_ = db_.test_account_dao().next_id(); - db_.test_account_dao().accounts().emplace(id_, serialize()); + id_ = db_.next_account_id(); + db_.dict(*this)[db_.sid(*this)] = serialize(); } void test_account::do_remove() { - assert(db_.test_account_dao().accounts().count(id_)); + assert(db_.exists(*this)); - // Do cascade deletion. - db_.test_account_dao().accounts().erase(id_); - db_.test_character_dao().remove_all(id_); + db_.dict(*this).erase(db_.sid(*this)); } void test_account::do_set_password(const std::string& password) { - assert(db_.test_account_dao().accounts().count(id_)); + assert(db_.exists(*this)); - db_.test_account_dao().accounts().at(id_)["password"] = password; + db_.ref(*this)["password"] = password; } void test_account::do_set_email(const std::string& email) { - assert(db_.test_account_dao().accounts().count(id_)); + assert(db_.exists(*this)); - db_.test_account_dao().accounts().at(id_)["email"] = email; + db_.ref(*this)["email"] = email; } void test_account::do_set_firstname(const std::string& name) { - assert(db_.test_account_dao().accounts().count(id_)); + assert(db_.exists(*this)); - db_.test_account_dao().accounts().at(id_)["firstname"] = name; + + db_.ref(*this)["firstname"] = name; } void test_account::do_set_lastname(const std::string& name) { - assert(db_.test_account_dao().accounts().count(id_)); + assert(db_.exists(*this)); + + db_.ref(*this)["lastname"] = name; +} - db_.test_account_dao().accounts().at(id_)["lastname"] = name; +test_account::test_account(nlohmann::json json, test_database& db) + : test_account(json["login"], json["password"], db) +{ + id_ = json["id"]; + login_ = json["login"]; + password_ = json["password"]; + email_ = json["email"]; + firstname_ = json["firstname"]; + lastname_ = json["lastname"]; + + for (const auto& cdata : json["characters"]) + characters_.push_back(std::make_unique<test_character>(cdata, db_)); } nlohmann::json test_account::serialize() const @@ -77,32 +90,16 @@ { "password", password_ }, { "email", email_ }, { "firstname", firstname_ }, - { "lastname", lastname_ } + { "lastname", lastname_ }, + { "characters", nlohmann::json::object() } }; } -void test_account::unserialize(const nlohmann::json& input) -{ - id_ = input["id"]; - login_ = input["login"]; - password_ = input["password"]; - email_ = input["email"]; - firstname_ = input["firstname"]; - lastname_ = input["lastname"]; -} - std::unique_ptr<account> test_dao::find_by_login(const std::string& login) { - for (const auto& pair : accounts_) { - if (pair.second["login"] == login) { - auto a = std::make_unique<test_account>(pair.second["login"], pair.second["password"], db_); - - a->unserialize(pair.second); - a->characters_ = db_.test_character_dao().characters_for_account(a->id_); - - return std::move(a); - } - } + for (const auto& v : db_.data()["accounts"]) + if (v["login"].get<std::string>() == login) + return std::make_unique<test_account>(v, db_); return nullptr; }
--- a/libserver-test/malikania/server/db/test_account.hpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_account.hpp Thu Oct 05 08:38:47 2017 +0200 @@ -50,6 +50,17 @@ public: /** + * Parse the JSON as account. + * + * This also load recursively characters. + * + * \warning json input is not verified + * \param json the JSON input + * \param db the database + */ + test_account(nlohmann::json json, test_database& db); + + /** * Construct a test_account. * * \param login the login @@ -63,6 +74,16 @@ } /** + * Path where accounts are saved in JSON. + * + * \return the path as JSON pointer + */ + inline std::string path() const noexcept + { + return "/accounts"; + } + + /** * Dump this account as json. * * \return the json @@ -70,14 +91,6 @@ nlohmann::json serialize() const; /** - * Fill this account with json input. - * - * \warning json input must be valid, no checks are performed - * \param input the json input - */ - void unserialize(const nlohmann::json& json); - - /** * \copydoc account::do_save */ void do_save() override; @@ -116,7 +129,6 @@ */ class test_account::test_dao : public account::dao { private: - std::unordered_map<std::int64_t, nlohmann::json> accounts_; std::int64_t sequence_{0}; test_database& db_; @@ -132,36 +144,6 @@ } /** - * Get the in memory accounts. - * - * \return the account map - */ - inline const std::unordered_map<std::int64_t, nlohmann::json>& accounts() const noexcept - { - return accounts_; - } - - /** - * Overloaded function. - * - * \return the account map - */ - inline std::unordered_map<std::int64_t, nlohmann::json>& accounts() noexcept - { - return accounts_; - } - - /** - * Compute the next id from the internal sequence. - * - * \return the next id - */ - inline std::int64_t next_id() noexcept - { - return ++sequence_; - } - - /** * \copydoc dao::find_by_login */ std::unique_ptr<account> find_by_login(const std::string& login) override;
--- a/libserver-test/malikania/server/db/test_character.cpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_character.cpp Thu Oct 05 08:38:47 2017 +0200 @@ -26,21 +26,19 @@ void test_character::do_save(std::int64_t account_id) { - id_ = db_.test_character_dao().next_id(); + id_ = db_.next_character_id(); account_id_ = account_id; - db_.test_character_dao().characters().emplace(id_, serialize()); + db_.dict(*this)[db_.sid(*this)] = serialize(); } void test_character::do_remove() { - db_.test_character_dao().characters().erase(id_); + db_.dict(*this).erase(db_.sid(*this)); } void test_character::do_set_level(std::uint16_t level) { - assert(db_.test_character_dao().characters().count(id_)); - - db_.test_character_dao().characters().at(id_)["level"] = level; + db_.ref(*this)["level"] = level; } nlohmann::json test_character::serialize() const @@ -54,7 +52,8 @@ }; } -void test_character::unserialize(const nlohmann::json& json) +test_character::test_character(nlohmann::json json, test_database& db) + : test_character(json["nickname"], json["classname"], db) { id_ = json["id"]; account_id_ = json["account_id"]; @@ -63,37 +62,6 @@ level_ = json["level"]; } -character_list test_dao::characters_for_account(std::int64_t account_id) -{ - character_list set; - - for (const auto& pair : characters_) { - if (pair.second["account_id"].get<int>() == account_id) { - auto c = std::make_unique<test_character>( - pair.second["nickname"], - pair.second["classname"], - db_ - ); - - c->unserialize(pair.second); - set.push_back(std::move(c)); - } - } - - return set; -} - -void test_dao::remove_all(std::int64_t account_id) -{ - for (auto it = characters_.begin(); it != characters_.end(); ) { - if (it->second["account_id"].get<int>() == account_id) { - db_.test_character_dao().remove_all(it->second["id"]); - it = characters_.erase(it); - } else - it++; - } -} - } // !server } // !mlk
--- a/libserver-test/malikania/server/db/test_character.hpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_character.hpp Thu Oct 05 08:38:47 2017 +0200 @@ -25,6 +25,7 @@ */ #include <unordered_map> +#include <sstream> #include <json.hpp> @@ -64,6 +65,17 @@ public: /** + * Parse the JSON as character. + * + * This also load recursively spells. + * + * \warning json input is not verified + * \param json the JSON input + * \param db the database + */ + test_character(nlohmann::json json, test_database& db); + + /** * Construct a test account. * * \param nickname the nickname @@ -77,80 +89,28 @@ } /** + * Path where characters are saved in JSON. + * + * \return the path as JSON pointer + */ + inline std::string path() const + { + assert(account_id_ > 0); + + std::ostringstream oss; + + oss << "/accounts/" << account_id_; + oss << "/characters"; + + return oss.str(); + } + + /** * Dump this account as json. * * \return the json */ nlohmann::json serialize() const; - - /** - * Fill this account with json input. - * - * \warning json input must be valid, no checks are performed - * \param input the json input - */ - void unserialize(const nlohmann::json& json); -}; - -/** - * \brief Character dao for test_character. - * - * This class saves characters in memory, thus once deleted the no characters - * are available anymore. - */ -class test_character::test_dao { -private: - std::unordered_map<std::int64_t, nlohmann::json> characters_; - std::int64_t sequence_{0}; - test_database& db_; - -public: - inline test_dao(test_database& db) noexcept - : db_(db) - { - } - - /** - * Get the in memory characters. - * - * \return the character map - */ - inline const std::unordered_map<std::int64_t, nlohmann::json>& characters() const noexcept - { - return characters_; - } - - /** - * Overloaded function. - * - * \return the character map - */ - inline std::unordered_map<std::int64_t, nlohmann::json>& characters() noexcept - { - return characters_; - } - - /** - * Compute the next id from the internal sequence. - * - * \return the next id - */ - inline std::int64_t next_id() noexcept - { - return ++sequence_; - } - - /** - * Get the set of characters for the given account. - */ - character_list characters_for_account(std::int64_t account_id); - - /** - * Remove all characters for the specified account. - * - * \param account_id the account id - */ - void remove_all(std::int64_t account_id); }; } // !server
--- a/libserver-test/malikania/server/db/test_database.cpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_database.cpp Thu Oct 05 08:38:47 2017 +0200 @@ -23,6 +23,19 @@ namespace server { +/* + * test_database::test_database + * ------------------------------------------------------------------ + * + * We can't use the in-class initialization because the JSON object will be + * constructed as an array. + */ +test_database::test_database() + : data_(nlohmann::json::value_t::object) +{ + data_["accounts"] = nlohmann::json::object(); +} + std::unique_ptr<account> test_database::account_draft(std::string login, std::string password) { return std::make_unique<test_account>(std::move(login), std::move(password), *this);
--- a/libserver-test/malikania/server/db/test_database.hpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_database.hpp Thu Oct 05 08:38:47 2017 +0200 @@ -27,6 +27,8 @@ #include <malikania/server/db/database.hpp> +#include <json.hpp> + #include "test_account.hpp" #include "test_character.hpp" #include "test_spell.hpp" @@ -40,12 +42,59 @@ */ class test_database : public database { private: + nlohmann::json data_; test_spell::test_dao spell_dao_{*this}; - test_character::test_dao character_dao_{*this}; test_account::test_dao account_dao_{*this}; + std::uint64_t seq_account_id_{1}; + std::uint64_t seq_character_id_{1}; public: /** + * Construct the database and initialize the JSON tree. + */ + test_database(); + + /** + * Get the next account id. + * + * \return the next id + */ + inline std::int64_t next_account_id() noexcept + { + return seq_account_id_ ++; + } + + /** + * Get the next character id. + * + * \return the next character id + */ + inline std::int64_t next_character_id() noexcept + { + return seq_character_id_ ++; + } + + /** + * Access the underlying data. + * + * \return the data + */ + inline const nlohmann::json& data() const noexcept + { + return data_; + } + + /** + * Overloaded function. + * + * \return the data + */ + inline nlohmann::json& data() noexcept + { + return data_; + } + + /** * Get the real underlying test_dao type for accounts. * * \return the test_dao instance @@ -56,16 +105,6 @@ } /** - * Get the real underlying test_dao type for characters. - * - * \return the test_character::test_dao instance - */ - inline test_character::test_dao& test_character_dao() noexcept - { - return character_dao_; - } - - /** * Get the real underlying test_dao type for spells. * * \return the test_spell::test_dao instance @@ -89,6 +128,86 @@ * \copydoc database::character_draft */ std::unique_ptr<character> character_draft(std::string nickname, std::string classname) override; + + /** + * Convert the model id into a string to be used as JSON keys. + * + * \return the string id + */ + inline std::string sid(const model& m) const noexcept + { + return std::to_string(m.id()); + } + + /** + * Overload that takes an id. + * + * \return the id + */ + inline std::string sid(std::int64_t id) const noexcept + { + return std::to_string(id); + } + + /** + * Check if the specified object exists in JSON database. + * + * \param obj the object + * \return true if exists + */ + template <typename Object> + inline bool exists(const Object& obj) const noexcept + { + return data_[nlohmann::json::json_pointer(obj.path())].count(sid(obj)) > 0; + } + + /** + * Get the parent JSON object. + * + * \param obj the object + * \return the parent JSON object + */ + template <typename Object> + inline const nlohmann::json& dict(Object& obj) const noexcept + { + return data_[nlohmann::json::json_pointer(obj.path())]; + } + + /** + * Overloaded function. + * + * \param obj the object + * \return the parent JSON object + */ + template <typename Object> + inline nlohmann::json& dict(Object& obj) noexcept + { + return data_[nlohmann::json::json_pointer(obj.path())]; + } + + /** + * Get the reference to the object in JSON database. + * + * \param obj the object + * \return the reference to the object + */ + template <typename Object> + inline nlohmann::json& ref(Object& obj) noexcept + { + return data_[nlohmann::json::json_pointer(obj.path() + std::string("/") + sid(obj))]; + } + + /** + * Overloaded function + * + * \param obj the object + * \return the reference to the object + */ + template <typename Object> + inline const nlohmann::json& ref(const Object& obj) noexcept + { + return data_[nlohmann::json::json_pointer(obj.path() + std::string("/") + sid(obj))]; + } }; } // !server
--- a/libserver-test/malikania/server/db/test_spell.cpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_spell.cpp Thu Oct 05 08:38:47 2017 +0200 @@ -10,19 +10,14 @@ { id_ = db_.test_spell_dao().next_id(); character_id_ = character_id; - db_.test_spell_dao().spells().emplace(id_, serialize()); } void test_spell::do_remove() { - db_.test_spell_dao().spells().erase(id_); } -void test_spell::do_set_level(std::uint8_t level) +void test_spell::do_set_level(std::uint8_t) { - assert(db_.test_spell_dao().spells().count(id_)); - - db_.test_spell_dao().spells().at(id_)["level"] = level; } nlohmann::json test_spell::serialize() const @@ -45,6 +40,7 @@ spell_list test_dao::spells_for_charater(std::int64_t character_id) { +#if 0 spell_list list; for (const auto& pair : spells_) { @@ -57,16 +53,25 @@ } return list; +#endif + spell_list list; + + (void)character_id; + + return list; } void test_dao::remove_all(std::int64_t character_id) { +#if 0 for (auto it = spells_.begin(); it != spells_.end(); ) { if (it->second["character_id"].get<int>() == character_id) it = spells_.erase(it); else it++; } +#endif + (void)character_id; } } // !server
--- a/libserver-test/malikania/server/db/test_spell.hpp Fri Oct 20 11:44:57 2017 +0200 +++ b/libserver-test/malikania/server/db/test_spell.hpp Thu Oct 05 08:38:47 2017 +0200 @@ -76,7 +76,6 @@ class test_spell::test_dao { private: - std::unordered_map<std::int64_t, nlohmann::json> spells_; std::int64_t sequence_{0}; test_database& db_; @@ -87,26 +86,6 @@ } /** - * Get the in memory spells. - * - * \return the spell map - */ - inline const std::unordered_map<std::int64_t, nlohmann::json>& spells() const noexcept - { - return spells_; - } - - /** - * Overloaded function. - * - * \return the spell map - */ - inline std::unordered_map<std::int64_t, nlohmann::json>& spells() noexcept - { - return spells_; - } - - /** * Compute the next id from the internal sequence. * * \return the next id
--- a/tests/libserver/db/account/main.cpp Fri Oct 20 11:44:57 2017 +0200 +++ b/tests/libserver/db/account/main.cpp Thu Oct 05 08:38:47 2017 +0200 @@ -30,31 +30,16 @@ class account_fixture { protected: - std::shared_ptr<test_database> db_{new test_database}; + test_database db_; - inline const std::unordered_map<std::int64_t, nlohmann::json> accounts() const noexcept - { - return db_->test_account_dao().accounts(); - } - - inline std::unordered_map<std::int64_t, nlohmann::json> accounts() noexcept + const nlohmann::json& accounts() const noexcept { - return db_->test_account_dao().accounts(); - } - - inline const std::unordered_map<std::int64_t, nlohmann::json> characters() const noexcept - { - return db_->test_character_dao().characters(); - } - - inline std::unordered_map<std::int64_t, nlohmann::json> characters() noexcept - { - return db_->test_character_dao().characters(); + return db_.data()["accounts"]; } const nlohmann::json& get(std::int64_t id) const noexcept { - return db_->test_account_dao().accounts().at(id); + return accounts().at(std::to_string(id)); } }; @@ -70,7 +55,7 @@ */ BOOST_AUTO_TEST_CASE(set_password) { - auto ac = db_->account_draft("markand", "nopassword"); + auto ac = db_.account_draft("markand", "nopassword"); ac->set_password("temporarypassword"); @@ -83,7 +68,7 @@ */ BOOST_AUTO_TEST_CASE(set_email) { - auto ac = db_->account_draft("markand", "nopassword"); + auto ac = db_.account_draft("markand", "nopassword"); ac->set_email("fake@malikania.fr"); @@ -96,7 +81,7 @@ */ BOOST_AUTO_TEST_CASE(set_firstname) { - auto ac = db_->account_draft("markand", "nopassword"); + auto ac = db_.account_draft("markand", "nopassword"); ac->set_firstname("Jean"); @@ -109,7 +94,7 @@ */ BOOST_AUTO_TEST_CASE(set_lastname) { - auto ac = db_->account_draft("markand", "nopassword"); + auto ac = db_.account_draft("markand", "nopassword"); ac->set_lastname("Bertrand");