changeset 558:f1ad428208d3

Tests: convert cmd-server-*, #593
author David Demelier <markand@malikania.fr>
date Sat, 25 Nov 2017 17:00:24 +0100
parents c729f06c6f27
children 525f246d632d
files libirccd-test/irccd/journal_server.cpp libirccd-test/irccd/journal_server.hpp libirccd/irccd/server.hpp tests/CMakeLists.txt tests/cmd-server-cmode/main.cpp tests/cmd-server-cnotice/main.cpp tests/cmd-server-connect/main.cpp tests/cmd-server-disconnect/main.cpp tests/cmd-server-info/main.cpp tests/cmd-server-invite/main.cpp tests/cmd-server-join/main.cpp tests/cmd-server-kick/main.cpp tests/cmd-server-list/main.cpp tests/cmd-server-me/main.cpp tests/cmd-server-message/main.cpp tests/cmd-server-mode/main.cpp tests/cmd-server-nick/main.cpp tests/cmd-server-notice/main.cpp tests/cmd-server-part/main.cpp tests/cmd-server-reconnect/main.cpp tests/cmd-server-topic/main.cpp
diffstat 21 files changed, 762 insertions(+), 898 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-test/irccd/journal_server.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/libirccd-test/irccd/journal_server.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -20,6 +20,13 @@
 
 namespace irccd {
 
+void journal_server::reconnect() noexcept
+{
+    cqueue_.push_back({
+        { "command",    "reconnect" }
+    });
+}
+
 void journal_server::cmode(std::string channel, std::string mode)
 {
     cqueue_.push_back({
--- a/libirccd-test/irccd/journal_server.hpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/libirccd-test/irccd/journal_server.hpp	Sat Nov 25 17:00:24 2017 +0100
@@ -85,6 +85,27 @@
     }
 
     /**
+     * \copydoc server::connect
+     */
+    void connect() noexcept override
+    {
+        // avoid connecting.
+    }
+
+    /**
+     * \copydoc server::connect
+     */
+    void disconnect() noexcept override
+    {
+        // avoid disconnecting.
+    }
+
+    /**
+     * \copydoc server::reconnect
+     */
+    void reconnect() noexcept override;
+
+    /**
      * \copydoc server::cmode
      */
     void cmode(std::string channel, std::string mode) override;
--- a/libirccd/irccd/server.hpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/libirccd/irccd/server.hpp	Sat Nov 25 17:00:24 2017 +0100
@@ -792,6 +792,9 @@
         return jchannels_;
     }
 
+    /**
+     * Start connecting.
+     */
     virtual void connect() noexcept;
 
     /**
--- a/tests/CMakeLists.txt	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/CMakeLists.txt	Sat Nov 25 17:00:24 2017 +0100
@@ -32,23 +32,23 @@
     add_subdirectory(cmd-rule-list)
     add_subdirectory(cmd-rule-move)
     add_subdirectory(cmd-rule-remove)
-#    add_subdirectory(cmd-server-cmode)
-#    add_subdirectory(cmd-server-cnotice)
-#    add_subdirectory(cmd-server-connect)
-#    add_subdirectory(cmd-server-disconnect)
-#    add_subdirectory(cmd-server-info)
-#    add_subdirectory(cmd-server-invite)
-#    add_subdirectory(cmd-server-join)
-#    add_subdirectory(cmd-server-kick)
-#    add_subdirectory(cmd-server-list)
-#    add_subdirectory(cmd-server-me)
-#    add_subdirectory(cmd-server-message)
-#    add_subdirectory(cmd-server-mode)
-#    add_subdirectory(cmd-server-nick)
-#    add_subdirectory(cmd-server-notice)
-#    add_subdirectory(cmd-server-part)
-#    add_subdirectory(cmd-server-reconnect)
-#    add_subdirectory(cmd-server-topic)
+    add_subdirectory(cmd-server-cmode)
+    add_subdirectory(cmd-server-cnotice)
+    #add_subdirectory(cmd-server-connect)
+    add_subdirectory(cmd-server-disconnect)
+    add_subdirectory(cmd-server-info)
+    add_subdirectory(cmd-server-invite)
+    add_subdirectory(cmd-server-join)
+    add_subdirectory(cmd-server-kick)
+    add_subdirectory(cmd-server-list)
+    add_subdirectory(cmd-server-me)
+    add_subdirectory(cmd-server-message)
+    add_subdirectory(cmd-server-mode)
+    add_subdirectory(cmd-server-nick)
+    add_subdirectory(cmd-server-notice)
+    add_subdirectory(cmd-server-part)
+    add_subdirectory(cmd-server-reconnect)
+    add_subdirectory(cmd-server-topic)
 #    add_subdirectory(dynlib_plugin)
 
     add_subdirectory(irc)
--- a/tests/cmd-server-cmode/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-cmode/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-cmode"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_mode;
-
-} // !namespace
+class server_cmode_test : public command_test<server_channel_mode_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerChannelModeTest : public ServerTester {
-public:
-    void cmode(std::string channel, std::string mode)
+    server_cmode_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_mode = mode;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerChannelModeCommandTest : public CommandTester {
-public:
-    ServerChannelModeCommandTest()
-        : CommandTester(std::make_unique<server_channel_mode_command>(),
-                        std::make_unique<ServerChannelModeTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-cmode"      },
-            { "server",     "test"              },
-            { "channel",    "#staff"            },
-            { "mode",       "+c"                }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_cmode_test_suite, server_cmode_test)
 
-TEST_F(ServerChannelModeCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !cmd_channel.empty() && !cmd_mode.empty();
-        });
+    ctl_->send({
+        { "command",    "server-cmode"      },
+        { "server",     "test"              },
+        { "channel",    "#staff"            },
+        { "mode",       "+c"                }
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("+c", cmd_mode);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "cmode");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["mode"].get<std::string>() == "+c");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-cnotice/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-cnotice/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-cnotice"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_message;
-
-} // !namespace
+class server_cnotice_test : public command_test<server_channel_notice_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerChannelNoticeTest : public ServerTester {
-public:
-    virtual void cnotice(std::string channel, std::string message) override
+    server_cnotice_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_message = message;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerChannelNoticeCommandTest : public CommandTester {
-public:
-    ServerChannelNoticeCommandTest()
-        : CommandTester(std::make_unique<server_channel_notice_command>(),
-                        std::make_unique<ServerChannelNoticeTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-cnotice"    },
-            { "server",     "test"              },
-            { "channel",    "#staff"            },
-            { "message",    "silence"           }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_cnotice_test_suite, server_cnotice_test)
 
-TEST_F(ServerChannelNoticeCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !cmd_channel.empty() && !cmd_message.empty();
-        });
+    ctl_->send({
+        { "command",    "server-cnotice"    },
+        { "server",     "test"              },
+        { "channel",    "#staff"            },
+        { "message",    "silence"           }
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("silence", cmd_message);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "cnotice");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["message"].get<std::string>() == "silence");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-connect/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-connect/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,104 +16,88 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
+#define BOOST_TEST_MODULE "server-connect"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
+#include <irccd/server_service.hpp>
 
-namespace {
+#include <command_test.hpp>
 
-nlohmann::json message;
+namespace irccd {
 
-} // !namespace
+BOOST_FIXTURE_TEST_SUITE(server_connect_test_suite, command_test<server_connect_command>)
 
-class ServerConnectCommandTest : public CommandTester {
-public:
-    ServerConnectCommandTest()
-        : CommandTester(std::make_unique<server_connect_command>())
-    {
-        message = nullptr;
+BOOST_AUTO_TEST_CASE(minimal)
+{
+    nlohmann::json result;
 
-        m_irccdctl.client().onMessage.connect([&] (auto message) {
-            ::message = message;
-        });
-    }
-};
+    ctl_->send({
+        { "command",    "server-connect"    },
+        { "name",       "local"             },
+        { "host",       "irc.example.org"   }
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-TEST_F(ServerConnectCommandTest, minimal)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-connect"    },
-            { "name",       "local"             },
-            { "host",       "irc.example.org"   }
-        });
+    wait_for([&] () {
+        return result.is_object();
+    });
 
-        poll([&] () {
-            return message.is_object();
-        });
-
-        auto s = m_irccd.servers().get("local");
+    auto s = daemon_->servers().get("local");
 
-        ASSERT_TRUE(s != nullptr);
-        ASSERT_EQ("local", s->name());
-        ASSERT_EQ("irc.example.org", s->host());
-        ASSERT_EQ(6667U, s->port());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(s);
+    BOOST_TEST(s->name() == "local");
+    BOOST_TEST(s->host() == "irc.example.org");
+    BOOST_TEST(s->port() == 6667U);
 }
 
-TEST_F(ServerConnectCommandTest, full)
+BOOST_AUTO_TEST_CASE(full)
 {
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-connect"    },
-            { "name",       "local2"            },
-            { "host",       "irc.example2.org"  },
-            { "password",   "nonono"            },
-            { "nickname",   "francis"           },
-            { "realname",   "the_francis"       },
-            { "username",   "frc"               },
-            { "ctcpVersion", "ultra bot"        },
-            { "commandChar", "::"               },
-            { "port",       18000               },
-            { "ssl",        true                },
-            { "sslVerify",  true                },
-            { "autoRejoin", true                },
-            { "joinInvite", true                }
-        });
+    nlohmann::json result;
 
-        poll([&] () {
-            return message.is_object();
-        });
-
-        auto s = m_irccd.servers().get("local2");
+    ctl_->send({
+        { "command",    "server-connect"    },
+        { "name",       "local2"            },
+        { "host",       "irc.example2.org"  },
+        { "password",   "nonono"            },
+        { "nickname",   "francis"           },
+        { "realname",   "the_francis"       },
+        { "username",   "frc"               },
+        { "ctcpVersion", "ultra bot"        },
+        { "commandChar", "::"               },
+        { "port",       18000               },
+        { "ssl",        true                },
+        { "sslVerify",  true                },
+        { "autoRejoin", true                },
+        { "joinInvite", true                }
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        ASSERT_TRUE(s != nullptr);
-        ASSERT_EQ("local2", s->name());
-        ASSERT_EQ("irc.example2.org", s->host());
-        ASSERT_EQ(18000U, s->port());
-        ASSERT_EQ("nonono", s->password());
-        ASSERT_EQ("francis", s->nickname());
-        ASSERT_EQ("the_francis", s->realname());
-        ASSERT_EQ("frc", s->username());
-        ASSERT_EQ("::", s->command_char());
-        ASSERT_EQ("ultra bot", s->ctcp_version());
-        ASSERT_TRUE(s->flags() & server::ssl);
-        ASSERT_TRUE(s->flags() & server::ssl_verify);
-        ASSERT_TRUE(s->flags() & server::auto_rejoin);
-        ASSERT_TRUE(s->flags() & server::join_invite);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return result.is_object();
+    });
+
+    auto s = daemon_->servers().get("local2");
+
+    BOOST_TEST(s);
+    BOOST_TEST(s->name() == "local2");
+    BOOST_TEST(s->host() == "irc.example2.org");
+    BOOST_TEST(s->port() == 18000U);
+    BOOST_TEST(s->password() == "nonono");
+    BOOST_TEST(s->nickname() == "francis");
+    BOOST_TEST(s->realname() == "the_francis");
+    BOOST_TEST(s->username() == "frc");
+    BOOST_TEST(s->command_char() == "::");
+    BOOST_TEST(s->ctcp_version() == "ultra bot");
+    BOOST_TEST(s->flags() & server::ssl);
+    BOOST_TEST(s->flags() & server::ssl_verify);
+    BOOST_TEST(s->flags() & server::auto_rejoin);
+    BOOST_TEST(s->flags() & server::join_invite);
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-disconnect/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-disconnect/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,80 +16,70 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
-#include <server.hpp>
+#define BOOST_TEST_MODULE "server-disconnect"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
+
+#include <journal_server.hpp>
+#include <command_test.hpp>
 
-using namespace irccd;
+namespace irccd {
+
+namespace {
 
-class ServerDisconnectCommandTest : public CommandTester {
-public:
-    ServerDisconnectCommandTest()
-        : CommandTester(std::make_unique<server_disconnect_command>())
+class server_disconnect_test : public command_test<server_disconnect_command> {
+protected:
+    server_disconnect_test()
     {
+        daemon_->servers().add(std::make_unique<journal_server>(service_, "s1"));
+        daemon_->servers().add(std::make_unique<journal_server>(service_, "s2"));
     }
 };
 
-TEST_F(ServerDisconnectCommandTest, one)
-{
-    bool response = false;
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_disconnect_test_suite, server_disconnect_test)
 
-    try {
-        m_irccd.servers().add(std::make_unique<ServerTester>("s1"));
-        m_irccd.servers().add(std::make_unique<ServerTester>("s2"));
-        m_irccdctl.client().onMessage.connect([&] (const auto &msg) {
-            auto it = msg.find("command");
+BOOST_AUTO_TEST_CASE(one)
+{
+    nlohmann::json result;
 
-            if (it != msg.end())
-                response = it->is_string() && *it == "server-disconnect";
-        });
-        m_irccdctl.client().request({
-            { "command",    "server-disconnect" },
-            { "server",     "s1"                }
-        });
+    ctl_->send({
+        { "command",    "server-disconnect" },
+        { "server",     "s1"                }
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        poll([&] () { return response; });
+    wait_for([&] () {
+        return result.is_object();
+    });
 
-        ASSERT_TRUE(response);
-        ASSERT_FALSE(m_irccd.servers().has("s1"));
-        ASSERT_TRUE(m_irccd.servers().has("s2"));
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(result["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(!daemon_->servers().has("s1"));
+    BOOST_TEST(daemon_->servers().has("s2"));
 }
 
-TEST_F(ServerDisconnectCommandTest, all)
+BOOST_AUTO_TEST_CASE(all)
 {
-    bool response = false;
-
-    try {
-        m_irccd.servers().add(std::make_unique<ServerTester>("s1"));
-        m_irccd.servers().add(std::make_unique<ServerTester>("s2"));
-        m_irccdctl.client().onMessage.connect([&] (const auto &msg) {
-            auto it = msg.find("command");
+    nlohmann::json result;
 
-            if (it != msg.end())
-                response = it->is_string() && *it == "server-disconnect";
-        });
-        m_irccdctl.client().request({
-            { "command",    "server-disconnect" }
-        });
+    ctl_->send({{"command", "server-disconnect"}});
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        poll([&] () { return response; });
+    wait_for([&] () {
+        return result.is_object();
+    });
 
-        ASSERT_TRUE(response);
-        ASSERT_FALSE(m_irccd.servers().has("s1"));
-        ASSERT_FALSE(m_irccd.servers().has("s2"));
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    BOOST_TEST(result["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(!daemon_->servers().has("s1"));
+    BOOST_TEST(!daemon_->servers().has("s2"));
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-info/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-info/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * main.cpp -- test server-cmode remote command
+ * main.cpp -- test server-info remote command
  *
  * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
  *
@@ -16,93 +16,78 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
+#define BOOST_TEST_MODULE "server-info"
+#include <boost/test/unit_test.hpp>
 
-using namespace irccd;
-
-namespace {
+#include <irccd/server_service.hpp>
 
-nlohmann::json message;
-
-} // !namespace
+#include <journal_server.hpp>
+#include <command_test.hpp>
 
-class ServerInfoCommandTest : public CommandTester {
-public:
-    ServerInfoCommandTest()
-        : CommandTester(std::make_unique<server_info_command>())
-    {
-        message = nullptr;
+namespace irccd {
+
+BOOST_FIXTURE_TEST_SUITE(server_info_test_suite, command_test<server_info_command>)
 
-        m_irccdctl.client().onMessage.connect([&] (auto message) {
-            ::message = message;
-        });
-    }
-};
-
-TEST_F(ServerInfoCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        auto server = std::make_unique<ServerTester>();
+    auto server = std::make_unique<journal_server>(service_, "test");
 
-        server->set_host("example.org");
-        server->set_port(8765);
-        server->set_password("none");
-        server->set_nickname("pascal");
-        server->set_username("psc");
-        server->set_realname("Pascal le grand frere");
-        server->set_ctcp_version("yeah");
-        server->set_command_char("@");
-        server->set_reconnect_tries(80);
-        server->set_ping_timeout(20000);
+    server->set_host("example.org");
+    server->set_port(8765);
+    server->set_password("none");
+    server->set_nickname("pascal");
+    server->set_username("psc");
+    server->set_realname("Pascal le grand frere");
+    server->set_ctcp_version("yeah");
+    server->set_command_char("@");
+    server->set_reconnect_tries(80);
+    server->set_ping_timeout(20000);
+
+    nlohmann::json result;
 
-        m_irccd.servers().add(std::move(server));
-        m_irccdctl.client().request({
-            { "command",    "server-info"       },
-            { "server",     "test"              },
-        });
-
-        poll([&] () {
-            return message.is_object();
-        });
+    daemon_->servers().add(std::move(server));
+    ctl_->send({
+        { "command",    "server-info"       },
+        { "server",     "test"              },
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        ASSERT_TRUE(message.is_object());
-        ASSERT_EQ("example.org", message["host"].get<std::string>());
-        ASSERT_EQ("test", message["name"].get<std::string>());
-        ASSERT_EQ("pascal", message["nickname"].get<std::string>());
-        ASSERT_EQ(8765, message["port"].get<int>());
-        ASSERT_EQ("Pascal le grand frere", message["realname"].get<std::string>());
-        ASSERT_EQ("psc", message["username"].get<std::string>());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return result.is_object();
+    });
+
+    BOOST_TEST(result.is_object());
+    BOOST_TEST(result["host"].get<std::string>() == "example.org");
+    BOOST_TEST(result["name"].get<std::string>() == "test");
+    BOOST_TEST(result["nickname"].get<std::string>() == "pascal");
+    BOOST_TEST(result["port"].get<int>() == 8765);
+    BOOST_TEST(result["realname"].get<std::string>() == "Pascal le grand frere");
+    BOOST_TEST(result["username"].get<std::string>() == "psc");
 }
 
-TEST_F(ServerInfoCommandTest, notfound)
+BOOST_AUTO_TEST_CASE(notfound)
 {
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-info"       },
-            { "server",     "test"              },
-        });
+    nlohmann::json result;
 
-        poll([&] () {
-            return message.is_object();
-        });
+    ctl_->send({
+        { "command",    "server-info"       },
+        { "server",     "test"              },
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        ASSERT_TRUE(message.is_object());
-        ASSERT_FALSE(message["status"]);
-        ASSERT_EQ("server test not found", message["error"].get<std::string>());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return result.is_object();
+    });
+
+    // TODO: error code
+    BOOST_TEST(result.is_object());
+    BOOST_TEST(result.count("error"));
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-invite/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-invite/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-invite"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_target;
-std::string cmd_channel;
-
-} // !namespace
+class server_invite_test : public command_test<server_invite_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerInviteTest : public ServerTester {
-public:
-    void invite(std::string target, std::string channel) override
+    server_invite_test()
     {
-        ::cmd_target = target;
-        ::cmd_channel = channel;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerInviteCommandTest : public CommandTester {
-public:
-    ServerInviteCommandTest()
-        : CommandTester(std::make_unique<server_invite_command>(),
-                        std::make_unique<ServerInviteTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-invite"     },
-            { "server",     "test"              },
-            { "target",     "francis"           },
-            { "channel",    "#music"            }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_invite_test_suite, server_invite_test)
 
-TEST_F(ServerInviteCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !cmd_target.empty() && !cmd_channel.empty();
-        });
+    ctl_->send({
+        { "command",    "server-invite"     },
+        { "server",     "test"              },
+        { "target",     "francis"           },
+        { "channel",    "#music"            }
+    });
 
-        ASSERT_EQ("francis", cmd_target);
-        ASSERT_EQ("#music", cmd_channel);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "invite");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#music");
+    BOOST_TEST(cmd["target"].get<std::string>() == "francis");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-join/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-join/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,83 +16,71 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-join"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_password;
+class server_join_test : public command_test<server_join_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-} // !namespace
-
-class ServerJoinTest : public ServerTester {
-public:
-    void join(std::string channel, std::string password) override
+    server_join_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_password = password;
-    }
-};
-
-class ServerJoinCommandTest : public CommandTester {
-public:
-    ServerJoinCommandTest()
-        : CommandTester(std::make_unique<server_join_command>(),
-                        std::make_unique<ServerJoinTest>())
-    {
-        cmd_channel.clear();
-        cmd_password.clear();
+        daemon_->servers().add(server_);
     }
 };
 
-TEST_F(ServerJoinCommandTest, basic)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-join"       },
-            { "server",     "test"              },
-            { "channel",    "#music"            },
-            { "password",   "plop"              }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_join_test_suite, server_join_test)
 
-        poll([&] () {
-            return !cmd_channel.empty();
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "server-join"       },
+        { "server",     "test"              },
+        { "channel",    "#music"            },
+        { "password",   "plop"              }
+    });
 
-        ASSERT_EQ("#music", cmd_channel);
-        ASSERT_EQ("plop", cmd_password);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "join");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#music");
+    BOOST_TEST(cmd["password"].get<std::string>() == "plop");
 }
 
-TEST_F(ServerJoinCommandTest, nopassword)
+BOOST_AUTO_TEST_CASE(nopassword)
 {
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-join"       },
-            { "server",     "test"              },
-            { "channel",    "#music"            }
-        });
+    ctl_->send({
+        { "command",    "server-join"       },
+        { "server",     "test"              },
+        { "channel",    "#music"            }
+    });
 
-        poll([&] () {
-            return !cmd_channel.empty();
-        });
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
 
-        ASSERT_EQ("#music", cmd_channel);
-        ASSERT_EQ("", cmd_password);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "join");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#music");
+    BOOST_TEST(cmd["password"].get<std::string>() == "");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-kick/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-kick/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,90 +16,75 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-kick"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_target;
-std::string cmd_channel;
-std::string cmd_reason;
+class server_kick_test : public command_test<server_kick_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-} // !namespace
-
-class ServerKickTest : public ServerTester {
-public:
-    void kick(std::string target, std::string channel, std::string reason) override
+    server_kick_test()
     {
-        ::cmd_target = target;
-        ::cmd_channel = channel;
-        ::cmd_reason = reason;
-    }
-};
-
-class ServerKickCommandTest : public CommandTester {
-public:
-    ServerKickCommandTest()
-        : CommandTester(std::make_unique<server_kick_command>(),
-                        std::make_unique<ServerKickTest>())
-    {
-        cmd_target.clear();
-        cmd_channel.clear();
-        cmd_reason.clear();
+        daemon_->servers().add(server_);
     }
 };
 
-TEST_F(ServerKickCommandTest, basic)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-kick"       },
-            { "server",     "test"              },
-            { "target",     "francis"           },
-            { "channel",    "#staff"            },
-            { "reason",     "too noisy"         }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_kick_test_suite, server_kick_test)
 
-        poll([&] () {
-            return !cmd_target.empty() && !cmd_channel.empty();
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "server-kick"       },
+        { "server",     "test"              },
+        { "target",     "francis"           },
+        { "channel",    "#staff"            },
+        { "reason",     "too noisy"         }
+    });
 
-        ASSERT_EQ("francis", cmd_target);
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("too noisy", cmd_reason);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "kick");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["target"].get<std::string>() == "francis");
+    BOOST_TEST(cmd["reason"].get<std::string>() == "too noisy");
 }
 
-TEST_F(ServerKickCommandTest, noreason)
+BOOST_AUTO_TEST_CASE(noreason)
 {
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-kick"       },
-            { "server",     "test"              },
-            { "target",     "francis"           },
-            { "channel",    "#staff"            }
-        });
+    ctl_->send({
+        { "command",    "server-kick"       },
+        { "server",     "test"              },
+        { "target",     "francis"           },
+        { "channel",    "#staff"            }
+    });
 
-        poll([&] () {
-            return !cmd_target.empty() && !cmd_channel.empty();
-        });
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
 
-        ASSERT_EQ("francis", cmd_target);
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("", cmd_reason);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "kick");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["target"].get<std::string>() == "francis");
+    BOOST_TEST(cmd["reason"].get<std::string>() == "");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-list/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-list/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,53 +16,51 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
+#define BOOST_TEST_MODULE "server-list"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-nlohmann::json result;
+class server_list_test : public command_test<server_list_command> {
+protected:
+    server_list_test()
+    {
+        daemon_->servers().add(std::make_unique<journal_server>(service_, "s1"));
+        daemon_->servers().add(std::make_unique<journal_server>(service_, "s2"));
+    }
+};
 
 } // !namespace
 
-class ServerListCommandTest : public CommandTester {
-public:
-    ServerListCommandTest()
-        : CommandTester(std::make_unique<server_list_command>())
-    {
-        m_irccd.servers().add(std::make_unique<ServerTester>("s1"));
-        m_irccd.servers().add(std::make_unique<ServerTester>("s2"));
-        m_irccdctl.client().request({{ "command", "server-list" }});
-        m_irccdctl.client().onMessage.connect([&] (auto result) {
-            ::result = result;
-        });
-    }
-};
+BOOST_FIXTURE_TEST_SUITE(server_list_test_suite, server_list_test)
+
+BOOST_AUTO_TEST_CASE(basic)
+{
+    nlohmann::json result;
 
-TEST_F(ServerListCommandTest, basic)
-{
-    try {
-        poll([&] () {
-            return result.is_object();
-        });
+    ctl_->send({{"command", "server-list"}});
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        ASSERT_TRUE(result.is_object());
-        ASSERT_TRUE(result["list"].is_array());
-        ASSERT_EQ(2U, result["list"].size());
-        ASSERT_EQ("s1", result["list"][0]);
-        ASSERT_EQ("s2", result["list"][1]);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return result.is_object();
+    });
+
+    BOOST_TEST(result.is_object());
+    BOOST_TEST(result["list"].is_array());
+    BOOST_TEST(result["list"].size() == 2U);
+    BOOST_TEST(result["list"][0].get<std::string>() == "s1");
+    BOOST_TEST(result["list"][1].get<std::string>() == "s2");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-me/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-me/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-me"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string target;
-std::string message;
-
-} // !namespace
+class server_me_test : public command_test<server_me_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerMeTest : public ServerTester {
-public:
-    void me(std::string target, std::string message) override
+    server_me_test()
     {
-        ::target = target;
-        ::message = message;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerMeCommandTest : public CommandTester {
-public:
-    ServerMeCommandTest()
-        : CommandTester(std::make_unique<server_me_command>(),
-                        std::make_unique<ServerMeTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-me"         },
-            { "server",     "test"              },
-            { "target",     "jean"              },
-            { "message",    "hello!"            }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_me_test_suite, server_me_test)
 
-TEST_F(ServerMeCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !target.empty() && !message.empty();
-        });
+    ctl_->send({
+        { "command",    "server-me"         },
+        { "server",     "test"              },
+        { "target",     "jean"              },
+        { "message",    "hello!"            }
+    });
 
-        ASSERT_EQ("jean", target);
-        ASSERT_EQ("hello!", message);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "me");
+    BOOST_TEST(cmd["message"].get<std::string>() == "hello!");
+    BOOST_TEST(cmd["target"].get<std::string>() == "jean");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-message/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-message/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-message"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string target;
-std::string message;
-
-} // !namespace
+class server_message_test : public command_test<server_message_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerMessageTest : public ServerTester {
-public:
-    void message(std::string target, std::string message) override
+    server_message_test()
     {
-        ::target = target;
-        ::message = message;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerMessageCommandTest : public CommandTester {
-public:
-    ServerMessageCommandTest()
-        : CommandTester(std::make_unique<server_message_command>(),
-                        std::make_unique<ServerMessageTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-message"    },
-            { "server",     "test"              },
-            { "target",     "jean"              },
-            { "message",    "hello!"            }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_message_test_suite, server_message_test)
 
-TEST_F(ServerMessageCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !target.empty() && !message.empty();
-        });
+    ctl_->send({
+        { "command",    "server-message"    },
+        { "server",     "test"              },
+        { "target",     "#staff"            },
+        { "message",    "plop!"             }
+    });
 
-        ASSERT_EQ("jean", target);
-        ASSERT_EQ("hello!", message);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "message");
+    BOOST_TEST(cmd["message"].get<std::string>() == "plop!");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#staff");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-mode/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-mode/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,56 +16,50 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-mode"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string mode;
-
-} // !namespace
+class server_mode_test : public command_test<server_mode_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerModeTest : public ServerTester {
-public:
-    void mode(std::string mode) override
+    server_mode_test()
     {
-        ::mode = mode;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerModeCommandTest : public CommandTester {
-public:
-    ServerModeCommandTest()
-        : CommandTester(std::make_unique<server_mode_command>(),
-                        std::make_unique<ServerModeTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-mode"   },
-            { "server",     "test"          },
-            { "mode",       "+t"            }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_mode_test_suite, server_mode_test)
 
-TEST_F(ServerModeCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !mode.empty();
-        });
+    ctl_->send({
+        { "command",    "server-mode"   },
+        { "server",     "test"          },
+        { "mode",       "+t"            }
+    });
 
-        ASSERT_EQ("+t", mode);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "mode");
+    BOOST_TEST(cmd["mode"].get<std::string>() == "+t");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-nick/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-nick/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,56 +16,53 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-nick"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string nick;
-
-} // !namespace
+class server_nick_test : public command_test<server_nick_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerNickTest : public ServerTester {
-public:
-    void set_nickname(std::string nick) override
+    server_nick_test()
     {
-        ::nick = nick;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerNickCommandTest : public CommandTester {
-public:
-    ServerNickCommandTest()
-        : CommandTester(std::make_unique<server_nick_command>(),
-                        std::make_unique<ServerNickTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-nick"   },
-            { "server",     "test"          },
-            { "nickname",   "chris"         }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_nick_test_suite, server_nick_test)
+
+BOOST_AUTO_TEST_CASE(basic)
+{
+    nlohmann::json result;
 
-TEST_F(ServerNickCommandTest, basic)
-{
-    try {
-        poll([&] () {
-            return !nick.empty();
-        });
+    ctl_->send({
+        { "command",    "server-nick"   },
+        { "server",     "test"          },
+        { "nickname",   "chris"         }
+    });
+    ctl_->recv([&] (auto, auto msg) {
+        result = msg;
+    });
 
-        ASSERT_EQ("chris", nick);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([&] () {
+        return result.is_object();
+    });
+
+    BOOST_TEST(result.is_object());
+    BOOST_TEST(result.count("error") == 0U);
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-notice/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-notice/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-notice"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_message;
-
-} // !namespace
+class server_notice_test : public command_test<server_notice_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerNoticeTest : public ServerTester {
-public:
-    void notice(std::string channel, std::string message) override
+    server_notice_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_message = message;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerNoticeCommandTest : public CommandTester {
-public:
-    ServerNoticeCommandTest()
-        : CommandTester(std::make_unique<server_notice_command>(),
-                        std::make_unique<ServerNoticeTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-notice" },
-            { "server",     "test"          },
-            { "target",     "#staff"        },
-            { "message",    "quiet!"        }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_notice_test_suite, server_notice_test)
 
-TEST_F(ServerNoticeCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !cmd_channel.empty() && !cmd_message.empty();
-        });
+    ctl_->send({
+        { "command",    "server-notice" },
+        { "server",     "test"          },
+        { "target",     "#staff"        },
+        { "message",    "quiet!"        }
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("quiet!", cmd_message);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "notice");
+    BOOST_TEST(cmd["message"].get<std::string>() == "quiet!");
+    BOOST_TEST(cmd["target"].get<std::string>() == "#staff");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-part/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-part/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -5,7 +5,7 @@
  *
  * 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.
+ * copyright part and this permission part 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
@@ -16,83 +16,71 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-part"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_reason;
+class server_part_test : public command_test<server_part_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-} // !namespace
-
-class ServerPartTest : public ServerTester {
-public:
-    void part(std::string channel, std::string reason) override
+    server_part_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_reason = reason;
-    }
-};
-
-class ServerPartCommandTest : public CommandTester {
-public:
-    ServerPartCommandTest()
-        : CommandTester(std::make_unique<server_part_command>(),
-                        std::make_unique<ServerPartTest>())
-    {
-        cmd_channel.clear();
-        cmd_reason.clear();
+        daemon_->servers().add(server_);
     }
 };
 
-TEST_F(ServerPartCommandTest, basic)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-part"   },
-            { "server",     "test"          },
-            { "channel",    "#staff"        },
-            { "reason",     "too noisy"     }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_part_test_suite, server_part_test)
 
-        poll([&] () {
-            return !cmd_channel.empty();
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "server-part"   },
+        { "server",     "test"          },
+        { "channel",    "#staff"        },
+        { "reason",     "too noisy"     }
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("too noisy", cmd_reason);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "part");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["reason"].get<std::string>() == "too noisy");
 }
 
-TEST_F(ServerPartCommandTest, noreason)
+BOOST_AUTO_TEST_CASE(noreason)
 {
-    try {
-        m_irccdctl.client().request({
-            { "command",    "server-part"   },
-            { "server",     "test"          },
-            { "channel",    "#staff"        }
-        });
+    ctl_->send({
+        { "command",    "server-part"   },
+        { "server",     "test"          },
+        { "channel",    "#staff"        }
+    });
 
-        poll([&] () {
-            return !cmd_channel.empty();
-        });
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_TRUE(cmd_reason.empty());
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "part");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["reason"].get<std::string>() == "");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-reconnect/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-reconnect/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,86 +16,66 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
-#include <service.hpp>
+#define BOOST_TEST_MODULE "server-reconnect"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-bool s1;
-bool s2;
-
-} // !namespace
+class server_reconnect_test : public command_test<server_reconnect_command> {
+protected:
+    std::shared_ptr<journal_server> server1_{new journal_server(service_, "s1")};
+    std::shared_ptr<journal_server> server2_{new journal_server(service_, "s2")};
 
-class ServerReconnectTest : public ServerTester {
-private:
-    bool &m_ref;
-
-public:
-    inline ServerReconnectTest(std::string name, bool &ref) noexcept
-        : ServerTester(name)
-        , m_ref(ref)
+    server_reconnect_test()
     {
-        m_ref = false;
-    }
-
-    void reconnect() noexcept override
-    {
-        m_ref = true;
-    }
-};
-
-class ServerReconnectCommandTest : public CommandTester {
-public:
-    ServerReconnectCommandTest()
-        : CommandTester(std::make_unique<server_reconnect_command>())
-    {
-        m_irccd.servers().add(std::make_unique<ServerReconnectTest>("s1", s1));
-        m_irccd.servers().add(std::make_unique<ServerReconnectTest>("s2", s2));
+        daemon_->servers().add(server1_);
+        daemon_->servers().add(server2_);
     }
 };
 
-TEST_F(ServerReconnectCommandTest, basic)
-{
-    try {
-        m_irccdctl.client().request({
-            { "command", "server-reconnect" },
-            { "server", "s1" }
-        });
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_reconnect_test_suite, server_reconnect_test)
 
-        poll([&] () {
-            return s1;
-        });
+BOOST_AUTO_TEST_CASE(basic)
+{
+    ctl_->send({
+        { "command",    "server-reconnect"  },
+        { "server",     "s1"                }
+    });
 
-        ASSERT_TRUE(s1);
-        ASSERT_FALSE(s2);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server1_->cqueue().empty();
+    });
+
+    auto cmd1 = server1_->cqueue().back();
+
+    BOOST_TEST(cmd1["command"].get<std::string>() == "reconnect");
+    BOOST_TEST(server2_->cqueue().empty());
 }
 
-TEST_F(ServerReconnectCommandTest, all)
+BOOST_AUTO_TEST_CASE(all)
 {
-    try {
-        m_irccdctl.client().request({{ "command", "server-reconnect" }});
+    ctl_->send({{"command", "server-reconnect"}});
 
-        poll([&] () {
-            return s1 && s2;
-        });
+    wait_for([this] () {
+        return !server1_->cqueue().empty() && !server2_->cqueue().empty();
+    });
 
-        ASSERT_TRUE(s1);
-        ASSERT_TRUE(s2);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    auto cmd1 = server1_->cqueue().back();
+    auto cmd2 = server2_->cqueue().back();
+
+    BOOST_TEST(cmd1["command"].get<std::string>() == "reconnect");
+    BOOST_TEST(cmd2["command"].get<std::string>() == "reconnect");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd
--- a/tests/cmd-server-topic/main.cpp	Sat Nov 25 14:34:20 2017 +0100
+++ b/tests/cmd-server-topic/main.cpp	Sat Nov 25 17:00:24 2017 +0100
@@ -16,60 +16,52 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <command.hpp>
-#include <command-tester.hpp>
-#include <server-tester.hpp>
+#define BOOST_TEST_MODULE "server-topic"
+#include <boost/test/unit_test.hpp>
+
+#include <irccd/server_service.hpp>
 
-using namespace irccd;
+#include <journal_server.hpp>
+#include <command_test.hpp>
+
+namespace irccd {
 
 namespace {
 
-std::string cmd_channel;
-std::string cmd_topic;
-
-} // !namespace
+class server_topic_test : public command_test<server_topic_command> {
+protected:
+    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
 
-class ServerTopicTest : public ServerTester {
-public:
-    void topic(std::string channel, std::string topic) override
+    server_topic_test()
     {
-        ::cmd_channel = channel;
-        ::cmd_topic = topic;
+        daemon_->servers().add(server_);
     }
 };
 
-class ServerTopicCommandTest : public CommandTester {
-public:
-    ServerTopicCommandTest()
-        : CommandTester(std::make_unique<server_topic_command>(),
-                        std::make_unique<ServerTopicTest>())
-    {
-        m_irccdctl.client().request({
-            { "command",    "server-topic"  },
-            { "server",     "test"          },
-            { "channel",    "#staff"        },
-            { "topic",      "new version"   }
-        });
-    }
-};
+} // !namespace
+
+BOOST_FIXTURE_TEST_SUITE(server_topic_test_suite, server_topic_test)
 
-TEST_F(ServerTopicCommandTest, basic)
+BOOST_AUTO_TEST_CASE(basic)
 {
-    try {
-        poll([&] () {
-            return !cmd_channel.empty() && !cmd_topic.empty();
-        });
+    ctl_->send({
+        { "command",    "server-topic"  },
+        { "server",     "test"          },
+        { "channel",    "#staff"        },
+        { "topic",      "new version"   }
+    });
 
-        ASSERT_EQ("#staff", cmd_channel);
-        ASSERT_EQ("new version", cmd_topic);
-    } catch (const std::exception &ex) {
-        FAIL() << ex.what();
-    }
+    wait_for([this] () {
+        return !server_->cqueue().empty();
+    });
+
+    auto cmd = server_->cqueue().back();
+
+    BOOST_TEST(cmd["command"].get<std::string>() == "topic");
+    BOOST_TEST(cmd["channel"].get<std::string>() == "#staff");
+    BOOST_TEST(cmd["topic"].get<std::string>() == "new version");
 }
 
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
+BOOST_AUTO_TEST_SUITE_END()
 
-    return RUN_ALL_TESTS();
-}
+} // !irccd