changeset 737:190b16cfa848

Tests: improve command tests readability
author David Demelier <markand@malikania.fr>
date Tue, 24 Jul 2018 23:01:00 +0200
parents 49b7c7660a00
children 199f36d4edc8
files tests/src/libirccd/command-plugin-config/main.cpp tests/src/libirccd/command-plugin-info/main.cpp tests/src/libirccd/command-plugin-list/main.cpp tests/src/libirccd/command-plugin-load/main.cpp tests/src/libirccd/command-plugin-reload/main.cpp tests/src/libirccd/command-plugin-unload/main.cpp tests/src/libirccd/command-rule-add/main.cpp tests/src/libirccd/command-rule-edit/main.cpp tests/src/libirccd/command-rule-info/main.cpp tests/src/libirccd/command-rule-list/main.cpp tests/src/libirccd/command-rule-move/main.cpp tests/src/libirccd/command-rule-remove/main.cpp 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/libirccd/dynlib-plugin/main.cpp
diffstat 28 files changed, 791 insertions(+), 862 deletions(-) [+]
line wrap: on
line diff
--- a/tests/src/libirccd/command-plugin-config/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-config/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -58,27 +58,24 @@
 BOOST_AUTO_TEST_CASE(set)
 {
     daemon_->plugins().add(std::make_unique<custom_plugin>());
-    ctl_->write({
+
+    const auto [json, code] = request({
         { "command",    "plugin-config" },
         { "plugin",     "test"          },
         { "variable",   "verbosy"       },
         { "value",      "falsy"         }
     });
 
-    wait_for([&] {
-        return !daemon_->plugins().require("test")->get_options().empty();
-    });
+    const auto config = daemon_->plugins().require("test")->get_options();
 
-    auto config = daemon_->plugins().require("test")->get_options();
-
+    BOOST_TEST(!code);
     BOOST_TEST(!config.empty());
-    BOOST_TEST(config["verbosy"] == "falsy");
+    BOOST_TEST(config.at("verbosy") == "falsy");
 }
 
 BOOST_AUTO_TEST_CASE(get)
 {
     auto plugin = std::make_unique<custom_plugin>();
-    auto json = nlohmann::json();
 
     plugin->set_options({
         { "x1", "10" },
@@ -86,20 +83,20 @@
     });
     daemon_->plugins().add(std::move(plugin));
 
-    auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-config" },
         { "plugin",     "test"          },
         { "variable",   "x1"            }
     });
 
-    BOOST_TEST(result.first["variables"]["x1"].get<std::string>() == "10");
-    BOOST_TEST(result.first["variables"]["x2"].is_null());
+    BOOST_TEST(!code);
+    BOOST_TEST(json["variables"]["x1"].get<std::string>() == "10");
+    BOOST_TEST(json["variables"].count("x2") == 0U);
 }
 
 BOOST_AUTO_TEST_CASE(getall)
 {
     auto plugin = std::make_unique<custom_plugin>();
-    auto json = nlohmann::json();
 
     plugin->set_options({
         { "x1", "10" },
@@ -107,38 +104,39 @@
     });
     daemon_->plugins().add(std::move(plugin));
 
-    auto result = request({
-        { "command", "plugin-config" },
-        { "plugin", "test" }
+    const auto [json, code] = request({
+        { "command",    "plugin-config" },
+        { "plugin",     "test"          }
     });
 
-    BOOST_TEST(result.first["variables"]["x1"].get<std::string>() == "10");
-    BOOST_TEST(result.first["variables"]["x2"].get<std::string>() == "20");
+    BOOST_TEST(!code);
+    BOOST_TEST(json["variables"]["x1"].get<std::string>() == "10");
+    BOOST_TEST(json["variables"]["x2"].get<std::string>() == "20");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-config" }
     });
 
-    BOOST_TEST(result.second == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-config" },
         { "plugin",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-info/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-info/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -65,45 +65,43 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    auto plg = std::make_unique<sample_plugin>();
-    auto response = nlohmann::json();
+    daemon_->plugins().add(std::make_unique<sample_plugin>());
 
-    daemon_->plugins().add(std::move(plg));
-
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-info"       },
         { "plugin",     "test"              },
     });
 
-    BOOST_TEST(result.first["author"].get<std::string>() == "Francis Beaugrand");
-    BOOST_TEST(result.first["license"].get<std::string>() == "GPL");
-    BOOST_TEST(result.first["summary"].get<std::string>() == "Completely useless plugin");
-    BOOST_TEST(result.first["version"].get<std::string>() == "0.0.0.0.0.0.0.0.1-beta5");
+    BOOST_TEST(!code);
+    BOOST_TEST(json["author"].get<std::string>() == "Francis Beaugrand");
+    BOOST_TEST(json["license"].get<std::string>() == "GPL");
+    BOOST_TEST(json["summary"].get<std::string>() == "Completely useless plugin");
+    BOOST_TEST(json["version"].get<std::string>() == "0.0.0.0.0.0.0.0.1-beta5");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-info"   }
     });
 
