changeset 735:64839725f346

Tests: replace journal_server with mock_server
author David Demelier <markand@malikania.fr>
date Tue, 24 Jul 2018 22:00:00 +0200
parents 649bb151f40d
children 49b7c7660a00
files libirccd-test/CMakeLists.txt libirccd-test/irccd/test/journal_server.cpp libirccd-test/irccd/test/journal_server.hpp libirccd-test/irccd/test/js_test.hpp libirccd-test/irccd/test/mock.cpp libirccd-test/irccd/test/mock.hpp libirccd-test/irccd/test/mock_server.cpp libirccd-test/irccd/test/mock_server.hpp libirccd-test/irccd/test/plugin_test.cpp libirccd-test/irccd/test/plugin_test.hpp tests/src/libirccd/command-server-connect/main.cpp tests/src/libirccd/command-server-disconnect/main.cpp tests/src/libirccd/command-server-info/main.cpp tests/src/libirccd/command-server-invite/main.cpp tests/src/libirccd/command-server-join/main.cpp tests/src/libirccd/command-server-kick/main.cpp tests/src/libirccd/command-server-list/main.cpp tests/src/libirccd/command-server-me/main.cpp tests/src/libirccd/command-server-message/main.cpp tests/src/libirccd/command-server-mode/main.cpp tests/src/libirccd/command-server-nick/main.cpp tests/src/libirccd/command-server-notice/main.cpp tests/src/libirccd/command-server-part/main.cpp tests/src/libirccd/command-server-reconnect/main.cpp tests/src/libirccd/command-server-topic/main.cpp tests/src/plugins/ask/main.cpp tests/src/plugins/auth/main.cpp tests/src/plugins/hangman/main.cpp tests/src/plugins/history/main.cpp tests/src/plugins/joke/main.cpp tests/src/plugins/plugin/main.cpp tests/src/plugins/tictactoe/main.cpp
diffstat 32 files changed, 816 insertions(+), 869 deletions(-) [+]
line wrap: on
line diff
--- a/libirccd-test/CMakeLists.txt	Tue Jul 24 21:45:00 2018 +0200
+++ b/libirccd-test/CMakeLists.txt	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,8 @@
     ${libirccd-test_SOURCE_DIR}/irccd/test/cli_test.hpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/command_test.hpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.hpp
