changeset 126:a8bddc9566b0

Server: make more comprehensive tests
author David Demelier <markand@malikania.fr>
date Fri, 22 Sep 2017 13:03:33 +0200
parents e08378afc8f8
children 991f2af682a0
files libserver-test/CMakeLists.txt libserver-test/malikania/server/db/database_fixture.cpp libserver-test/malikania/server/db/database_fixture.hpp libserver-test/malikania/server/db/test_character.cpp libserver-test/malikania/server/db/test_character.hpp libserver-test/malikania/server/db/test_spell.cpp libserver-test/malikania/server/db/test_spell.hpp libserver/malikania/server/db/account.cpp libserver/malikania/server/db/account.hpp libserver/malikania/server/db/character.cpp libserver/malikania/server/db/character.hpp libserver/malikania/server/db/spell.hpp tests/libserver/db/account/main.cpp
diffstat 13 files changed, 289 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/libserver-test/CMakeLists.txt	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver-test/CMakeLists.txt	Fri Sep 22 13:03:33 2017 +0200
@@ -22,6 +22,7 @@
     HEADERS
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/broken_account.hpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/broken_character.hpp
+    ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/database_fixture.hpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_account.hpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_character.hpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_database.hpp
@@ -32,6 +33,7 @@
     SOURCES
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/broken_account.cpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/broken_character.cpp
+    ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/database_fixture.cpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_account.cpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_character.cpp
     ${libmlk-server-test_SOURCE_DIR}/malikania/server/db/test_database.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libserver-test/malikania/server/db/database_fixture.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -0,0 +1,30 @@
+/*
+ * database_fixture.cpp -- helper to test all database backends in a row
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cstdio>
+
+#include "database_fixture.hpp"
+
+namespace mlk {
+
+namespace server {
+
+
+} // !server
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libserver-test/malikania/server/db/database_fixture.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -0,0 +1,60 @@
+/*
+ * database_fixture.hpp -- helper to test all database backends in a row
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_SERVER_DATABASE_FIXTURE_HPP
+#define MALIKANIA_SERVER_DATABASE_FIXTURE_HPP
+
+#include <memory>
+
+#include <boost/mpl/list.hpp>
+
+#include <malikania/server/db/test_database.hpp>
+
+namespace mlk {
+
+namespace server {
+
+/**
+ * \brief Create a test database fixture.
+ */
+class test_fixture {
+private:
+    test_database db_;
+
+public:
+    /**
+     * Access the database object.
+     */
+    inline database& db() noexcept
+    {
+        return db_;
+    }
+};
+
+/**
+ * \brief List to use with BOOST_AUTO_TEST_CASE_TEMPLATE
+ */
+using database_types = boost::mpl::list<
+    test_fixture
+>;
+
+} // !server
+
+} // !mlk
+
+#endif // !MALIKANIA_SERVER_DATABASE_FIXTURE_HPP
--- a/libserver-test/malikania/server/db/test_character.cpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver-test/malikania/server/db/test_character.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -63,9 +63,9 @@
     level_ = json["level"];
 }
 
-character_set test_dao::characters_for_account(std::int64_t account_id)
+character_list test_dao::characters_for_account(std::int64_t account_id)
 {
-    character_set set;
+    character_list set;
 
     for (const auto& pair : characters_) {
         if (pair.second["account_id"].get<int>() == account_id) {
@@ -76,7 +76,7 @@
             );
 
             c->unserialize(pair.second);
-            set.insert(std::move(c));
+            set.push_back(std::move(c));
         }
     }
 
--- a/libserver-test/malikania/server/db/test_character.hpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver-test/malikania/server/db/test_character.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -143,7 +143,7 @@
     /**
      * Get the set of characters for the given account.
      */