-    BOOST_TEST(result.second == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-info"   },
         { "plugin",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-list/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-list/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -51,14 +51,14 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto [ result, code ] = request({
+    const auto [json, code] = request({
         { "command", "plugin-list" }
     });
 
     BOOST_TEST(!code);
-    BOOST_TEST(result.is_object());
-    BOOST_TEST(result["list"][0].template get<std::string>() == "t1");
-    BOOST_TEST(result["list"][1].template get<std::string>() == "t2");
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["list"][0].get<std::string>() == "t1");
+    BOOST_TEST(json["list"][1].get<std::string>() == "t2");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-load/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-load/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -114,16 +114,12 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto [json, code] = request({
         { "command",    "plugin-load"   },
         { "plugin",     "test"          }
     });
 
-    wait_for([&] () {
-        return daemon_->plugins().has("test");
-    });
-
-    BOOST_TEST(!daemon_->plugins().all().empty());
+    BOOST_TEST(!code);
     BOOST_TEST(daemon_->plugins().has("test"));
 }
 
@@ -131,49 +127,49 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-load"   }
     });
 
-    BOOST_TEST(result.second == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-load"   },
         { "plugin",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(already_exists)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-load"   },
         { "plugin",     "already"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::already_exists);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::already_exists);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::already_exists);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::already_exists);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(exec_error)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-load"   },
         { "plugin",     "broken"        }
     });
 
-    BOOST_TEST(result.second == plugin_error::exec_error);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::exec_error);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::exec_error);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::exec_error);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-reload/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-reload/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -23,15 +23,14 @@
 #include <irccd/daemon/service/plugin_service.hpp>
 
 #include <irccd/test/command_test.hpp>
+#include <irccd/test/mock.hpp>
 
 namespace irccd {
 
 namespace {
 
-class reloadable_plugin : public plugin {
+class reloadable_plugin : public mock, public plugin {
 public:
-    bool reloaded{false};
-
     reloadable_plugin()
         : plugin("test")
     {
@@ -44,7 +43,7 @@
 
     void handle_reload(irccd&) override
     {
-        reloaded = true;
+        push("handle_reload");
     }
 };
 
@@ -84,53 +83,50 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto [json, code] = request({
         { "command",    "plugin-reload" },
         { "plugin",     "test"          }
     });
 
-    wait_for([&] () {
-        return plugin_->reloaded;
-    });
-
-    BOOST_TEST(plugin_->reloaded);
+    BOOST_TEST(!code);
+    BOOST_TEST(plugin_->find("handle_reload").size() == 1U);
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-reload" }
     });
 
-    BOOST_TEST(result.second == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-reload" },
         { "plugin",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(exec_error)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-reload" },
         { "plugin",     "broken"        }
     });
 
-    BOOST_TEST(result.second == plugin_error::exec_error);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::exec_error);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::exec_error);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::exec_error);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-plugin-unload/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-plugin-unload/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -23,15 +23,14 @@
 #include <irccd/daemon/service/plugin_service.hpp>
 
 #include <irccd/test/command_test.hpp>
+#include <irccd/test/mock.hpp>
 
 namespace irccd {
 
 namespace {
 
-class unloadable_plugin : public plugin {
+class unloadable_plugin : public mock, public plugin {
 public:
-    bool unloaded{false};
-
     unloadable_plugin()
         : plugin("test")
     {
@@ -42,9 +41,9 @@
         return "unload";
     }
 
-    void handle_unload(irccd &) override
+    void handle_unload(irccd&) override
     {
-        unloaded = true;
+        push("handle_unload");
     }
 };
 
@@ -82,53 +81,50 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    ctl_->write({
+    const auto [json, code] = request({
         { "command",    "plugin-unload" },
         { "plugin",     "test"          }
     });
 
-    wait_for([&] () {
-        return plugin_->unloaded;
-    });
-
-    BOOST_TEST(plugin_->unloaded);
+    BOOST_TEST(!code);
+    BOOST_TEST(plugin_->find("handle_unload").size() == 1U);
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-unload" }
     });
 
-    BOOST_TEST(result.second == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-unload" },
         { "plugin",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == plugin_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
 }
 
 BOOST_AUTO_TEST_CASE(exec_error)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "plugin-unload" },
         { "plugin",     "broken"        }
     });
 
-    BOOST_TEST(result.second == plugin_error::exec_error);
-    BOOST_TEST(result.first["error"].template get<int>() == plugin_error::exec_error);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "plugin");
+    BOOST_TEST(code == plugin_error::exec_error);
+    BOOST_TEST(json["error"].get<int>() == plugin_error::exec_error);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "plugin");
     BOOST_TEST(!daemon_->plugins().has("broken"));
 }
 
--- a/tests/src/libirccd/command-rule-add/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-add/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -47,16 +47,17 @@
         { "index",      0                   }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command", "rule-list" }
     });
 
-    BOOST_TEST(result.first.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
 
-    auto servers = result.first["list"][0]["servers"];
-    auto channels = result.first["list"][0]["channels"];
-    auto plugins = result.first["list"][0]["plugins"];
-    auto events = result.first["list"][0]["events"];
+    auto servers = json["list"][0]["servers"];
+    auto channels = json["list"][0]["channels"];
+    auto plugins = json["list"][0]["plugins"];
+    auto events = json["list"][0]["events"];
 
     BOOST_TEST(json_util::contains(servers, "s1"));
     BOOST_TEST(json_util::contains(servers, "s2"));
@@ -65,7 +66,7 @@
     BOOST_TEST(json_util::contains(plugins, "p1"));
     BOOST_TEST(json_util::contains(plugins, "p2"));
     BOOST_TEST(json_util::contains(events, "onMessage"));
-    BOOST_TEST(result.first["list"][0]["action"].get<std::string>() == "accept");
+    BOOST_TEST(json["list"][0]["action"].get<std::string>() == "accept");
 }
 
 BOOST_AUTO_TEST_CASE(append)