-    ${libirccd-test_SOURCE_DIR}/irccd/test/journal_server.hpp
+    ${libirccd-test_SOURCE_DIR}/irccd/test/mock.hpp
+    ${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.hpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/plugin_cli_test.hpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/rule_cli_test.hpp
     $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/plugin_test.hpp>
@@ -34,7 +35,8 @@
     SOURCES
     ${libirccd-test_SOURCE_DIR}/irccd/test/cli_test.cpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.cpp
-    ${libirccd-test_SOURCE_DIR}/irccd/test/journal_server.cpp
+    ${libirccd-test_SOURCE_DIR}/irccd/test/mock.cpp
+    ${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.cpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/plugin_cli_test.cpp
     ${libirccd-test_SOURCE_DIR}/irccd/test/rule_cli_test.cpp
     $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/plugin_test.cpp>
--- a/libirccd-test/irccd/test/journal_server.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-/*
- * journal_server.cpp -- journaled server that logs every command
- *
- * Copyright (c) 2013-2018 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 "journal_server.hpp"
-
-namespace irccd {
-
-void journal_server::connect(connect_handler) noexcept
-{
-    cqueue_.push_back({
-        { "command",    "connect"               },
-    });
-}
-
-void journal_server::disconnect() noexcept
-{
-    cqueue_.push_back({
-        { "command",    "disconnect"            },
-    });
-}
-
-void journal_server::invite(std::string_view target, std::string_view channel)
-{
-    cqueue_.push_back({
-        { "command",    "invite"                },
-        { "target",     std::string(target)     },
-        { "channel",    std::string(channel)    }
-    });
-}
-
-void journal_server::join(std::string_view channel, std::string_view password)
-{
-    cqueue_.push_back({
-        { "command",    "join"                  },
-        { "channel",    std::string(channel)    },
-        { "password",   std::string(password)   }
-    });
-}
-
-void journal_server::kick(std::string_view target, std::string_view channel, std::string_view reason)
-{
-    cqueue_.push_back({
-        { "command",    "kick"                  },
-        { "target",     std::string(target)     },
-        { "channel",    std::string(channel)    },
-        { "reason",     std::string(reason)     }
-    });
-}
-
-void journal_server::me(std::string_view target, std::string_view message)
-{
-    cqueue_.push_back({
-        { "command",    "me"                    },
-        { "target",     std::string(target)     },
-        { "message",    std::string(message)    }
-    });
-}
-
-void journal_server::message(std::string_view target, std::string_view message)
-{
-    cqueue_.push_back({
-        { "command",    "message"               },
-        { "target",     std::string(target)     },
-        { "message",    std::string(message)    }
-    });
-}
-
-void journal_server::mode(std::string_view channel,
-                          std::string_view mode,
-                          std::string_view limit,
-                          std::string_view user,
-                          std::string_view mask)
-{
-    cqueue_.push_back({
-        { "command",    "mode"                  },
-        { "channel",    std::string(channel)    },
-        { "mode",       std::string(mode)       },
-        { "limit",      std::string(limit)      },
-        { "user",       std::string(user)       },
-        { "mask",       std::string(mask)       }
-    });
-}
-
-void journal_server::names(std::string_view channel)
-{
-    cqueue_.push_back({
-        { "command",    "names"                 },
-        { "channel",    std::string(channel)    }
-    });
-}
-
-void journal_server::notice(std::string_view target, std::string_view message)
-{
-    cqueue_.push_back({
-        { "command",    "notice"                },
-        { "target",     std::string(target)     },
-        { "message",    std::string(message)    }
-    });
-}
-
-void journal_server::part(std::string_view channel, std::string_view reason)
-{
-    cqueue_.push_back({
-        { "command",    "part"                  },
-        { "channel",    std::string(channel)    },
-        { "reason",     std::string(reason)     }
-    });
-}
-
-void journal_server::send(std::string_view raw)
-{
-    cqueue_.push_back({
-        { "command",    "send"                  },
-        { "raw",        std::string(raw)        }
-    });
-}
-
-void journal_server::topic(std::string_view channel, std::string_view topic)
-{
-    cqueue_.push_back({
-        { "command",    "topic"                 },
-        { "channel",    std::string(channel)    },
-        { "topic",      std::string(topic)      }
-    });
-}
-
-void journal_server::whois(std::string_view target)
-{
-    cqueue_.push_back({
-        { "command",    "whois"                 },
-        { "target",     std::string(target)     }
-    });
-}
-
-} // !irccd
--- a/libirccd-test/irccd/test/journal_server.hpp	Tue Jul 24 21:45:00 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * journal_server.hpp -- journaled server that logs every command
- *
- * Copyright (c) 2013-2018 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 IRCCD_TEST_JOURNAL_SERVER_HPP
-#define IRCCD_TEST_JOURNAL_SERVER_HPP
-
-#include <deque>
-
-#include <json.hpp>
-
-#include <irccd/daemon/server.hpp>
-
-/**
- * \file journal_server.hpp
- * \brief Journaled server that logs every command.
- */
-
-namespace irccd {
-
-/**
- * \brief Journaled server that logs every command.
- *
- * This class is used for unit testing, it logs every user command such as
- * message, invite.
- *
- * Each command store exactly the function name, parameter names in a json
- * object in a FIFO queue, don't forget to clear that queue when you don't need
- * it anymore.
- *
- * Example with message:
- *
- * ````json
- * {
- *     "command": "message",
- *     "target": "<argument value>",
- *     "message": "<argument value>"
- * }
- * ````
- *
- * \see cqueue()
- */
-class journal_server : public server {
-private:
-    std::deque<nlohmann::json> cqueue_;
-
-public:
-    /**
-     * Inherited constructors.
-     */
-    using server::server;
-
-    /**
-     * Access the command queue.
-     *
-     * \return the queue
-     */
-    inline const std::deque<nlohmann::json>& cqueue() const noexcept
-    {
-        return cqueue_;
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \return the queue
-     */
-    inline std::deque<nlohmann::json>& cqueue() noexcept
-    {
-        return cqueue_;
-    }
-
-    /**
-     * \copydoc server::connect
-     */
-    void connect(connect_handler handler) noexcept override;
-    /**
-     * \copydoc server::disconnect
-     */
-
-    void disconnect() noexcept override;
-
-    /**
-     * \copydoc server::invite
-     */
-    void invite(std::string_view target, std::string_view channel) override;
-
-    /**
-     * \copydoc server::join
-     */
-    void join(std::string_view channel, std::string_view password = "") override;
-
-    /**
-     * \copydoc server::kick
-     */
-    void kick(std::string_view target, std::string_view channel, std::string_view reason = "") override;
-
-    /**
-     * \copydoc server::me
-     */
-    void me(std::string_view target, std::string_view message) override;
-
-    /**
-     * \copydoc server::message
-     */
-    void message(std::string_view target, std::string_view message) override;
-
-    /**
-     * \copydoc server::mode
-     */
-    void mode(std::string_view channel,
-              std::string_view mode,
-              std::string_view limit = "",
-              std::string_view user = "",
-              std::string_view mask = "") override;
-
-    /**
-     * \copydoc server::names
-     */
-    void names(std::string_view channel) override;
-
-    /**
-     * \copydoc server::notice
-     */
-    void notice(std::string_view target, std::string_view message) override;
-
-    /**
-     * \copydoc server::part
-     */
-    void part(std::string_view channel, std::string_view reason = "") override;
-
-    /**
-     * \copydoc server::send
-     */
-    void send(std::string_view raw) override;
-
-    /**
-     * \copydoc server::topic
-     */
-    void topic(std::string_view channel, std::string_view topic) override;
-
-    /**
-     * \copydoc server::whois
-     */
-    void whois(std::string_view target) override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_TEST_JOURNAL_SERVER_HPP
--- a/libirccd-test/irccd/test/js_test.hpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/libirccd-test/irccd/test/js_test.hpp	Tue Jul 24 22:00:00 2018 +0200
@@ -32,7 +32,7 @@
 #include <irccd/js/js_plugin.hpp>
 #include <irccd/js/irccd_jsapi.hpp>
 
-#include "journal_server.hpp"
+#include "mock_server.hpp"
 
 namespace irccd {
 
@@ -48,11 +48,11 @@
     template <typename M1, typename M2, typename... Tail>
     void add();
 
-public:
+protected:
     boost::asio::io_service service_;
     irccd irccd_{service_};                     //!< Irccd instance.
     std::shared_ptr<js_plugin> plugin_;         //!< Javascript plugin.
-    std::shared_ptr<journal_server> server_;    //!< A journal server.
+    std::shared_ptr<mock_server> server_;       //!< A mock server.
 
     /**
      * Constructor.
@@ -80,7 +80,7 @@
 template <typename... Modules>
 js_test<Modules...>::js_test(const std::string& plugin_path)
     : plugin_(new js_plugin("test", plugin_path))
-    , server_(new journal_server(service_, "test"))
+    , server_(new mock_server(service_, "test", "localhost"))
 {
     irccd_.set_log(std::make_unique<logger::silent_sink>());
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/mock.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -0,0 +1,51 @@
+/*
+ * mock.cpp -- keep track of function invocations
+ *
+ * Copyright (c) 2013-2018 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 "mock.hpp"
+
+namespace irccd {
+
+void mock::push(std::string name, args args)
+{
+    table_[name].push_back(std::move(args));
+}
+
+auto mock::find(const std::string& name) -> std::vector<args>
+{
+    if (const auto it = table_.find(name); it != table_.end())
+        return it->second;
+
+    return {};
+}
+
+void mock::clear(const std::string& name) noexcept
+{
+    table_.erase(name);
+}
+
+void mock::clear() noexcept
+{
+    table_.clear();
+}
+
+auto mock::empty() const noexcept -> bool
+{
+    return table_.empty();
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/mock.hpp	Tue Jul 24 22:00:00 2018 +0200
@@ -0,0 +1,92 @@
+/*
+ * mock.hpp -- keep track of function invocations
+ *
+ * Copyright (c) 2013-2018 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 IRCCD_TEST_MOCK_HPP
+#define IRCCD_TEST_MOCK_HPP
+
+/**
+ * \file mock.hpp
+ * \brief Keep track of function invocations.
+ */
+
+#include <any>
+#include <initializer_list>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace irccd {
+
+/**
+ * \brief Keep track of function invocations.
+ */
+class mock {
+public:
+    /**
+     * \brief Functions arguments.
+     */
+    using args = std::vector<std::any>;
+
+    /**
+     * \brief Map of all functions.
+     */
+    using functions = std::unordered_map<std::string, std::vector<args>>;
+
+private:
+    functions table_;
+
+public:
+    /**
+     * Register a new function invocation.
+     *
+     * \param name the function name
+     * \param args the arguments list
+     */
+    void push(std::string name, args args = {});
+
+    /**
+     * Get all function invocations by name.
+     *
+     * \param name the function name
+     * \return the list of functions and their arguments or empty if not called
+     */
+    auto find(const std::string& name) -> std::vector<args>;
+
+    /**
+     * Clear all function invocations by name.
+     *
+     * \param name the function name
+     */
+    void clear(const std::string& name) noexcept;
+
+    /**
+     * Clear all function invocations.
+     */
+    void clear() noexcept;
+
+    /**
+     * Tells if no functions have been called.
+     *
+     * \return true if no functions have been called
+     */
+    auto empty() const noexcept -> bool;
+};
+
+} // !irccd
+
+#endif // !IRCCD_TEST_MOCK_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/mock_server.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -0,0 +1,103 @@
+/*
+ * mock_server.cpp -- mock server
+ *
+ * Copyright (c) 2013-2018 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 "mock_server.hpp"
+
+namespace irccd {
+
+void mock_server::connect(connect_handler) noexcept
+{
+    push("connect");
+}
+
+void mock_server::disconnect() noexcept
+{
+    push("disconnect");
+}
+
+void mock_server::invite(std::string_view target, std::string_view channel)
+{
+    push("invite", { std::string(target), std::string(channel) });
+}
+
+void mock_server::join(std::string_view channel, std::string_view password)
+{
+    push("join", { std::string(channel), std::string(password) });
+}
+
+void mock_server::kick(std::string_view target, std::string_view channel, std::string_view reason)
+{
+    push("kick", { std::string(target), std::string(channel), std::string(reason) });
+}
+
+void mock_server::me(std::string_view target, std::string_view message)
+{
+    push("me", { std::string(target), std::string(message) });
+}
+
+void mock_server::message(std::string_view target, std::string_view message)
+{
+    push("message", { std::string(target), std::string(message) });
+}
+
+void mock_server::mode(std::string_view channel,
+                       std::string_view mode,
+                       std::string_view limit,
+                       std::string_view user,
+                       std::string_view mask)
+{
+    push("mode", {
+        std::string(channel),
+        std::string(mode),
+        std::string(limit),
+        std::string(user),
+        std::string(mask)
+    });
+}
+
+void mock_server::names(std::string_view channel)
+{
+    push("names", { std::string(channel) });
+}
+
+void mock_server::notice(std::string_view target, std::string_view message)
+{
+    push("notice", { std::string(target), std::string(message) });
+}
+
+void mock_server::part(std::string_view channel, std::string_view reason)
+{
+    push("part", { std::string(channel), std::string(reason) });
+}
+
+void mock_server::send(std::string_view raw)
+{
+    push("send", { std::string(raw) });
+}
+
+void mock_server::topic(std::string_view channel, std::string_view topic)
+{
+    push("topic", { std::string(channel), std::string(topic) });
+}
+
+void mock_server::whois(std::string_view target)
+{
+    push("whois", { std::string(target) });
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd-test/irccd/test/mock_server.hpp	Tue Jul 24 22:00:00 2018 +0200
@@ -0,0 +1,120 @@
+/*
+ * mock_server.hpp -- mock server
+ *
+ * Copyright (c) 2013-2018 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 IRCCD_TEST_MOCK_SERVER_HPP
+#define IRCCD_TEST_MOCK_SERVER_HPP
+
+/**
+ * \file mock_server.hpp
+ * \brief Mock server.
+ */
+
+#include <irccd/daemon/server.hpp>
+
+#include "mock.hpp"
+
+namespace irccd {
+
+/**
+ * \brief Mock server.
+ */
+class mock_server : public server, public mock {
+public:
+    /**
+     * Inherited constructors.
+     */
+    using server::server;
+
+    /**
+     * \copydoc server::connect
+     */
+    void connect(connect_handler handler) noexcept override;
+
+    /**
+     * \copydoc server::disconnect
+     */
+    void disconnect() noexcept override;
+
+    /**
+     * \copydoc server::invite
+     */
+    void invite(std::string_view target, std::string_view channel) override;
+
+    /**
+     * \copydoc server::join
+     */
+    void join(std::string_view channel, std::string_view password = "") override;
+
+    /**
+     * \copydoc server::kick
+     */
+    void kick(std::string_view target, std::string_view channel, std::string_view reason = "") override;
+
+    /**
+     * \copydoc server::me
+     */
+    void me(std::string_view target, std::string_view message) override;
+
+    /**
+     * \copydoc server::message
+     */
+    void message(std::string_view target, std::string_view message) override;
+
+    /**
+     * \copydoc server::mode
+     */
+    void mode(std::string_view channel,
+              std::string_view mode,
+              std::string_view limit = "",
+              std::string_view user = "",
+              std::string_view mask = "") override;
+
+    /**
+     * \copydoc server::names
+     */
+    void names(std::string_view channel) override;
+
+    /**
+     * \copydoc server::notice
+     */
+    void notice(std::string_view target, std::string_view message) override;
+
+    /**
+     * \copydoc server::part
+     */
+    void part(std::string_view channel, std::string_view reason = "") override;
+
+    /**
+     * \copydoc server::send
+     */
+    void send(std::string_view raw) override;
+
+    /**
+     * \copydoc server::topic
+     */
+    void topic(std::string_view channel, std::string_view topic) override;
+
+    /**
+     * \copydoc server::whois
+     */
+    void whois(std::string_view target) override;
+};
+
+} // !irccd
+
+#endif // !IRCCD_TEST_MOCK_SERVER_HPP
--- a/libirccd-test/irccd/test/plugin_test.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/libirccd-test/irccd/test/plugin_test.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -41,7 +41,7 @@
 namespace irccd {
 
 plugin_test::plugin_test(std::string path)
-    : server_(std::make_shared<journal_server>(service_, "test", "local"))
+    : server_(std::make_shared<mock_server>(service_, "test", "local"))
 {
     plugin_ = std::make_unique<js_plugin>("test", std::move(path));
 
@@ -51,7 +51,7 @@
     irccd_.servers().add(server_);
 
     server_->set_nickname("irccd");
-    server_->cqueue().clear();
+    server_->clear();
 
     irccd_jsapi().load(irccd_, plugin_);
     directory_jsapi().load(irccd_, plugin_);
--- a/libirccd-test/irccd/test/plugin_test.hpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/libirccd-test/irccd/test/plugin_test.hpp	Tue Jul 24 22:00:00 2018 +0200
@@ -24,13 +24,11 @@
  * \brief test fixture helper for Javascript plugins.
  */
 
-#include <boost/asio/io_service.hpp>
-
 #include <irccd/daemon/irccd.hpp>
 
 #include <irccd/js/js_plugin.hpp>
 
-#include "journal_server.hpp"
+#include "mock_server.hpp"
 
 namespace irccd {
 
@@ -44,7 +42,7 @@
     boost::asio::io_service service_;
     irccd irccd_{service_};
     std::shared_ptr<js_plugin> plugin_;
-    std::shared_ptr<journal_server> server_;
+    std::shared_ptr<mock_server> server_;
 
 public:
     /**
@@ -57,4 +55,4 @@
 
 } // !irccd
 
-#endif // !IRCCD_TEST_PLUGIN_TESTER_HPP
+#endif // !IRCCD_TEST_PLUGIN_TEST_HPP
--- a/tests/src/libirccd/command-server-connect/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-connect/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -92,7 +92,7 @@
 
 BOOST_AUTO_TEST_CASE(already_exists)
 {
-    daemon_->servers().add(std::make_unique<journal_server>(service_, "local"));
+    daemon_->servers().add(std::make_unique<mock_server>(service_, "local"));
 
     const auto result = request({
         { "command",    "server-connect"    },
--- a/tests/src/libirccd/command-server-disconnect/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-disconnect/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -22,29 +22,24 @@
 #include <irccd/daemon/command/server_disconnect_command.hpp>
 #include <irccd/daemon/service/server_service.hpp>
 
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 #include <irccd/test/command_test.hpp>
 
 namespace irccd {
 
 namespace {
 
-class no_disconnect_server : public journal_server {
-public:
-    using journal_server::journal_server;
-
-    void disconnect() noexcept override
-    {
-        // do nothing.
-    }
-};
-
 class server_disconnect_test : public command_test<server_disconnect_command> {
 protected:
+    std::shared_ptr<mock_server> s1_;
+    std::shared_ptr<mock_server> s2_;
+
     server_disconnect_test()
+        : s1_(new mock_server(service_, "s1", "localhost"))
+        , s2_(new mock_server(service_, "s2", "localhost"))
     {
-        daemon_->servers().add(std::make_unique<no_disconnect_server>(service_, "s1"));
-        daemon_->servers().add(std::make_unique<no_disconnect_server>(service_, "s2"));
+        daemon_->servers().add(s1_);
+        daemon_->servers().add(s2_);
     }
 };
 
@@ -58,6 +53,7 @@
     });
 
     BOOST_TEST(result.first["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(s1_->find("disconnect").size() == 1U);
     BOOST_TEST(!daemon_->servers().has("s1"));
     BOOST_TEST(daemon_->servers().has("s2"));
 }
@@ -69,6 +65,8 @@
     });
 
     BOOST_TEST(result.first["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(s1_->find("disconnect").size() == 1U);
+    BOOST_TEST(s2_->find("disconnect").size() == 1U);
     BOOST_TEST(!daemon_->servers().has("s1"));
     BOOST_TEST(!daemon_->servers().has("s2"));
 }
--- a/tests/src/libirccd/command-server-info/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-info/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -33,7 +33,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    auto server = std::make_unique<journal_server>(service_, "test", "example.org");
+    auto server = std::make_unique<mock_server>(service_, "test", "example.org");
 
     server->set_port(8765);
     server->set_password("none");
--- a/tests/src/libirccd/command-server-invite/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-invite/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_invite_test : public command_test<server_invite_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_invite_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,22 +45,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-invite"     },
         { "server",     "test"              },
         { "target",     "francis"           },
         { "channel",    "#music"            }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("invite").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "francis");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "#music");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-join/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-join/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_join_test : public command_test<server_join_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_join_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,22 +45,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-join"       },
         { "server",     "test"              },
         { "channel",    "#music"            },
         { "password",   "plop"              }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("join").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#music");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "plop");
 }
 
 BOOST_AUTO_TEST_CASE(nopassword)
@@ -70,11 +66,10 @@
         { "channel",    "#music"            }
     });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("join").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "join");
-    BOOST_TEST(cmd["channel"].get<std::string>() == "#music");
-    BOOST_TEST(cmd["password"].get<std::string>() == "");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#music");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-kick/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-kick/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_kick_test : public command_test<server_kick_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_kick_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-kick"       },
         { "server",     "test"              },
         { "target",     "francis"           },
@@ -52,37 +53,27 @@
         { "reason",     "too noisy"         }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("kick").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "francis");
+    BOOST_TEST(std::any_cast<std::string>(cmd[2]) == "too noisy");
 }
 
 BOOST_AUTO_TEST_CASE(noreason)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-kick"       },
         { "server",     "test"              },
         { "target",     "francis"           },
         { "channel",    "#staff"            }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("kick").back();
 
-    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>() == "");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "francis");
+    BOOST_TEST(std::any_cast<std::string>(cmd[2]) == "");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-list/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-list/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -33,8 +33,8 @@
 protected:
     server_list_test()
     {
-        daemon_->servers().add(std::make_unique<journal_server>(service_, "s1"));
-        daemon_->servers().add(std::make_unique<journal_server>(service_, "s2"));
+        daemon_->servers().add(std::make_unique<mock_server>(service_, "s1", "localhost"));
+        daemon_->servers().add(std::make_unique<mock_server>(service_, "s2", "localhost"));
     }
 };
 
--- a/tests/src/libirccd/command-server-me/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-me/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_me_test : public command_test<server_me_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_me_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -45,17 +46,16 @@
 BOOST_AUTO_TEST_CASE(basic)
 {
     const auto result = request({
-        { "command",    "server-me"         },
-        { "server",     "test"              },
-        { "target",     "jean"              },
-        { "message",    "hello!"            }
+        { "command",    "server-me" },
+        { "server",     "test"      },
+        { "target",     "jean"      },
+        { "message",    "hello!"    }
     });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("me").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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "hello!");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-message/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-message/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_message_test : public command_test<server_message_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_message_test()
+        : server_(new mock_server(service_, "test"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -51,11 +52,10 @@
         { "message",    "plop!"             }
     });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "plop!");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-mode/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-mode/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_mode_test : public command_test<server_mode_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_mode_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,22 +45,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    "#irccd"        },
         { "mode",       "+t"            }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("mode").back();
 
-    auto cmd = server_->cqueue().back();
-
-    BOOST_TEST(cmd["command"].get<std::string>() == "mode");
-    BOOST_TEST(cmd["channel"].get<std::string>() == "#irccd");
-    BOOST_TEST(cmd["mode"].get<std::string>() == "+t");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#irccd");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "+t");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-nick/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-nick/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_nick_test : public command_test<server_nick_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_nick_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
--- a/tests/src/libirccd/command-server-notice/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-notice/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_notice_test : public command_test<server_notice_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_notice_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,22 +45,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-notice" },
         { "server",     "test"          },
         { "target",     "#staff"        },
         { "message",    "quiet!"        }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("notice").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "quiet!");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-part/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-part/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_part_test : public command_test<server_part_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_part_test()
+        : server_(new mock_server(service_, "test"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,41 +45,31 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    "#staff"        },
         { "reason",     "too noisy"     }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("part").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "too noisy");
 }
 
 BOOST_AUTO_TEST_CASE(noreason)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    "#staff"        }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("part").back();
 
-    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>() == "");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-reconnect/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-reconnect/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,13 +31,17 @@
 
 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")};
+    std::shared_ptr<mock_server> s1_;
+    std::shared_ptr<mock_server> s2_;
 
     server_reconnect_test()
+        : s1_(new mock_server(service_, "s1", "localhost"))
+        , s2_(new mock_server(service_, "s2", "localhost"))
     {
-        daemon_->servers().add(server1_);
-        daemon_->servers().add(server2_);
+        daemon_->servers().add(s1_);
+        daemon_->servers().add(s2_);
+        s1_->clear();
+        s2_->clear();
     }
 };
 
@@ -45,38 +49,24 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-reconnect"  },
         { "server",     "s1"                }
     });
 
-    wait_for([this] () {
-        return !server1_->cqueue().empty();
-    });
-
-    auto cmd1 = server1_->cqueue().back();
-
-#if 0
-    BOOST_TEST(cmd1["command"].get<std::string>() == "reconnect");
-    BOOST_TEST(server2_->cqueue().empty());
-#endif
+    BOOST_TEST(s1_->find("disconnect").size() == 1U);
+    BOOST_TEST(s1_->find("connect").size() == 1U);
+    BOOST_TEST(s2_->empty());
 }
 
 BOOST_AUTO_TEST_CASE(all)
 {
-    ctl_->write({{"command", "server-reconnect"}});
-
-    wait_for([this] () {
-        return !server1_->cqueue().empty() && !server2_->cqueue().empty();
-    });
+    request({{ "command", "server-reconnect" }});
 
-    auto cmd1 = server1_->cqueue().back();
-    auto cmd2 = server2_->cqueue().back();
-
-#if 0
-    BOOST_TEST(cmd1["command"].get<std::string>() == "reconnect");
-    BOOST_TEST(cmd2["command"].get<std::string>() == "reconnect");
-#endif
+    BOOST_TEST(s1_->find("disconnect").size() == 1U);
+    BOOST_TEST(s1_->find("connect").size() == 1U);
+    BOOST_TEST(s2_->find("disconnect").size() == 1U);
+    BOOST_TEST(s2_->find("connect").size() == 1U);
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/libirccd/command-server-topic/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/libirccd/command-server-topic/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -23,7 +23,7 @@
 #include <irccd/daemon/service/server_service.hpp>
 
 #include <irccd/test/command_test.hpp>
-#include <irccd/test/journal_server.hpp>
+#include <irccd/test/mock_server.hpp>
 
 namespace irccd {
 
@@ -31,12 +31,13 @@
 
 class server_topic_test : public command_test<server_topic_command> {
 protected:
-    std::shared_ptr<journal_server> server_{new journal_server(service_, "test")};
+    std::shared_ptr<mock_server> server_;
 
     server_topic_test()
+        : server_(new mock_server(service_, "test", "localhost"))
     {
         daemon_->servers().add(server_);
-        server_->cqueue().clear();
+        server_->clear();
     }
 };
 
@@ -44,22 +45,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto result = request({
         { "command",    "server-topic"  },
         { "server",     "test"          },
         { "channel",    "#staff"        },
         { "topic",      "new version"   }
     });
 
-    wait_for([this] () {
-        return !server_->cqueue().empty();
-    });
+    const auto cmd = server_->find("topic").back();
 
-    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");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "new version");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
--- a/tests/src/plugins/ask/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/ask/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -54,23 +54,22 @@
     for (int i = 0; i < 1000; ++i) {
         plugin_->handle_command(irccd_, {server_, "tester", "#dummy", ""});
 
-        auto cmd = server_->cqueue().front();
+        const auto cmd = server_->find("message").back();
 
-        BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-        BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#dummy");
+        BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#dummy");
 
-        auto msg = cmd["message"].get<std::string>();
+        const auto msg = std::any_cast<std::string>(cmd[1]);
 
         if (msg == "tester, YES")
             yes = true;
         if (msg == "tester, NO")
             no = true;
 
-        server_->cqueue().clear();
+        server_->clear();
     }
 
-    BOOST_REQUIRE(no);
-    BOOST_REQUIRE(yes);
+    BOOST_TEST(no);
+    BOOST_TEST(yes);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugins/auth/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/auth/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -30,26 +30,26 @@
 
 class auth_test : public plugin_test {
 protected:
-    std::shared_ptr<journal_server> nickserv1_;
-    std::shared_ptr<journal_server> nickserv2_;
-    std::shared_ptr<journal_server> quakenet_;
+    std::shared_ptr<mock_server> nickserv1_;
+    std::shared_ptr<mock_server> nickserv2_;
+    std::shared_ptr<mock_server> quakenet_;
 
 public:
     auth_test()
         : plugin_test(PLUGIN_PATH)
-        , nickserv1_(std::make_shared<journal_server>(service_, "nickserv1"))
-        , nickserv2_(std::make_shared<journal_server>(service_, "nickserv2"))
-        , quakenet_(std::make_shared<journal_server>(service_, "quakenet"))
+        , nickserv1_(std::make_shared<mock_server>(service_, "nickserv1", "localhost"))
+        , nickserv2_(std::make_shared<mock_server>(service_, "nickserv2", "localhost"))
+        , quakenet_(std::make_shared<mock_server>(service_, "quakenet", "localhost"))
     {
         plugin_->set_options({
-            { "nickserv1.type", "nickserv" },
-            { "nickserv1.password", "plopation" },
-            { "nickserv2.type", "nickserv" },
-            { "nickserv2.password", "something" },
-            { "nickserv2.username", "jean" },
-            { "quakenet.type", "quakenet" },
-            { "quakenet.password", "hello" },
-            { "quakenet.username", "mario" }
+            { "nickserv1.type",     "nickserv"      },
+            { "nickserv1.password", "plopation"     },
+            { "nickserv2.type",     "nickserv"      },
+            { "nickserv2.password", "something"     },
+            { "nickserv2.username", "jean"          },
+            { "quakenet.type",      "quakenet"      },
+            { "quakenet.password",  "hello"         },
+            { "quakenet.username",  "mario"         }
         });
         plugin_->handle_load(irccd_);
     }
@@ -59,35 +59,32 @@
 
 BOOST_AUTO_TEST_CASE(nickserv1)
 {
-    plugin_->handle_connect(irccd_, {nickserv1_});
-
-    auto cmd = nickserv1_->cqueue().front();
+    plugin_->handle_connect(irccd_, { nickserv1_ });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "NickServ");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "identify plopation");
+    const auto cmd = nickserv1_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "NickServ");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "identify plopation");
 }
 
 BOOST_AUTO_TEST_CASE(nickserv2)
 {
-    plugin_->handle_connect(irccd_, {nickserv2_});
-
-    auto cmd = nickserv2_->cqueue().front();
+    plugin_->handle_connect(irccd_, { nickserv2_ });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "NickServ");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "identify jean something");
+    const auto cmd = nickserv2_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "NickServ");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "identify jean something");
 }
 
 BOOST_AUTO_TEST_CASE(quakenet)
 {
-    plugin_->handle_connect(irccd_, {quakenet_});
-
-    auto cmd = quakenet_->cqueue().front();
+    plugin_->handle_connect(irccd_, { quakenet_ });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "Q@CServe.quakenet.org");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "AUTH mario hello");
+    const auto cmd = quakenet_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "Q@CServe.quakenet.org");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "AUTH mario hello");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugins/hangman/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/hangman/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -66,64 +66,59 @@
 {
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-
-    auto cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _");
+    auto cmd = server_->find("message").back();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _");
 
     plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
-    cmd = server_->cqueue().back();
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
 
     plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
-    cmd = server_->cqueue().back();
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "asked=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "asked=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s");
 }
 
 BOOST_AUTO_TEST_CASE(dead)
 {
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "a"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "b"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "c"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "d"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "e"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "f"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "g"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "h"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "i"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "j"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "a" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "b" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "c" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "d" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "e" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "f" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "g" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "h" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "i" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "j" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "dead=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "dead=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
 }
 
 BOOST_AUTO_TEST_CASE(found)
 {
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "s" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
 }
 
 BOOST_AUTO_TEST_CASE(start)
@@ -132,69 +127,64 @@
 
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _");
 }
 
 BOOST_AUTO_TEST_CASE(win1)
 {
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "k"});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "y"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "s" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "k" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "y" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
 }
 
 BOOST_AUTO_TEST_CASE(win2)
 {
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", "sky"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "sky" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky");
 }
 
 BOOST_AUTO_TEST_CASE(wrong_letter)
 {
     load();
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "x"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "x" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "wrong-letter=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:x");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "wrong-letter=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:x");
 }
 
 BOOST_AUTO_TEST_CASE(wrongWord)
 {
     load();
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", "cheese"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "cheese" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "wrong-word=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:cheese");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "wrong-word=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:cheese");
 }
 
 BOOST_AUTO_TEST_CASE(collaborative_disabled)
@@ -202,21 +192,19 @@
     // Disable collaborative mode.
     load({{ "collaborative", "false" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "s" });
 
-    auto cmd = server_->cqueue().back();
+    auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s k _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s k _");
 }
 
 BOOST_AUTO_TEST_CASE(collaborative_enabled)
@@ -224,56 +212,50 @@
     // Enable collaborative mode.
     load({{ "collaborative", "true" }});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "s"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "s" });
 
-    auto cmd = server_->cqueue().back();
+    auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k");
 
-    plugin_->handle_message(irccd_, {server_, "francis!francis@localhost", "#hangman", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "francis!francis@localhost", "#hangman", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _");
 }
 
 BOOST_AUTO_TEST_CASE(case_fix_642)
 {
     load();
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#HANGMAN", "s"});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#HANGMAN", "s" });
 
-    auto cmd = server_->cqueue().back();
+    auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _");
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#HaNGMaN", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#HaNGMaN", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k");
 
-    plugin_->handle_message(irccd_, {server_, "francis!francis@localhost", "#hAngmAn", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "francis!francis@localhost", "#hAngmAn", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _");
 }
 
 BOOST_AUTO_TEST_CASE(query)
@@ -281,49 +263,44 @@
     load();
 
     // Query mode is never collaborative.
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "irccd", ""});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "irccd", "" });
 
-    auto cmd = server_->cqueue().back();
+    auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "jean!jean@localhost");
-    BOOST_TEST(cmd["message"].get<std::string>() == "start=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:_ _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean!jean@localhost");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "start=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:_ _ _");
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "irccd", "s"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "irccd", "s" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "jean!jean@localhost");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s _ _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean!jean@localhost");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s _ _");
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "irccd", "k"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "irccd", "k" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "jean!jean@localhost");
-    BOOST_TEST(cmd["message"].get<std::string>() == "found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s k _");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean!jean@localhost");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s k _");
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "irccd", "sky"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "irccd", "sky" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "jean!jean@localhost");
-    BOOST_TEST(cmd["message"].get<std::string>() == "win=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:sky");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean!jean@localhost");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "win=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:sky");
 }
 
 BOOST_AUTO_TEST_CASE(running)
 {
     load();
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#hangman", "y"});
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#hangman", "y" });
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#hangman", "" });
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#hangman");
-    BOOST_TEST(cmd["message"].get<std::string>() == "running=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ y");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#hangman");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "running=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ y");
 }
 
 BOOST_AUTO_TEST_CASE(wordlist_fix_644)
@@ -354,13 +331,13 @@
 
     // 1. Initial game + finish.
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    last = server_->cqueue().back()["message"].get<std::string>().length();
+    last = std::any_cast<std::string>(server_->find("message").back()[1]).length();
     found.insert(last);
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", words[last]});
 
     // 2. Current must not be the last one.
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    current = server_->cqueue().back()["message"].get<std::string>().length();
+    current = std::any_cast<std::string>(server_->find("message").back()[1]).length();
 
     BOOST_TEST(last != current);
     BOOST_TEST(0U == found.count(current));
@@ -371,7 +348,7 @@
 
     // 3. Last word must be the one that is kept into the map.
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#hangman", ""});
-    current = server_->cqueue().back()["message"].get<std::string>().length();
+    current = std::any_cast<std::string>(server_->find("message").back()[1]).length();
 
     BOOST_TEST(last != current);
     BOOST_TEST(0U == found.count(current));
--- a/tests/src/plugins/history/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/history/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -60,30 +60,28 @@
 {
     load({{"file", CMAKE_CURRENT_SOURCE_DIR "/broken-conf.json"}});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#history", "seen francis"});
-
-    auto cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#history", "seen francis" });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "error=history:!history:test:#history:jean!jean@localhost:jean");
+    const auto cmd = server_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "error=history:!history:test:#history:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_CASE(format_seen)
 {
-    const std::regex rule("seen=history:!history:test:#history:destructor!dst@localhost:destructor:jean:\\d{2}:\\d{2}");
+    static const std::regex rule("seen=history:!history:test:#history:destructor!dst@localhost:destructor:jean:\\d{2}:\\d{2}");
 
     remove(CMAKE_CURRENT_BINARY_DIR "/seen.json");
     load({{ "file", CMAKE_CURRENT_BINARY_DIR "/seen.json" }});
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#history", "hello"});
-    plugin_->handle_command(irccd_, {server_, "destructor!dst@localhost", "#history", "seen jean"});
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#history", "hello" });
+    plugin_->handle_command(irccd_, { server_, "destructor!dst@localhost", "#history", "seen jean" });
 
-    auto cmd = server_->cqueue().front();
+    auto cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE(std::regex_match(cmd["message"].get<std::string>(), rule));
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::regex_match(std::any_cast<std::string>(cmd[1]), rule));
 }
 
 BOOST_AUTO_TEST_CASE(format_said)
@@ -93,14 +91,13 @@
     remove(CMAKE_CURRENT_BINARY_DIR "/said.json");
     load({{ "file", CMAKE_CURRENT_BINARY_DIR "/said.json" }});
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#history", "hello"});
-    plugin_->handle_command(irccd_, {server_, "destructor!dst@localhost", "#history", "said jean"});
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#history", "hello" });
+    plugin_->handle_command(irccd_, { server_, "destructor!dst@localhost", "#history", "said jean" });
 
-    auto cmd = server_->cqueue().front();
+    const auto cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE(std::regex_match(cmd["message"].get<std::string>(), rule));
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::regex_match(std::any_cast<std::string>(cmd[1]), rule));
 }
 
 BOOST_AUTO_TEST_CASE(format_unknown)
@@ -108,42 +105,38 @@
     remove(CMAKE_CURRENT_BINARY_DIR "/unknown.json");
     load({{ "file", CMAKE_CURRENT_BINARY_DIR "/unknown.json" }});
 
-    plugin_->handle_message(irccd_, {server_, "jean!jean@localhost", "#history", "hello"});
-    plugin_->handle_command(irccd_, {server_, "destructor!dst@localhost", "#history", "seen nobody"});
+    plugin_->handle_message(irccd_, { server_, "jean!jean@localhost", "#history", "hello" });
+    plugin_->handle_command(irccd_, { server_, "destructor!dst@localhost", "#history", "seen nobody" });
 
-    auto cmd = server_->cqueue().front();
+    const auto cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "unknown=history:!history:test:#history:destructor!dst@localhost:destructor:nobody");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "unknown=history:!history:test:#history:destructor!dst@localhost:destructor:nobody");
 }
 
 BOOST_AUTO_TEST_CASE(fix_642)
 {
-    const std::regex rule("said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:\\d{2}:\\d{2}");
+    static const std::regex rule("said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:\\d{2}:\\d{2}");
 
     remove(CMAKE_CURRENT_BINARY_DIR "/case.json");
     load({{"file", CMAKE_CURRENT_BINARY_DIR "/case.json"}});
 
-    plugin_->handle_message(irccd_, {server_, "JeaN!JeaN@localhost", "#history", "hello"});
+    plugin_->handle_message(irccd_, { server_, "JeaN!JeaN@localhost", "#history", "hello" });
 
     // Full caps.
-    plugin_->handle_command(irccd_, {server_, "destructor!dst@localhost", "#HISTORY", "said JEAN"});
-
-    auto cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "destructor!dst@localhost", "#HISTORY", "said JEAN" });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE(std::regex_match(cmd["message"].get<std::string>(), rule));
+    auto cmd = server_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::regex_match(std::any_cast<std::string>(cmd[1]), rule));
 
     // Random caps.
-    plugin_->handle_command(irccd_, {server_, "destructor!dst@localhost", "#HiSToRy", "said JeaN"});
-
-    cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "destructor!dst@localhost", "#HiSToRy", "said JeaN" });
+    cmd = server_->find("message").back();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#history");
-    BOOST_REQUIRE(std::regex_match(cmd["message"].get<std::string>(), rule));
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#history");
+    BOOST_TEST(std::regex_match(std::any_cast<std::string>(cmd[1]), rule));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugins/joke/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/joke/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -61,29 +61,29 @@
      * bbbb
      */
     std::unordered_map<std::string, int> said{
-        { "aaa", 0 },
-        { "bbbb", 0 }
+        { "aaa",    0 },
+        { "bbbb",   0 }
     };
 
     load();
 
-    auto call = [&] () {
-        plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
+    const auto call = [&] () {
+        plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#joke", "" });
 
-        auto cmd = server_->cqueue().back();
+        const auto cmd = server_->find("message").back();
+        const auto msg = std::any_cast<std::string>(cmd[1]);
 
         // "bbbb" is two lines.
-        if (cmd["message"] == "bbbb") {
-            auto first = server_->cqueue().front();
+        if (msg == "bbbb") {
+            const auto first = server_->find("message").front();
 
-            BOOST_TEST(first["command"].template get<std::string>() == "message");
-            BOOST_TEST(first["target"].template get<std::string>() == "#joke");
-            BOOST_TEST(first["message"].template get<std::string>() == "bbbb");
+            BOOST_TEST(std::any_cast<std::string>(first[0]) == "#joke");
+            BOOST_TEST(std::any_cast<std::string>(first[1]) == "bbbb");
         } else
-            BOOST_TEST(cmd["message"].template get<std::string>() == "aaa");
+            BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "aaa");
 
-        said[cmd["message"].template get<std::string>()] += 1;
-        server_->cqueue().clear();
+        said[msg] += 1;
+        server_->clear();
     };
 
     call();
@@ -106,17 +106,16 @@
         { "a", 0 }
     };
 
-    auto call = [&] () {
-        plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
+    const auto call = [&] () {
+        plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#joke", "" });
 
-        auto cmd = server_->cqueue().back();
+        const auto cmd = server_->find("message").back();
 
-        BOOST_TEST(cmd["command"].template get<std::string>() == "message");
-        BOOST_TEST(cmd["target"].template get<std::string>() == "#joke");
-        BOOST_TEST(cmd["message"].template get<std::string>() == "a");
+        BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#joke");
+        BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "a");
 
-        said[cmd["message"].template get<std::string>()] += 1;
-        server_->cqueue().clear();
+        said[std::any_cast<std::string>(cmd[1])] += 1;
+        server_->clear();
     };
 
     call();
@@ -138,17 +137,16 @@
         { "a", 0 }
     };
 
-    auto call = [&] () {
-        plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
+    const auto call = [&] () {
+        plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#joke", "" });
 
-        auto cmd = server_->cqueue().back();
+        const auto cmd = server_->find("message").back();
 
-        BOOST_TEST(cmd["command"].template get<std::string>() == "message");
-        BOOST_TEST(cmd["target"].template get<std::string>() == "#joke");
-        BOOST_TEST(cmd["message"].template get<std::string>() == "a");
+        BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#joke");
+        BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "a");
 
-        server_->cqueue().clear();
-        said[cmd["message"].template get<std::string>()] += 1;
+        server_->clear();
+        said[std::any_cast<std::string>(cmd[1])] += 1;
     };
 
     call();
@@ -165,26 +163,24 @@
 {
     load({{"file", "doesnotexist.json"}});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
-
-    auto cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#joke", "" });
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#joke");
-    BOOST_TEST(cmd["message"].get<std::string>() == "error=test:#joke:jean!jean@localhost:jean");
+    const auto cmd = server_->find("message").back();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#joke");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "error=test:#joke:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_CASE(not_array)
 {
     load({{"file", CMAKE_CURRENT_SOURCE_DIR "/jokes-not-array.json"}});
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
-
-    auto cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#joke", "" });
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#joke");
-    BOOST_TEST(cmd["message"].get<std::string>() == "error=test:#joke:jean!jean@localhost:jean");
+    const auto cmd = server_->find("message").back();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#joke");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "error=test:#joke:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_CASE(empty)
@@ -193,11 +189,10 @@
 
     plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#joke", ""});
 
-    auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#joke");
-    BOOST_TEST(cmd["message"].get<std::string>() == "error=test:#joke:jean!jean@localhost:jean");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#joke");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "error=test:#joke:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugins/plugin/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/plugin/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -86,50 +86,44 @@
 
 BOOST_AUTO_TEST_CASE(format_usage)
 {
-    nlohmann::json cmd;
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "" });
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", ""});
-    cmd = server_->cqueue().front();
+    auto cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "fail"});
-    cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "fail" });
+    cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "info"});
-    cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "info" });
+    cmd = server_->find("message").front();
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "usage=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_CASE(format_info)
 {
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "info fake"});
-
-    auto cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "info fake" });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "info=plugin:!plugin:test:#staff:jean!jean@localhost:jean:jean:BEER:fake:Fake White Beer 2000:0.0.0.0.0.1");
+    const auto cmd = server_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "info=plugin:!plugin:test:#staff:jean!jean@localhost:jean:jean:BEER:fake:Fake White Beer 2000:0.0.0.0.0.1");
 }
 
 BOOST_AUTO_TEST_CASE(format_not_found)
 {
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "info doesnotexistsihope"});
-
-    auto cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "info doesnotexistsihope" });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "not-found=plugin:!plugin:test:#staff:jean!jean@localhost:jean:doesnotexistsihope");
+    const auto cmd = server_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "not-found=plugin:!plugin:test:#staff:jean!jean@localhost:jean:doesnotexistsihope");
 }
 
 BOOST_AUTO_TEST_CASE(format_too_long)