-    character_set characters_for_account(std::int64_t account_id);
+    character_list characters_for_account(std::int64_t account_id);
 
     /**
      * Remove all characters for the specified account.
--- a/libserver-test/malikania/server/db/test_spell.cpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver-test/malikania/server/db/test_spell.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -43,20 +43,20 @@
     level_ = json["level"];
 }
 
-spell_set test_dao::spells_for_charater(std::int64_t character_id)
+spell_list test_dao::spells_for_charater(std::int64_t character_id)
 {
-    spell_set set;
+    spell_list list;
 
     for (const auto& pair : spells_) {
         if (pair.second["character_id"].get<int>() == character_id) {
             auto s = std::make_unique<test_spell>(pair.second["classname"], db_);
 
             s->unserialize(pair.second);
-            set.insert(std::move(s));
+            list.push_back(std::move(s));
         }
     }
 
-    return set;
+    return list;
 }
 
 void test_dao::remove_all(std::int64_t character_id)
--- a/libserver-test/malikania/server/db/test_spell.hpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver-test/malikania/server/db/test_spell.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -119,7 +119,7 @@
     /**
      * Get the set of spells for the given character.
      */
-    spell_set spells_for_charater(std::int64_t character_id);
+    spell_list spells_for_charater(std::int64_t character_id);
 
     /**
      * Remove all characters for the specified character.
--- a/libserver/malikania/server/db/account.cpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver/malikania/server/db/account.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -27,31 +27,33 @@
  * account::add_character
  * ------------------------------------------------------------------
  */
-const std::unique_ptr<character>& account::add_character(std::unique_ptr<character> ch)
+std::unique_ptr<character>& account::add_character(std::unique_ptr<character> ch)
 {
     assert(is_published());
     assert(ch && ch->is_draft());
 
     ch->do_save(id_);
+    characters_.push_back(std::move(ch));
 
-    return *characters_.insert(std::move(ch)).first;
+    return characters_.back();
 }
 
 /*
  * account::remove_character
  * ------------------------------------------------------------------
  */
-void account::remove_character(const std::unique_ptr<character>& ch)
+void account::remove_character(std::unique_ptr<character>& ch)
 {
     assert(is_published());
     assert(ch && ch->is_published());
 
-    auto it = characters_.find(ch);
+    auto it = std::find(characters_.begin(), characters_.end(), ch);
 
     if (it == characters_.end())
         return;
 
     ch->do_remove();
+    ch = nullptr;
     characters_.erase(it);
 }
 
--- a/libserver/malikania/server/db/account.hpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver/malikania/server/db/account.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -24,6 +24,8 @@
  * \brief Database account object.
  */
 
+#include <algorithm>
+
 #include "model.hpp"
 #include "character.hpp"
 
@@ -44,7 +46,7 @@
     std::string email_;                 //!< raw email
     std::string firstname_;             //!< optional first name
     std::string lastname_;              //!< optional last name
-    character_set characters_;          //!< set of characters
+    character_list characters_;         //!< list of characters
 
     /**
      * Save the account.
@@ -248,7 +250,7 @@
      *
      * \return the associated characters.
      */
-    inline const character_set& characters() const noexcept
+    inline const character_list& characters() const noexcept
     {
         return characters_;
     }
@@ -263,15 +265,37 @@
      * \return the attached character
      * \post ch->is_published()
      */
-    const std::unique_ptr<character>& add_character(std::unique_ptr<character> ch);
+    std::unique_ptr<character>& add_character(std::unique_ptr<character> ch);
+
+    /**
+     * Find a character by a predicate.
+     *
+     * The predicate must be the same as one use in std::find_if, the unique
+     * value given as argument is the const std::unique_ptr<character>&.
+     *
+     * \param pred the unary predicate
+     * \return the character or null if not found.
+     */
+    template <typename Predicate>
+    inline std::unique_ptr<character>& find_character(Predicate&& pred) noexcept
+    {
+        static std::unique_ptr<character> none;
+
+        auto it = std::find_if(characters_.begin(), characters_.end(), std::forward<Predicate>(pred));
+
+        if (it != characters_.end())
+            return *it;
+
+        return none;
+    }
 
     /**
      * Remove the character from the account.
      *
      * \param ch the character
-     * \post ch->is_draft() and empty
+     * \post ch == nullptr
      */
-    void remove_character(const std::unique_ptr<character>& ch);
+    void remove_character(std::unique_ptr<character>& ch);
 
     /**
      * Save the account, does nothing if is_published().
--- a/libserver/malikania/server/db/character.cpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver/malikania/server/db/character.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -26,31 +26,33 @@
  * character::add_spell
  * ------------------------------------------------------------------
  */