@@ -90,39 +91,40 @@
         { "index",      1                   }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command", "rule-list" }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["list"].size() == 2U);
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["list"].size() == 2U);
 
     // Rule 0.
     {
-        auto servers = result.first["list"][0]["servers"];
-        auto channels = result.first["list"][0]["channels"];
-        auto plugins = result.first["list"][0]["plugins"];
-        auto events = result.first["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result.first["list"][0]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "accept");
     }
 
     // Rule 1.
     {
-        auto servers = result.first["list"][1]["servers"];
-        auto channels = result.first["list"][1]["channels"];
-        auto plugins = result.first["list"][1]["plugins"];
-        auto events = result.first["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s2"));
         BOOST_TEST(json_util::contains(channels, "c2"));
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result.first["list"][1]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "drop");
     }
 }
 
@@ -130,14 +132,14 @@
 
 BOOST_AUTO_TEST_CASE(invalid_action)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-add"  },
         { "action",     "unknown"   }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_action);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_action);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_action);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_action);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-rule-edit/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-edit/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -56,22 +56,23 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "new-s3"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["servers"], "new-s3"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(add_channel)
@@ -82,22 +83,23 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "new-c3"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["channels"], "new-c3"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(add_plugin)
@@ -108,22 +110,23 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "new-p3"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "new-p3"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(add_event)
@@ -134,22 +137,23 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onQuery"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json_util::contains(json["events"], "onQuery"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(add_event_and_server)
@@ -161,23 +165,24 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "new-s3"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onQuery"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["servers"], "new-s3"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json_util::contains(json["events"], "onQuery"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(change_action)
@@ -188,21 +193,22 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "accept");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "accept");
 }
 
 BOOST_AUTO_TEST_CASE(remove_server)