@@ -137,13 +131,12 @@
     for (int i = 0; i < 100; ++i)
         irccd_.plugins().add(std::make_shared<fake_plugin>(str(format("plugin-n-%1%") % i)));
 
-    plugin_->handle_command(irccd_, {server_, "jean!jean@localhost", "#staff", "list"});
-
-    auto cmd = server_->cqueue().front();
+    plugin_->handle_command(irccd_, { server_, "jean!jean@localhost", "#staff", "list" });
 
-    BOOST_REQUIRE_EQUAL(cmd["command"].get<std::string>(), "message");
-    BOOST_REQUIRE_EQUAL(cmd["target"].get<std::string>(), "#staff");
-    BOOST_REQUIRE_EQUAL(cmd["message"].get<std::string>(), "too-long=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
+    const auto cmd = server_->find("message").front();
+
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "too-long=plugin:!plugin:test:#staff:jean!jean@localhost:jean");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugins/tictactoe/main.cpp	Tue Jul 24 21:45:00 2018 +0200
+++ b/tests/src/plugins/tictactoe/main.cpp	Tue Jul 24 22:00:00 2018 +0200
@@ -46,13 +46,15 @@
         });
     }
 
-    auto next_players() const
+    auto next_players() const -> std::pair<std::string, std::string>
     {
-        if (server_->cqueue().size() == 0)
+        const auto functions = server_->find("message");
+
+        if (functions.size() == 0U)
             throw std::runtime_error("no message");
 
-        const auto cmd = server_->cqueue().back();
-        const auto list = string_util::split(cmd["message"].get<std::string>(), ":");
+        const auto cmd = functions.back();
+        const auto list = string_util::split(std::any_cast<std::string>(cmd[1]), ":");
 
         BOOST_TEST(list.size() == 5U);
         BOOST_TEST(list[0] == "turn=#tictactoe");
@@ -65,8 +67,8 @@
 
     auto start()
     {
-        plugin_->handle_command(irccd_, {server_, "a!a@localhost", "#tictactoe", "b"});
-        plugin_->handle_names(irccd_, {server_, "#tictactoe", {"a", "b"}});
+        plugin_->handle_command(irccd_, { server_, "a!a@localhost", "#tictactoe", "b" });
+        plugin_->handle_names(irccd_, { server_, "#tictactoe", { "a", "b" }});
 
         return next_players();
     }
@@ -83,8 +85,8 @@
         auto players = start();
 
         for (const auto& p : points) {
-            server_->cqueue().clear();
-            plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", p});
+            server_->clear();
+            plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", p });
             players = next_players();
         }
     }