-const std::unique_ptr<spell>& character::add_spell(std::unique_ptr<spell> sp)
+std::unique_ptr<spell>& character::add_spell(std::unique_ptr<spell> sp)
 {
     assert(is_published());
     assert(sp && sp->is_draft());
 
     sp->do_save(id_);
+    spells_.push_back(std::move(sp));
 
-    return *spells_.insert(std::move(sp)).first;
+    return spells_.back();
 }
 
 /*
  * character::remove_spell
  * ------------------------------------------------------------------
  */
-void character::remove_spell(const std::unique_ptr<spell>& sp)
+void character::remove_spell(std::unique_ptr<spell>& sp)
 {
     assert(is_published());
     assert(sp && sp->is_published());
 
-    auto it = spells_.find(sp);
+    auto it = std::find(spells_.begin(), spells_.end(), sp);
 
     if (it == spells_.end())
         return;
 
     sp->do_remove();
+    sp = nullptr;
     spells_.erase(it);
 }
 
--- a/libserver/malikania/server/db/character.hpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver/malikania/server/db/character.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -24,6 +24,8 @@
  * \brief Database character object.
  */
 
+#include <algorithm>
+
 #include "model.hpp"
 #include "spell.hpp"
 
@@ -43,7 +45,7 @@
     std::string nickname_;              //!< nickname (non null)
     std::string classname_;             //!< class type to instanciate
     std::uint16_t level_{1};            //!< character level
-    spell_set spells_;                  //!< set of spells
+    spell_list spells_;                 //!< list of spells
 
     /**
      * Save this character.
@@ -132,7 +134,7 @@
      *
      * \return the spells
      */
-    inline const spell_set& spells() const noexcept
+    inline const spell_list& spells() const noexcept
     {
         return spells_;
     }
@@ -146,15 +148,15 @@
      * \param sp the spell
      * \return the attached spell
      */
-    const std::unique_ptr<spell>& add_spell(std::unique_ptr<spell> sp);
+    std::unique_ptr<spell>& add_spell(std::unique_ptr<spell> sp);
 
     /**
      * Remove the spell from the character.
      *
      * \param sp the spell
-     * \post sp->is_draft() and empty
+     * \post sp == nullptr
      */
-    void remove_spell(const std::unique_ptr<spell>& sp);
+    void remove_spell(std::unique_ptr<spell>& sp);
 
     /**
      * Set the account level.
@@ -173,7 +175,7 @@
 /**
  * Type for storing characters.
  */
-using character_set = std::unordered_set<std::unique_ptr<character>>;
+using character_list = std::vector<std::unique_ptr<character>>;
 
 } // !server
 
--- a/libserver/malikania/server/db/spell.hpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/libserver/malikania/server/db/spell.hpp	Fri Sep 22 13:03:33 2017 +0200
@@ -26,7 +26,7 @@
 
 #include <memory>
 #include <string>
-#include <unordered_set>
+#include <vector>
 
 #include "model.hpp"
 
@@ -122,7 +122,7 @@
 /**
  * Type for storing spells.
  */
-using spell_set = std::unordered_set<std::unique_ptr<spell>>;
+using spell_list = std::vector<std::unique_ptr<spell>>;
 
 } // !server
 
--- a/tests/libserver/db/account/main.cpp	Fri Sep 22 13:42:31 2017 +0200
+++ b/tests/libserver/db/account/main.cpp	Fri Sep 22 13:03:33 2017 +0200
@@ -22,6 +22,7 @@
 #include <malikania/server/db/test_database.hpp>
 #include <malikania/server/db/broken_account.hpp>
 #include <malikania/server/db/broken_character.hpp>