@@ -213,21 +219,22 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(!json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(!json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(remove_channel)
@@ -238,21 +245,22 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(!json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(!json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(remove_plugin)
@@ -263,21 +271,22 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(!json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(!json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(remove_event)
@@ -288,21 +297,22 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(!json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(!json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_CASE(remove_event_and_server)
@@ -314,75 +324,76 @@
         { "index",          0               }
     });
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",        "rule-info"     },
         { "index",          0               }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(json_util::contains(result.first["servers"], "s1"));
-    BOOST_TEST(!json_util::contains(result.first["servers"], "s2"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c1"));
-    BOOST_TEST(json_util::contains(result.first["channels"], "c2"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p1"));
-    BOOST_TEST(json_util::contains(result.first["plugins"], "p2"));
-    BOOST_TEST(json_util::contains(result.first["events"], "onMessage"));
-    BOOST_TEST(!json_util::contains(result.first["events"], "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json_util::contains(json["servers"], "s1"));
+    BOOST_TEST(!json_util::contains(json["servers"], "s2"));
+    BOOST_TEST(json_util::contains(json["channels"], "c1"));
+    BOOST_TEST(json_util::contains(json["channels"], "c2"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p1"));
+    BOOST_TEST(json_util::contains(json["plugins"], "p2"));
+    BOOST_TEST(json_util::contains(json["events"], "onMessage"));
+    BOOST_TEST(!json_util::contains(json["events"], "onCommand"));
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_index_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-edit" },
         { "index",      -100        },
         { "action",     "drop"      }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-edit" },
         { "index",      100         },
         { "action",     "drop"      }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_3)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-edit" },
         { "index",      "notaint"   },
         { "action",     "drop"      }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_action)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-edit" },
         { "index",      0           },
         { "action",     "unknown"   }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_action);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_action);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_action);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_action);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-rule-info/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-info/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -57,16 +57,17 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-info" },
         { "index",      0           }
     });
 
-    auto servers = result.first["servers"];
-    auto channels = result.first["channels"];
-    auto plugins = result.first["plugins"];
-    auto events = result.first["events"];
+    auto servers = json["servers"];
+    auto channels = json["channels"];
+    auto plugins = json["plugins"];
+    auto events = json["events"];
 
+    BOOST_TEST(!code);
     BOOST_TEST(json_util::contains(servers, "s1"));
     BOOST_TEST(json_util::contains(servers, "s2"));
     BOOST_TEST(json_util::contains(channels, "c1"));
@@ -75,45 +76,45 @@
     BOOST_TEST(json_util::contains(plugins, "p2"));
     BOOST_TEST(json_util::contains(events, "onMessage"));
     BOOST_TEST(json_util::contains(events, "onCommand"));
-    BOOST_TEST(result.first["action"].get<std::string>() == "drop");
+    BOOST_TEST(json["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_index_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-info" },
         { "index",      -100        }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-info" },
         { "index",      100         }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_3)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-info" },
         { "index",      "notaint"   }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-rule-list/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-list/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -57,20 +57,19 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
-        { "command", "rule-list" }
-    });
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["list"].is_array());
-    BOOST_TEST(result.first["list"].size() == 2U);
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["list"].is_array());
+    BOOST_TEST(json["list"].size() == 2U);
 
     // Rule 0.
     {
-        auto servers = result.first["list"][0]["servers"];
-        auto channels = result.first["list"][0]["channels"];
-        auto plugins = result.first["list"][0]["plugins"];
-        auto events = result.first["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(servers, "s2"));
@@ -80,21 +79,21 @@
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
         BOOST_TEST(json_util::contains(events, "onCommand"));
-        BOOST_TEST(result.first["list"][0]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "drop");
     }
 
     // Rule 1.
     {
-        auto servers = result.first["list"][1]["servers"];
-        auto channels = result.first["list"][1]["channels"];
-        auto plugins = result.first["list"][1]["plugins"];
-        auto events = result.first["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result.first["list"][1]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "accept");
     }
 }
 
@@ -103,13 +102,12 @@
     daemon_->rules().remove(0);
     daemon_->rules().remove(0);
 
-    const auto result = request({
-        { "command", "rule-list" }
-    });
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["list"].is_array());
-    BOOST_TEST(result.first["list"].empty());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["list"].is_array());
+    BOOST_TEST(json["list"].empty());
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-rule-move/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-move/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -66,297 +66,225 @@
 
 BOOST_AUTO_TEST_CASE(backward)
 {
-    nlohmann::json result;
-
-    ctl_->write({
+    request({
         { "command",    "rule-move" },
         { "from",       2           },
         { "to",         0           }
     });
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
 
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    result = nullptr;
-    ctl_->write({{ "command", "rule-list" }});
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
-
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
 
     // Rule 2.
     {
-        auto servers = result["list"][0]["servers"];
-        auto channels = result["list"][0]["channels"];
-        auto plugins = result["list"][0]["plugins"];
-        auto events = result["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s2"));
         BOOST_TEST(json_util::contains(channels, "c2"));
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][0]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "accept");
     }
 
     // Rule 0.
     {
-        auto servers = result["list"][1]["servers"];
-        auto channels = result["list"][1]["channels"];
-        auto plugins = result["list"][1]["plugins"];
-        auto events = result["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s0"));
         BOOST_TEST(json_util::contains(channels, "c0"));
         BOOST_TEST(json_util::contains(plugins, "p0"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][1]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "drop");
     }
 
     // Rule 1.
     {
-        auto servers = result["list"][2]["servers"];
-        auto channels = result["list"][2]["channels"];
-        auto plugins = result["list"][2]["plugins"];
-        auto events = result["list"][2]["events"];
+        auto servers = json["list"][2]["servers"];
+        auto channels = json["list"][2]["channels"];
+        auto plugins = json["list"][2]["plugins"];
+        auto events = json["list"][2]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][2]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][2]["action"].get<std::string>() == "accept");
     }
 }
 
 BOOST_AUTO_TEST_CASE(upward)
 {
-    nlohmann::json result;
-
-    ctl_->write({
+    request({
         { "command",    "rule-move" },
         { "from",       0           },
         { "to",         2           }
     });
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
 
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    result = nullptr;
-    ctl_->write({{ "command", "rule-list" }});
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
-
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
 
     // Rule 1.
     {
-        auto servers = result["list"][0]["servers"];
-        auto channels = result["list"][0]["channels"];
-        auto plugins = result["list"][0]["plugins"];
-        auto events = result["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][0]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "accept");
     }
 
     // Rule 2.
     {
-        auto servers = result["list"][1]["servers"];
-        auto channels = result["list"][1]["channels"];
-        auto plugins = result["list"][1]["plugins"];
-        auto events = result["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s2"));
         BOOST_TEST(json_util::contains(channels, "c2"));
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][1]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "accept");
     }
 
     // Rule 0.
     {
-        auto servers = result["list"][2]["servers"];
-        auto channels = result["list"][2]["channels"];
-        auto plugins = result["list"][2]["plugins"];
-        auto events = result["list"][2]["events"];
+        auto servers = json["list"][2]["servers"];
+        auto channels = json["list"][2]["channels"];
+        auto plugins = json["list"][2]["plugins"];
+        auto events = json["list"][2]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s0"));
         BOOST_TEST(json_util::contains(channels, "c0"));
         BOOST_TEST(json_util::contains(plugins, "p0"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][2]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][2]["action"].get<std::string>() == "drop");
     }
 }
 
 BOOST_AUTO_TEST_CASE(same)
 {
-    nlohmann::json result;
-
-    ctl_->write({
+    request({
         { "command",    "rule-move" },
         { "from",       1           },
         { "to",         1           }
     });
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
 
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    result = nullptr;
-    ctl_->write({{ "command", "rule-list" }});
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
-
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
 
     // Rule 0.
     {
-        auto servers = result["list"][0]["servers"];
-        auto channels = result["list"][0]["channels"];
-        auto plugins = result["list"][0]["plugins"];
-        auto events = result["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s0"));
         BOOST_TEST(json_util::contains(channels, "c0"));
         BOOST_TEST(json_util::contains(plugins, "p0"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][0]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "drop");
     }
 
     // Rule 1.
     {
-        auto servers = result["list"][1]["servers"];
-        auto channels = result["list"][1]["channels"];
-        auto plugins = result["list"][1]["plugins"];
-        auto events = result["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][1]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "accept");
     }
 
     // Rule 2.
     {
-        auto servers = result["list"][2]["servers"];
-        auto channels = result["list"][2]["channels"];
-        auto plugins = result["list"][2]["plugins"];
-        auto events = result["list"][2]["events"];
+        auto servers = json["list"][2]["servers"];
+        auto channels = json["list"][2]["channels"];
+        auto plugins = json["list"][2]["plugins"];
+        auto events = json["list"][2]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s2"));
         BOOST_TEST(json_util::contains(channels, "c2"));
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][2]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][2]["action"].get<std::string>() == "accept");
     }
 }
 
 BOOST_AUTO_TEST_CASE(beyond)
 {
-    nlohmann::json result;
-
-    ctl_->write({
+    request({
         { "command",    "rule-move" },
         { "from",       0           },
         { "to",         123         }
     });
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
 
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    result = nullptr;
-    ctl_->write({{ "command", "rule-list" }});
-    ctl_->read([&] (auto, auto msg) {
-        result = msg;
-    });
-
-    wait_for([&] () {
-        return result.is_object();
-    });
-
-    BOOST_TEST(result.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
 
     // Rule 1.
     {
-        auto servers = result["list"][0]["servers"];
-        auto channels = result["list"][0]["channels"];
-        auto plugins = result["list"][0]["plugins"];
-        auto events = result["list"][0]["events"];
+        auto servers = json["list"][0]["servers"];
+        auto channels = json["list"][0]["channels"];
+        auto plugins = json["list"][0]["plugins"];
+        auto events = json["list"][0]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s1"));
         BOOST_TEST(json_util::contains(channels, "c1"));
         BOOST_TEST(json_util::contains(plugins, "p1"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][0]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][0]["action"].get<std::string>() == "accept");
     }
 
     // Rule 2.
     {
-        auto servers = result["list"][1]["servers"];
-        auto channels = result["list"][1]["channels"];
-        auto plugins = result["list"][1]["plugins"];
-        auto events = result["list"][1]["events"];
+        auto servers = json["list"][1]["servers"];
+        auto channels = json["list"][1]["channels"];
+        auto plugins = json["list"][1]["plugins"];
+        auto events = json["list"][1]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s2"));
         BOOST_TEST(json_util::contains(channels, "c2"));
         BOOST_TEST(json_util::contains(plugins, "p2"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][1]["action"].get<std::string>() == "accept");
+        BOOST_TEST(json["list"][1]["action"].get<std::string>() == "accept");
     }
 
     // Rule 0.
     {
-        auto servers = result["list"][2]["servers"];
-        auto channels = result["list"][2]["channels"];
-        auto plugins = result["list"][2]["plugins"];
-        auto events = result["list"][2]["events"];
+        auto servers = json["list"][2]["servers"];
+        auto channels = json["list"][2]["channels"];
+        auto plugins = json["list"][2]["plugins"];
+        auto events = json["list"][2]["events"];
 
         BOOST_TEST(json_util::contains(servers, "s0"));
         BOOST_TEST(json_util::contains(channels, "c0"));
         BOOST_TEST(json_util::contains(plugins, "p0"));
         BOOST_TEST(json_util::contains(events, "onMessage"));
-        BOOST_TEST(result["list"][2]["action"].get<std::string>() == "drop");
+        BOOST_TEST(json["list"][2]["action"].get<std::string>() == "drop");
     }
 }
 
@@ -364,67 +292,67 @@
 
 BOOST_AUTO_TEST_CASE(invalid_index_1_from)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-move" },
         { "from",       -100        },
         { "to",         0           }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_1_to)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-move" },
         { "from",       0           },
         { "to",         -100        }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_2_from)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-move" },
         { "from",       100         },
         { "to",         0           }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_3_from)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-move" },
         { "from",       "notaint"   },
         { "to",         0           }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_3_to)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-move" },
         { "from",       0           },
         { "to",         "notaint"   }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-rule-remove/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-rule-remove/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -63,17 +63,16 @@
         { "index",      1               }
     });
 