@@ -94,19 +96,17 @@
 
 BOOST_AUTO_TEST_CASE(win)
 {
-    run({"a 1", "b1", "a 2", "b2"});
+    run({ "a 1", "b1", "a 2", "b2" });
 
     const auto players = next_players();
 
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 3"});
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 3" });
 
-    const auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd.is_object());
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
 
-    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+    const auto parts = string_util::split(std::any_cast<std::string>(cmd[1]), ":");
 
     BOOST_TEST(parts.size() == 5U);
     BOOST_TEST(parts[0] == "win=#tictactoe");
@@ -128,15 +128,13 @@
 
     const auto players = next_players();
 
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "b 1"});
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "b 1" });
 
-    const auto cmd = server_->cqueue().back();
+    const auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd.is_object());
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
 
-    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+    const auto parts = string_util::split(std::any_cast<std::string>(cmd[1]), ":");
 
     BOOST_TEST(parts.size() == 5U);
     BOOST_TEST(parts[0] == "draw=#tictactoe");
@@ -150,16 +148,14 @@
 {
     auto players = start();
 
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
-    plugin_->handle_message(irccd_, {server_, players.second, "#tictactoe", "a 1"});
-
-    const auto cmd = server_->cqueue().back();
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 1" });
+    plugin_->handle_message(irccd_, { server_, players.second, "#tictactoe", "a 1" });
 
-    BOOST_TEST(cmd.is_object());
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
+    const auto cmd = server_->find("message").back();
 
-    const auto parts = string_util::split(cmd["message"].get<std::string>(), ":");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
+
+    const auto parts = string_util::split(std::any_cast<std::string>(cmd[1]), ":");
 
     BOOST_TEST(parts[0] == "used=#tictactoe");
     BOOST_TEST(parts[1] == "!tictactoe");
@@ -171,40 +167,35 @@
 
 BOOST_AUTO_TEST_CASE(invalid)
 {
-    nlohmann::json cmd;
+    // empty name (no names)
+    plugin_->handle_command(irccd_, { server_, "jean", "#tictactoe", "" });
 
-    // empty name (no names)
-    plugin_->handle_command(irccd_, {server_, "jean", "#tictactoe", ""});
-    cmd = server_->cqueue().back();
+    auto cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
-    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
 
     // bot name (no names)
-    plugin_->handle_command(irccd_, {server_, "jean", "#tictactoe", "irccd"});
-    cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, "jean", "#tictactoe", "irccd" });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
-    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "invalid=#tictactoe:!tictactoe:jean:jean:tictactoe:test");
 
     // target is origin (no names)