+#include <malikania/server/db/database_fixture.hpp>
 
 namespace mlk {
 
@@ -61,47 +62,7 @@
  * Basic suite using test database.
  * ------------------------------------------------------------------
  */
-
-BOOST_FIXTURE_TEST_SUITE(basic_suite, account_fixture)
-
-/*
- * account::save.
- * ------------------------------------------------------------------
- *
- * Verify that database is untouched during all operation on a draft account.
- */
-BOOST_AUTO_TEST_CASE(save)
-{
-    auto acc = db_->account_draft("nanahara", "dummypassword");
-
-    // Initial creation must be draft.
-    BOOST_TEST(acc->is_draft());
-
-    /*
-     * Set some fields, the database must not be modified at all but fields
-     * must be set.
-     */
-    acc->set_email("nanahara@malikania.fr");
-    acc->set_firstname("Alexis");
-    acc->set_lastname("Dörr");
-
-    BOOST_TEST(acc->is_draft());
-    BOOST_TEST(!acc->is_published());
-    BOOST_TEST(acc->email() == "nanahara@malikania.fr");
-    BOOST_TEST(acc->firstname() == "Alexis");
-    BOOST_TEST(acc->lastname() == "Dörr");
-    BOOST_TEST(accounts().size() == 0U);
-
-    // Do save, database must be updated and account published.
-    acc->save();
-
-    BOOST_TEST(!acc->is_draft());
-    BOOST_TEST(acc->is_published());
-    BOOST_TEST(acc->email() == "nanahara@malikania.fr");
-    BOOST_TEST(acc->firstname() == "Alexis");
-    BOOST_TEST(acc->lastname() == "Dörr");
-    BOOST_TEST(accounts().size() == 1U);
-}
+BOOST_FIXTURE_TEST_SUITE(draft_suite, account_fixture)
 
 /*
  * account::set_password.
@@ -112,10 +73,8 @@
     auto ac = db_->account_draft("markand", "nopassword");
 
     ac->set_password("temporarypassword");
-    ac->save();
-    ac->set_password("newpassword");
 
-    BOOST_TEST(get(ac->id())["password"].get<std::string>() == "newpassword");
+    BOOST_TEST(accounts().empty());
 }
 
 /*
@@ -127,10 +86,8 @@
     auto ac = db_->account_draft("markand", "nopassword");
 
     ac->set_email("fake@malikania.fr");
-    ac->save();
-    ac->set_email("markand@malikania.fr");
 
-    BOOST_TEST(get(ac->id())["email"].get<std::string>() == "markand@malikania.fr");
+    BOOST_TEST(accounts().empty());
 }
 
 /*
@@ -142,10 +99,8 @@
     auto ac = db_->account_draft("markand", "nopassword");
 
     ac->set_firstname("Jean");
-    ac->save();
-    ac->set_firstname("David");
 
-    BOOST_TEST(get(ac->id())["firstname"].get<std::string>() == "David");
+    BOOST_TEST(accounts().empty());
 }
 
 /*
@@ -157,74 +112,10 @@
     auto ac = db_->account_draft("markand", "nopassword");
 
     ac->set_lastname("Bertrand");
-    ac->save();
-    ac->set_lastname("Demelier");
 
-    BOOST_TEST(get(ac->id())["lastname"].get<std::string>() == "Demelier");
-}
-
-/*
- * account::add_character.
- * ------------------------------------------------------------------
- */
-BOOST_AUTO_TEST_CASE(add_character)
-{
-    auto ac = db_->account_draft("markand", "nopassword");
-
-    ac->save();
-
-    auto& ch = ac->add_character(db_->character_draft("erekin", "fire"));
-
-    BOOST_TEST(ch->is_published());
-    BOOST_TEST(characters().size() == 1U);
+    BOOST_TEST(accounts().empty());
 }
 
-/*
- * account::dao::find_by_login suite.
- * ------------------------------------------------------------------
- */
-BOOST_AUTO_TEST_SUITE(find_by_login)
-
-/*
- * Find existing account.
- * ------------------------------------------------------------------
- */
-BOOST_AUTO_TEST_CASE(simple)
-{
-    {
-        auto ac = db_->account_draft("markand", "nopassword");
-
-        ac->set_email("markand@malikania.fr");
-        ac->set_firstname("David");
-        ac->set_lastname("Demelier");
-        ac->save();
-        ac->add_character(db_->character_draft("erekin", "mage"));
-        ac->add_character(db_->character_draft("irina", "fairy"));
-    }
-
-    auto ac = db_->account_dao().find_by_login("markand");
-
-    BOOST_TEST(ac->is_published());
-    BOOST_TEST(ac->login() == "markand");
-    BOOST_TEST(ac->email() == "markand@malikania.fr");
-    BOOST_TEST(ac->firstname() == "David");
-    BOOST_TEST(ac->lastname() == "Demelier");
-    BOOST_TEST(ac->characters().size() == 2U);
-}
-
-/*
- * Not existing account.
- * ------------------------------------------------------------------
- */
-BOOST_AUTO_TEST_CASE(not_found)
-{
-    auto ac = db_->account_dao().find_by_login("doesnotexist");
-
-    BOOST_TEST(!ac);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
 BOOST_AUTO_TEST_SUITE_END()
 
 /*
@@ -352,6 +243,137 @@
 
 BOOST_AUTO_TEST_SUITE_END()
 
+/*
+ * Generic tests using all backends.
+ * ------------------------------------------------------------------
+ */
+BOOST_AUTO_TEST_SUITE(generic)
+
+void fill(database& db)
+{
+    auto ac = db.account_draft("markand", "nopassword");
+
+    ac->set_email("markand@malikania.fr");
+    ac->set_firstname("David");
+    ac->set_lastname("Demelier");
+    ac->save();
+
+    {
+        auto ch = db.character_draft("erekin", "blackmage");
+        ch->set_level(75);
+        ac->add_character(std::move(ch));
+    }
+
+    {
+        auto ch = db.character_draft("luna", "fairy");
+        ch->set_level(45);
+        ac->add_character(std::move(ch));
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(load, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+
+    auto ac = db.db().account_dao().find_by_login("markand");
+
+    BOOST_TEST(ac->login() == "markand");
+    BOOST_TEST(ac->password() == "nopassword");
+    BOOST_TEST(ac->email() == "markand@malikania.fr");
+    BOOST_TEST(ac->firstname() == "David");
+    BOOST_TEST(ac->lastname() == "Demelier");
+    BOOST_TEST(ac->characters().size() == 2U);
+
+    auto& erekin = ac->find_character([] (const auto& c) {
+        return c->nickname() == "erekin";
+    });
+
+    BOOST_TEST(erekin.get());
+    BOOST_TEST(erekin->nickname() == "erekin");
+    BOOST_TEST(erekin->classname() == "blackmage");
+    BOOST_TEST(erekin->level() == 75U);
+
+    auto& luna = ac->find_character([] (const auto& c) {
+        return c->nickname() == "luna";
+    });
+
+    BOOST_TEST(luna.get());
+    BOOST_TEST(luna->nickname() == "luna");
+    BOOST_TEST(luna->classname() == "fairy");
+    BOOST_TEST(luna->level() == 45U);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(set_password, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+    db.db().account_dao().find_by_login("markand")->set_password("hello");
+
+    BOOST_TEST(db.db().account_dao().find_by_login("markand")->password() == "hello");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(set_email, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+    db.db().account_dao().find_by_login("markand")->set_email("no@spam.org");
+
+    BOOST_TEST(db.db().account_dao().find_by_login("markand")->email() == "no@spam.org");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(set_firstname, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+    db.db().account_dao().find_by_login("markand")->set_firstname("Francis");
+
+    BOOST_TEST(db.db().account_dao().find_by_login("markand")->firstname() == "Francis");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(set_lastname, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+    db.db().account_dao().find_by_login("markand")->set_lastname("Lalanne");
+
+    BOOST_TEST(db.db().account_dao().find_by_login("markand")->lastname() == "Lalanne");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(remove_character, Database, database_types)
+{
+    Database db;
+
+    fill(db.db());
+
+    {
+        auto ac = db.db().account_dao().find_by_login("markand");
+
+        ac->remove_character(
+            ac->find_character([] (const auto& c) {
+                return c->nickname() == "luna";
+            })
+        );
+    }
+
+    auto ac = db.db().account_dao().find_by_login("markand");
+
+    BOOST_TEST(ac->characters().size() == 1U);
+
+    auto& luna = ac->find_character([] (const auto& c) {
+        return c->nickname() == "luna";
+    });
+
+    BOOST_TEST(!luna.get());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
 } // !server
 
 } // !mlk