-    const auto result = request({
-        { "command", "rule-list" }
-    });
+    const auto [json, code] = request({{ "command", "rule-list" }});
 
-    BOOST_TEST(result.first["list"].is_array());
-    BOOST_TEST(result.first["list"].size() == 1U);
+    BOOST_TEST(!code);
+    BOOST_TEST(json["list"].is_array());
+    BOOST_TEST(json["list"].size() == 1U);
 
-    auto servers = result.first["list"][0]["servers"];
-    auto channels = result.first["list"][0]["channels"];
-    auto plugins = result.first["list"][0]["plugins"];
-    auto events = result.first["list"][0]["events"];
+    auto servers = json["list"][0]["servers"];
+    auto channels = json["list"][0]["channels"];
+    auto plugins = json["list"][0]["plugins"];
+    auto events = json["list"][0]["events"];
 
     BOOST_TEST(json_util::contains(servers, "s1"));
     BOOST_TEST(json_util::contains(servers, "s2"));
@@ -83,58 +82,45 @@
     BOOST_TEST(json_util::contains(plugins, "p2"));
     BOOST_TEST(json_util::contains(events, "onMessage"));
     BOOST_TEST(json_util::contains(events, "onCommand"));
-    BOOST_TEST(result.first["list"][0]["action"].get<std::string>() == "drop");
-}
-
-BOOST_AUTO_TEST_CASE(empty)
-{
-    daemon_->rules().remove(0);
-    daemon_->rules().remove(0);
-
-    const auto result = request({
-        { "command",    "rule-remove"   },
-        { "index",      1               }
-    });
-
-    BOOST_TEST(result.first.is_object());
+    BOOST_TEST(json["list"][0]["action"].get<std::string>() == "drop");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_index_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-remove"   },
         { "index",      -100            }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-remove"   },
         { "index",      100             }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_index_3)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "rule-remove"   },
         { "index",      "notaint"       }
     });
 
