# HG changeset patch # User David Demelier # Date 1499338138 -7200 # Node ID 9968eac538e6255d67a364b840f1ca5bfa05b050 # Parent 9be4f8a5cf1ac0dfbd8716f99ccd6a51f7931e21 Irccdctl: implement rule-info diff -r 9be4f8a5cf1a -r 9968eac538e6 doc/html/CMakeLists.txt --- a/doc/html/CMakeLists.txt Wed Jul 05 18:20:26 2017 +0200 +++ b/doc/html/CMakeLists.txt Thu Jul 06 12:48:58 2017 +0200 @@ -153,6 +153,7 @@ ${html_SOURCE_DIR}/irccdctl/command/plugin-reload.md ${html_SOURCE_DIR}/irccdctl/command/plugin-unload.md ${html_SOURCE_DIR}/irccdctl/command/rule-list.md + ${html_SOURCE_DIR}/irccdctl/command/rule-info.md ${html_SOURCE_DIR}/irccdctl/command/server-cmode.md ${html_SOURCE_DIR}/irccdctl/command/server-cnotice.md ${html_SOURCE_DIR}/irccdctl/command/server-connect.md diff -r 9be4f8a5cf1a -r 9968eac538e6 doc/html/irccdctl/command/index.md --- a/doc/html/irccdctl/command/index.md Wed Jul 05 18:20:26 2017 +0200 +++ b/doc/html/irccdctl/command/index.md Thu Jul 06 12:48:58 2017 +0200 @@ -13,6 +13,7 @@ - [plugin-reload](plugin-reload.html) - [plugin-unload](plugin-unload.html) - [rule-list](rule-list.html) + - [rule-info](rule-info.html) - [server-cmode](server-cmode.html) - [server-cnotice](server-cnotice.html) - [server-connect](server-connect.html) diff -r 9be4f8a5cf1a -r 9968eac538e6 doc/html/irccdctl/command/rule-info.md Binary file doc/html/irccdctl/command/rule-info.md has changed diff -r 9be4f8a5cf1a -r 9968eac538e6 irccd/main.cpp --- a/irccd/main.cpp Wed Jul 05 18:20:26 2017 +0200 +++ b/irccd/main.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -320,6 +320,7 @@ instance->commands().add(std::make_unique()); instance->commands().add(std::make_unique()); instance->commands().add(std::make_unique()); + instance->commands().add(std::make_unique()); instance->commands().add(std::make_unique()); // Load Javascript API and plugin loader. diff -r 9be4f8a5cf1a -r 9968eac538e6 irccdctl/cli.cpp --- a/irccdctl/cli.cpp Wed Jul 05 18:20:26 2017 +0200 +++ b/irccdctl/cli.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -908,6 +908,44 @@ } /* + * RuleInfoCli. + * ------------------------------------------------------------------ + */ + +RuleInfoCli::RuleInfoCli() + : Cli("rule-info", + "show a rule", + "rule-info index", + "Show a rule.\n\n" + "Example:\n" + "\tirccdctl rule-info 0\n" + "\tirccdctl rule-info 1") +{ +} + +void RuleInfoCli::exec(Irccdctl &irccdctl, const std::vector &args) +{ + if (args.size() < 1) + throw std::invalid_argument("rule-info requires 1 argument"); + + int index = 0; + + try { + index = std::stoi(args[0]); + } catch (...) { + throw std::invalid_argument("invalid number '" + args[0] + "'"); + } + + auto result = request(irccdctl, { + { "command", "rule-info" }, + { "index", index } + }); + + check(result); + showRule(result, 0); +} + +/* * WatchCli. * ------------------------------------------------------------------ */ diff -r 9be4f8a5cf1a -r 9968eac538e6 irccdctl/cli.hpp --- a/irccdctl/cli.hpp Wed Jul 05 18:20:26 2017 +0200 +++ b/irccdctl/cli.hpp Thu Jul 06 12:48:58 2017 +0200 @@ -607,6 +607,27 @@ }; /* + * RuleInfoCli + * ------------------------------------------------------------------ + */ + +/** + * \brief Implementation of irccdctl rule-info. + */ +class RuleInfoCli : public Cli { +public: + /** + * Default constructor. + */ + RuleInfoCli(); + + /** + * \copydoc Cli::exec + */ + void exec(Irccdctl &client, const std::vector &args) override; +}; + +/* * WatchCli. * ------------------------------------------------------------------ */ diff -r 9be4f8a5cf1a -r 9968eac538e6 irccdctl/main.cpp --- a/irccdctl/main.cpp Wed Jul 05 18:20:26 2017 +0200 +++ b/irccdctl/main.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -523,6 +523,7 @@ commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); + commands.push_back(std::make_unique()); commands.push_back(std::make_unique()); } diff -r 9be4f8a5cf1a -r 9968eac538e6 libcommon/irccd/util.hpp --- a/libcommon/irccd/util.hpp Wed Jul 05 18:20:26 2017 +0200 +++ b/libcommon/irccd/util.hpp Thu Jul 06 12:48:58 2017 +0200 @@ -399,7 +399,17 @@ */ inline std::int64_t requireInt(const nlohmann::json &json, const std::string &key) { - return require(json, key, nlohmann::json::value_t::number_integer); + auto it = json.find(key); + + if (it == json.end()) + throw std::runtime_error(fmt::format("missing '{}' property", key)); + if (it->is_number_integer()) + return it->get(); + if (it->is_number_unsigned() && it->get() <= INT_MAX) + return static_cast(it->get()); + + throw std::runtime_error(fmt::format("invalid '{}' property ({} expected, got {})", + key, it->type_name(), nlohmann::json(0).type_name())); } /** @@ -412,7 +422,17 @@ */ inline std::uint64_t requireUint(const nlohmann::json &json, const std::string &key) { - return require(json, key, nlohmann::json::value_t::number_unsigned); + auto it = json.find(key); + + if (it == json.end()) + throw std::runtime_error(fmt::format("missing '{}' property", key)); + if (it->is_number_unsigned()) + return it->get(); + if (it->is_number_integer() && it->get() >= 0) + return static_cast(it->get()); + + throw std::runtime_error(fmt::format("invalid '{}' property ({} expected, got {})", + key, it->type_name(), nlohmann::json(0U).type_name())); } /** diff -r 9be4f8a5cf1a -r 9968eac538e6 libirccd/irccd/command.cpp --- a/libirccd/irccd/command.cpp Wed Jul 05 18:20:26 2017 +0200 +++ b/libirccd/irccd/command.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -464,6 +464,16 @@ client.success("rule-list", {{ "list", std::move(array) }}); } +RuleInfoCommand::RuleInfoCommand() + : Command("rule-info") +{ +} + +void RuleInfoCommand::exec(Irccd &irccd, TransportClient &client, const nlohmann::json &args) +{ + client.success("rule-info", toJson(irccd.rules().require(util::json::requireUint(args, "index")))); +} + } // !command } // !irccd diff -r 9be4f8a5cf1a -r 9968eac538e6 libirccd/irccd/command.hpp --- a/libirccd/irccd/command.hpp Wed Jul 05 18:20:26 2017 +0200 +++ b/libirccd/irccd/command.hpp Thu Jul 06 12:48:58 2017 +0200 @@ -474,6 +474,22 @@ void exec(Irccd &irccd, TransportClient &client, const nlohmann::json &args) override; }; +/** + * \brief Implementation of rule-info transport command. + */ +class IRCCD_EXPORT RuleInfoCommand : public Command { +public: + /** + * Constructor. + */ + RuleInfoCommand(); + + /** + * \copydoc Command::exec + */ + void exec(Irccd &irccd, TransportClient &client, const nlohmann::json &args) override; +}; + } // !command } // !irccd diff -r 9be4f8a5cf1a -r 9968eac538e6 libirccd/irccd/service.cpp --- a/libirccd/irccd/service.cpp Wed Jul 05 18:20:26 2017 +0200 +++ b/libirccd/irccd/service.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -296,6 +296,14 @@ m_rules.erase(m_rules.begin() + position); } +const Rule &RuleService::require(unsigned position) const +{ + if (position >= m_rules.size()) + throw std::out_of_range("rule " + std::to_string(position) + " does not exist"); + + return m_rules[position]; +} + bool RuleService::solve(const std::string &server, const std::string &channel, const std::string &origin, diff -r 9be4f8a5cf1a -r 9968eac538e6 libirccd/irccd/service.hpp --- a/libirccd/irccd/service.hpp Wed Jul 05 18:20:26 2017 +0200 +++ b/libirccd/irccd/service.hpp Thu Jul 06 12:48:58 2017 +0200 @@ -347,6 +347,15 @@ IRCCD_EXPORT void remove(unsigned position); /** + * Get a rule at the specified index or throw an exception if not found. + * + * \param position the position + * \return the rule + * \throw std::out_of_range if position is invalid + */ + IRCCD_EXPORT const Rule &require(unsigned position) const; + + /** * Resolve the action to execute with the specified list of rules. * * \param server the server name diff -r 9be4f8a5cf1a -r 9968eac538e6 tests/CMakeLists.txt --- a/tests/CMakeLists.txt Wed Jul 05 18:20:26 2017 +0200 +++ b/tests/CMakeLists.txt Thu Jul 06 12:48:58 2017 +0200 @@ -27,6 +27,7 @@ add_subdirectory(cmd-plugin-reload) add_subdirectory(cmd-plugin-unload) add_subdirectory(cmd-rule-list) + add_subdirectory(cmd-rule-info) add_subdirectory(cmd-server-cmode) add_subdirectory(cmd-server-cnotice) add_subdirectory(cmd-server-connect) diff -r 9be4f8a5cf1a -r 9968eac538e6 tests/cmd-rule-info/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/cmd-rule-info/CMakeLists.txt Thu Jul 06 12:48:58 2017 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2017 David Demelier +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME cmd-rule-info + SOURCES main.cpp + LIBRARIES libirccd libirccdctl +) diff -r 9be4f8a5cf1a -r 9968eac538e6 tests/cmd-rule-info/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/cmd-rule-info/main.cpp Thu Jul 06 12:48:58 2017 +0200 @@ -0,0 +1,126 @@ +/* + * main.cpp -- test rule-info remote command + * + * Copyright (c) 2013-2017 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +using namespace irccd; +using namespace irccd::command; + +class RuleInfoCommandTest : public CommandTester { +protected: + nlohmann::json m_result; + + /* + * Rule sets are unordered so use this function to search a string in + * the JSON array. + */ + inline bool contains(const nlohmann::json &array, const std::string &str) + { + for (const auto &v : array) + if (v.is_string() && v == str) + return true; + + return false; + } + +public: + RuleInfoCommandTest() + : CommandTester(std::make_unique()) + { + m_irccd.rules().add(Rule( + { "s1", "s2" }, + { "c1", "c2" }, + { "o1", "o2" }, + { "p1", "p2" }, + { "onMessage", "onCommand" }, + RuleAction::Drop + )); + m_irccd.rules().add(Rule( + { "s1", }, + { "c1", }, + { "o1", }, + { "p1", }, + { "onMessage", }, + RuleAction::Accept + )); + m_irccdctl.client().onMessage.connect([&] (auto result) { + m_result = result; + }); + } +}; + +TEST_F(RuleInfoCommandTest, basic) +{ + m_irccdctl.client().request({ + { "command", "rule-info" }, + { "index", 0 } + }); + + try { + poll([&] () { + return m_result.is_object(); + }); + + ASSERT_TRUE(m_result.is_object()); + + auto servers = m_result["servers"]; + auto channels = m_result["channels"]; + auto plugins = m_result["plugins"]; + auto events = m_result["events"]; + + ASSERT_TRUE(contains(servers, "s1")); + ASSERT_TRUE(contains(servers, "s2")); + ASSERT_TRUE(contains(channels, "c1")); + ASSERT_TRUE(contains(channels, "c2")); + ASSERT_TRUE(contains(plugins, "p1")); + ASSERT_TRUE(contains(plugins, "p2")); + ASSERT_TRUE(contains(events, "onMessage")); + ASSERT_TRUE(contains(events, "onCommand")); + ASSERT_EQ("drop", m_result["action"]); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(RuleInfoCommandTest, outOfBounds) +{ + m_irccdctl.client().request({ + { "command", "rule-info" }, + { "index", 123 } + }); + + try { + poll([&] () { + return m_result.is_object(); + }); + + ASSERT_TRUE(m_result.is_object()); + ASSERT_FALSE(m_result["status"]); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}