-    plugin_->handle_command(irccd_, {server_, server_->get_nickname(), "#tictactoe", server_->get_nickname()});
-    cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, server_->get_nickname(), "#tictactoe", server_->get_nickname() });
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
-    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
 
     // not existing (names)
-    plugin_->handle_command(irccd_, {server_, server_->get_nickname(), "#tictactoe", server_->get_nickname()});
-    plugin_->handle_names(irccd_, {server_, "#tictactoe", {"a", "b", "c"}});
-    cmd = server_->cqueue().back();
+    plugin_->handle_command(irccd_, { server_, server_->get_nickname(), "#tictactoe", server_->get_nickname() });
+    plugin_->handle_names(irccd_, { server_, "#tictactoe", { "a", "b", "c" }});
+    cmd = server_->find("message").back();
 
-    BOOST_TEST(cmd["command"].get<std::string>() == "message");
-    BOOST_TEST(cmd["target"].get<std::string>() == "#tictactoe");
-    BOOST_TEST(cmd["message"].get<std::string>() == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
+    BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#tictactoe");
+    BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "invalid=#tictactoe:!tictactoe:irccd:irccd:tictactoe:test");
 }
 
 BOOST_AUTO_TEST_CASE(random)
@@ -219,50 +210,50 @@
 
     // Last player turn is the winner.
     while (!a && !b && count++ < 1000000U) {
-        run({"a 1", "b 1", "a 2", "b 2"});
+        run({ "a 1", "b 1", "a 2", "b 2" });
 
         const auto players = next_players();
 
-        if (players.first == std::string("a"))
+        if (players.first == "a")
             a = true;
         else
             b = true;
 
-        plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 3"});
+        plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 3" });
     }
 }
 
 BOOST_AUTO_TEST_CASE(disconnect)
 {
-    auto players = start();
+    const auto players = start();
 
-    plugin_->handle_disconnect(irccd_, {server_});
-    server_->cqueue().clear();
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+    plugin_->handle_disconnect(irccd_, { server_ });
+    server_->clear();
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 1" });
 
-    BOOST_TEST(server_->cqueue().empty());
+    BOOST_TEST(server_->empty());
 }
 
 BOOST_AUTO_TEST_CASE(kick)
 {
-    auto players = start();
+    const auto players = start();
 
-    server_->cqueue().clear();
-    plugin_->handle_kick(irccd_, {server_, "kefka", "#tictactoe", players.first, ""});
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+    server_->clear();
+    plugin_->handle_kick(irccd_, { server_, "kefka", "#tictactoe", players.first, "" });
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 1" });
 
-    BOOST_TEST(server_->cqueue().empty());
+    BOOST_TEST(server_->empty());
 }
 
 BOOST_AUTO_TEST_CASE(part)
 {
-    auto players = start();
+    const auto players = start();
 
-    server_->cqueue().clear();
-    plugin_->handle_part(irccd_, {server_, players.first, "#tictactoe", ""});
-    plugin_->handle_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+    server_->clear();
+    plugin_->handle_part(irccd_, { server_, players.first, "#tictactoe", "" });
+    plugin_->handle_message(irccd_, { server_, players.first, "#tictactoe", "a 1" });
 
-    BOOST_TEST(server_->cqueue().empty());
+    BOOST_TEST(server_->empty());
 }
 
 BOOST_AUTO_TEST_SUITE_END()