-    BOOST_TEST(result.second == rule_error::invalid_index);
-    BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule");
+    BOOST_TEST(code == rule_error::invalid_index);
+    BOOST_TEST(json["error"].get<int>() == rule_error::invalid_index);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "rule");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-connect/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-connect/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -33,7 +33,7 @@
 
 BOOST_AUTO_TEST_CASE(minimal)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "local"             },
         { "host",       "irc.example.org"   }
@@ -41,6 +41,7 @@
 
     const auto s = daemon_->servers().get("local");
 
+    BOOST_TEST(!code);
     BOOST_TEST(s);
     BOOST_TEST(s->get_id() == "local");
     BOOST_TEST(s->get_host() == "irc.example.org");
@@ -51,7 +52,7 @@
 
 BOOST_AUTO_TEST_CASE(full)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "local2"            },
         { "host",       "irc.example2.org"  },
@@ -70,6 +71,7 @@
 
     const auto s = daemon_->servers().get("local2");
 
+    BOOST_TEST(!code);
     BOOST_TEST(s);
     BOOST_TEST(s->get_id() == "local2");
     BOOST_TEST(s->get_host() == "irc.example2.org");
@@ -94,124 +96,124 @@
 {
     daemon_->servers().add(std::make_unique<mock_server>(service_, "local"));
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "local"             },
         { "host",       "127.0.0.1"         }
     });
 
-    BOOST_TEST(result.second == server_error::already_exists);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::already_exists);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::already_exists);
+    BOOST_TEST(json["error"].get<int>() == server_error::already_exists);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_hostname_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
     });
 
-    BOOST_TEST(result.second == server_error::invalid_hostname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_hostname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_hostname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_hostname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_hostname_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
         { "host",       123456              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_hostname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_hostname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_hostname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_hostname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       ""                  },
         { "host",       "127.0.0.1"         }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       123456              },
         { "host",       "127.0.0.1"         }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_port_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
         { "host",       "127.0.0.1"         },
         { "port",       "notaint"           }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_port);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_port);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_port);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_port);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_port_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
         { "host",       "127.0.0.1"         },
         { "port",       -123                }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_port);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_port);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_port);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_port);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_port_3)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
         { "host",       "127.0.0.1"         },
         { "port",       1000000             }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_port);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_port);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_port);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_port);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 #if !defined(IRCCD_HAVE_SSL)
 
 BOOST_AUTO_TEST_CASE(ssl_disabled)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-connect"    },
         { "name",       "new"               },
         { "host",       "127.0.0.1"         },
         { "ssl",        true                }
     });
 
-    BOOST_TEST(result.second == server_error::ssl_disabled);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::ssl_disabled);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::ssl_disabled);
+    BOOST_TEST(json["error"].get<int>() == server_error::ssl_disabled);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 #endif
--- a/tests/src/libirccd/command-server-disconnect/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-disconnect/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -47,12 +47,13 @@
 
 BOOST_AUTO_TEST_CASE(one)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-disconnect" },
         { "server",     "s1"                }
     });
 
-    BOOST_TEST(result.first["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(!code);
+    BOOST_TEST(json["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"));
@@ -60,11 +61,10 @@
 
 BOOST_AUTO_TEST_CASE(all)
 {
-    const auto result = request({
-        { "command", "server-disconnect" }
-    });
+    const auto [json, code] = request({{ "command", "server-disconnect" }});
 
-    BOOST_TEST(result.first["command"].get<std::string>() == "server-disconnect");
+    BOOST_TEST(!code);
+    BOOST_TEST(json["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"));
@@ -75,26 +75,26 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-disconnect" },
         { "server",     123456              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-disconnect" },
         { "server",     "unknown"           }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-info/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-info/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -46,56 +46,57 @@
 
     daemon_->servers().add(std::move(server));
 
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-info"       },
         { "server",     "test"              },
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["host"].get<std::string>() == "example.org");
-    BOOST_TEST(result.first["name"].get<std::string>() == "test");
-    BOOST_TEST(result.first["nickname"].get<std::string>() == "pascal");
-    BOOST_TEST(result.first["port"].get<int>() == 8765);
-    BOOST_TEST(result.first["realname"].get<std::string>() == "Pascal le grand frere");
-    BOOST_TEST(result.first["username"].get<std::string>() == "psc");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["host"].get<std::string>() == "example.org");
+    BOOST_TEST(json["name"].get<std::string>() == "test");
+    BOOST_TEST(json["nickname"].get<std::string>() == "pascal");
+    BOOST_TEST(json["port"].get<int>() == 8765);
+    BOOST_TEST(json["realname"].get<std::string>() == "Pascal le grand frere");
+    BOOST_TEST(json["username"].get<std::string>() == "psc");
 }
 
 BOOST_AUTO_TEST_SUITE(errors)
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-info"   },
         { "server",     123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-info"   },
         { "server",     ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-info"   },
         { "server",     "unknown"       }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-invite/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-invite/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite"     },
         { "server",     "test"              },
         { "target",     "francis"           },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("invite").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "francis");
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "#music");
 }
@@ -62,100 +63,100 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     123456          },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     ""              },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     "test"          },
         { "target",     ""              },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     "test"          },
         { "target",     123456          },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     "test"          },
         { "target",     "jean"          },
         { "channel",    ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     "test"          },
         { "target",     "jean"          },
         { "channel",    123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-invite" },
         { "server",     "unknown"       },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-join/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-join/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"       },
         { "server",     "test"              },
         { "channel",    "#music"            },
@@ -54,13 +54,14 @@
 
     const auto cmd = server_->find("join").back();
 
+    BOOST_TEST(!code);
     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)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"       },
         { "server",     "test"              },
         { "channel",    "#music"            }
@@ -68,6 +69,7 @@
 
     const auto cmd = server_->find("join").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#music");
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "");
 }
@@ -76,81 +78,81 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     123456          },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     ""              },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     "test"          },
         { "channel",    ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     "test"          },
         { "channel",    123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_password)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     "test"          },
         { "channel",    "#staff"        },
         { "password",   123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_password);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_password);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_password);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_password);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-join"   },
         { "server",     "unknown"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-kick/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-kick/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"       },
         { "server",     "test"              },
         { "target",     "francis"           },
@@ -55,6 +55,7 @@
 
     const auto cmd = server_->find("kick").back();
 
+    BOOST_TEST(!code);
     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");
@@ -62,7 +63,7 @@
 
 BOOST_AUTO_TEST_CASE(noreason)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"       },
         { "server",     "test"              },
         { "target",     "francis"           },
@@ -71,6 +72,7 @@
 
     const auto cmd = server_->find("kick").back();
 
+    BOOST_TEST(!code);
     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]) == "");
@@ -80,91 +82,91 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     123456          },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     ""              },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "test"          },
         { "target",     ""              },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "test"          },
         { "target",     123456          },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "test"          },
         { "target",     "jean"          },
         { "channel",    ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "test"          },
         { "target",     "jean"          },
         { "channel",    123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_message)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "test"          },
         { "target",     "jean"          },
@@ -172,23 +174,23 @@
         { "reason",     123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_message);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_message);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_message);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_message);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-kick"   },
         { "server",     "unknown"       },
         { "target",     "francis"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-list/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-list/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -42,15 +42,16 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command", "server-list" }
     });
 
-    BOOST_TEST(result.first.is_object());
-    BOOST_TEST(result.first["list"].is_array());
-    BOOST_TEST(result.first["list"].size() == 2U);
-    BOOST_TEST(result.first["list"][0].get<std::string>() == "s1");
-    BOOST_TEST(result.first["list"][1].get<std::string>() == "s2");
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
+    BOOST_TEST(json["list"].is_array());
+    BOOST_TEST(json["list"].size() == 2U);
+    BOOST_TEST(json["list"][0].get<std::string>() == "s1");
+    BOOST_TEST(json["list"][1].get<std::string>() == "s2");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-me/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-me/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     "test"      },
         { "target",     "jean"      },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("me").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "hello!");
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "jean");
 }
@@ -62,72 +63,72 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     123456      },
         { "target",     "#music"    },
         { "message",    "hello!"    }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     ""          },
         { "target",     "#music"    },
         { "message",    "hello!"    }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     "test"      },
         { "target",     ""          },
         { "message",    "hello!"    }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     "test"      },
         { "target",     123456      },
         { "message",    "hello!"    }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-me" },
         { "server",     "unknown"   },
         { "target",     "#music"    },
         { "message",    "hello!"    }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-message/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-message/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     "test"              },
         { "target",     "#staff"            },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("message").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "plop!");
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
 }
@@ -62,72 +63,72 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     123456              },
         { "target",     "#music"            },
         { "message",    "plop!"             }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     ""                  },
         { "target",     "#music"            },
         { "message",    "plop!"             }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     "test"              },
         { "target",     ""                  },
         { "message",    "plop!"             }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     "test"              },
         { "target",     123456              },
         { "message",    "plop!"             }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-message"    },
         { "server",     "unknown"           },
         { "target",     "#music"            },
         { "message",    "plop!"             }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-mode/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-mode/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    "#irccd"        },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("mode").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#irccd");
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "+t");
 }
@@ -62,99 +63,99 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     123456          },
         { "channel",    "#music"        },
         { "mode",       "+i"            }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     ""              },
         { "channel",    "#music"        },
         { "mode",       "+i"            }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    ""              },
         { "mode",       "+i"            }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    123456          },
         { "mode",       "+i"            }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_mode_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    "#music"        },
         { "mode",       ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_mode);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_mode);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_mode);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_mode);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_mode_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "test"          },
         { "channel",    "#music"        },
         { "mode",       123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_mode);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_mode);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_mode);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_mode);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-mode"   },
         { "server",     "unknown"       },
         { "channel",    "#music"        },
         { "mode",       "+i"            }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-nick/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-nick/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,13 +45,14 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     "test"          },
         { "nickname",   "chris"         }
     });
 
-    BOOST_TEST(result.first.is_object());
+    BOOST_TEST(!code);
+    BOOST_TEST(json.is_object());
     BOOST_TEST(server_->get_nickname() == "chris");
 }
 
@@ -59,66 +60,66 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     123456          },
         { "nickname",   "chris"         }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     ""              },
         { "nickname",   "chris"         }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     "test"          },
         { "nickname",   ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_nickname_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     "test"          },
         { "nickname",   123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_nickname);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_nickname);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_nickname);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_nickname);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-nick"   },
         { "server",     "unknown"       },
         { "nickname",   "chris"         }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-notice/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-notice/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     "test"          },
         { "target",     "#staff"        },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("notice").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "quiet!");
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
 }
@@ -62,72 +63,72 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     123456          },
         { "target",     "#music"        },
         { "message",    "quiet!"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     ""              },
         { "target",     "#music"        },
         { "message",    "quiet!"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     "test"          },
         { "target",     ""              },
         { "message",    "quiet!"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     "test"          },
         { "target",     123456          },
         { "message",    "quiet!"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-notice" },
         { "server",     "unknown"       },
         { "target",     "#music"        },
         { "message",    "quiet!"        }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-part/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-part/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    "#staff"        },
@@ -54,13 +54,14 @@
 
     const auto cmd = server_->find("part").back();
 
+    BOOST_TEST(!code);
     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)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    "#staff"        }
@@ -68,6 +69,7 @@
 
     const auto cmd = server_->find("part").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "");
 }
@@ -76,67 +78,67 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     123456          },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     ""              },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    ""              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     "test"          },
         { "channel",    123456          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-part"   },
         { "server",     "unknown"       },
         { "channel",    "#music"        }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-reconnect/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-reconnect/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -49,11 +49,12 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [_, code] = request({
         { "command",    "server-reconnect"  },
         { "server",     "s1"                }
     });
 
+    BOOST_TEST(!code);
     BOOST_TEST(s1_->find("disconnect").size() == 1U);
     BOOST_TEST(s1_->find("connect").size() == 1U);
     BOOST_TEST(s2_->empty());
@@ -61,8 +62,9 @@
 
 BOOST_AUTO_TEST_CASE(all)
 {
-    request({{ "command", "server-reconnect" }});
+    const auto [_, code] = request({{ "command", "server-reconnect" }});
 
+    BOOST_TEST(!code);
     BOOST_TEST(s1_->find("disconnect").size() == 1U);
     BOOST_TEST(s1_->find("connect").size() == 1U);
     BOOST_TEST(s2_->find("disconnect").size() == 1U);
@@ -73,38 +75,38 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-reconnect"  },
         { "server",     123456              }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-reconnect"  },
         { "server",     ""                  }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-reconnect"  },
         { "server",     "unknown"           }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-topic/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/command-server-topic/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -45,7 +45,7 @@
 
 BOOST_AUTO_TEST_CASE(basic)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     "test"          },
         { "channel",    "#staff"        },
@@ -54,6 +54,7 @@
 
     const auto cmd = server_->find("topic").back();
 
+    BOOST_TEST(!code);
     BOOST_TEST(std::any_cast<std::string>(cmd[0]) == "#staff");
     BOOST_TEST(std::any_cast<std::string>(cmd[1]) == "new version");
 }
@@ -62,72 +63,72 @@
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     123456          },
         { "channel",    "#music"        },
         { "topic",      "plop"          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_identifier_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     ""              },
         { "channel",    "#music"        },
         { "topic",      "plop"          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_identifier);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_identifier);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_identifier);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_identifier);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_1)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     "test"          },
         { "channel",    ""              },
         { "topic",      "plop"          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(invalid_channel_2)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     "test"          },
         { "channel",    123456          },
         { "topic",      "plop"          }
     });
 
-    BOOST_TEST(result.second == server_error::invalid_channel);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::invalid_channel);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::invalid_channel);
+    BOOST_TEST(json["error"].get<int>() == server_error::invalid_channel);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_CASE(not_found)
 {
-    const auto result = request({
+    const auto [json, code] = request({
         { "command",    "server-topic"  },
         { "server",     "unknown"       },
         { "channel",    "#music"        },
         { "topic",      "plop"          }
     });
 
-    BOOST_TEST(result.second == server_error::not_found);
-    BOOST_TEST(result.first["error"].template get<int>() == server_error::not_found);
-    BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "server");
+    BOOST_TEST(code == server_error::not_found);
+    BOOST_TEST(json["error"].get<int>() == server_error::not_found);
+    BOOST_TEST(json["errorCategory"].get<std::string>() == "server");
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/dynlib-plugin/main.cpp	Tue Jul 24 22:14:00 2018 +0200
+++ b/tests/src/libirccd/dynlib-plugin/main.cpp	Tue Jul 24 23:01:00 2018 +0200
@@ -38,7 +38,7 @@
     std::shared_ptr<plugin> plugin_;
     irccd irccd_{service_};
 
-    inline fixture()
+    fixture()
     {
         plugin_ = dynlib_plugin_loader({CMAKE_CURRENT_BINARY_DIR}).find("test-plugin");