Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85595733
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
30 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/client/push-rules-desc-p.hpp b/src/client/push-rules-desc-p.hpp
index 338b6f0..436545b 100644
--- a/src/client/push-rules-desc-p.hpp
+++ b/src/client/push-rules-desc-p.hpp
@@ -1,38 +1,40 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <optional>
#include <event.hpp>
#include "push-rules-desc.hpp"
namespace Kazv
{
std::vector<std::string> splitPath(std::string path);
bool matchGlob(std::string target, std::string pattern);
std::optional<json> validateRule(std::string ruleSetName, const json &rule);
Event validatePushRules(const Event &e);
struct PushRulesDescPrivate
{
+ PushRulesDescPrivate(const Event &e);
+
Event pushRulesEvent;
std::optional<std::pair<std::string, json>> matchRule(const Event &e, const RoomModel &room) const;
std::optional<std::pair<std::string, json>> matchRuleSet(std::string ruleSetName, const Event &e, const RoomModel &room) const;
bool matchP(std::string ruleSetName, const json &rule, const Event &e, const RoomModel &room) const;
PushAction handleRule(std::string ruleSetName, const json &rule, const Event &e, const RoomModel &room) const;
};
}
diff --git a/src/client/push-rules-desc.cpp b/src/client/push-rules-desc.cpp
index 4990391..b1b7fc5 100644
--- a/src/client/push-rules-desc.cpp
+++ b/src/client/push-rules-desc.cpp
@@ -1,300 +1,304 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <boost/algorithm/string/regex.hpp>
#include <immer/array.hpp>
#include <event.hpp>
#include <json-utils.hpp>
#include <room/room-model.hpp>
#include "push-rules-desc-p.hpp"
namespace Kazv
{
static immer::array<std::string> ruleSets = {
"override",
"content",
"room",
"sender",
"underride",
};
// https://spec.matrix.org/v1.8/appendices/#dot-separated-property-paths
static const auto splitRegex = boost::regex("(?<!\\\\)\\.");
static const auto unescapeRegex = boost::regex("\\\\(\\\\|\\.)");
std::vector<std::string> splitPath(std::string path)
{
std::vector<std::string> ret;
boost::algorithm::split_regex(ret, path, splitRegex);
for (auto &part : ret) {
part = boost::regex_replace(part, unescapeRegex, "$1");
}
return ret;
}
static const auto metaCharsRegex = boost::regex("([*])|([?])|([.^$|()\\[\\]{}+\\\\])");
bool matchGlob(std::string target, std::string pattern)
{
auto matchRegex = boost::regex_replace(pattern, metaCharsRegex, "(?1.*:)(?2.:)(?3\\\\$3:)", boost::regex_constants::format_all);
return boost::regex_match(target, boost::regex(matchRegex, boost::regex::icase));
}
template<class Jsonish, class Key, class Validator>
static bool cast(Jsonish &ret, const Jsonish &input, Key &&k, Validator &&f)
{
if (!input.contains(k)) {
return false;
}
auto [res, output] = std::invoke(std::forward<Validator>(f), input[k]);
if (!res) {
return false;
}
ret[k] = output;
return true;
}
template<class Func>
static auto identValidate(Func &&f)
{
return [f=std::forward<Func>(f)](auto &&val) mutable {
auto res = std::invoke(std::forward<Func>(f), val);
return std::pair<bool, json>(res, std::forward<decltype(val)>(val));
};
}
static bool isConditionKind(const json &kind)
{
return kind == "event_match"
|| kind == "event_property_is"
|| kind == "event_property_contains";
}
static std::pair<bool, json> validateConditions(const json &conditions)
{
json ret = json::array();
if (!conditions.is_array()) {
return {false, ret};
}
for (const auto &cond : conditions) {
json validatedCond = json::object();
if (!(
cond.is_object()
&& cast(validatedCond, cond, "kind", identValidate(&isConditionKind))
)) {
return {false, ret};
}
if (validatedCond["kind"] == "event_match") {
if (!(
cast(validatedCond, cond, "key", identValidate(&json::is_string))
&& cast(validatedCond, cond, "pattern", identValidate(&json::is_string))
)) {
return {false, ret};
}
} else if (validatedCond["kind"] == "event_property_is" || validatedCond["kind"] == "event_property_contains") {
if (!(
cast(validatedCond, cond, "key", identValidate(&json::is_string))
&& cast(validatedCond, cond, "value", identValidate(&isNonCompoundCanonicalJsonValue))
)) {
return {false, ret};
}
} else {
return {false, ret};
}
ret.push_back(validatedCond);
}
return {true, ret};
}
static std::pair<bool, json> validateActions(const json &actions)
{
json ret = json::array();
if (!actions.is_array()) {
return {false, ret};
}
for (const auto &act : actions) {
if (act == "notify") {
ret.push_back(act);
} else if (act.is_object()) {
json validatedAct = json::object();
if (cast(validatedAct, act, "set_tweak", identValidate([](const auto &t) { return t == "sound"; }))) {
cast(validatedAct, act, "value", identValidate(&json::is_string));
} else if (cast(validatedAct, act, "set_tweak", identValidate([](const auto &t) { return t == "highlight"; }))) {
cast(validatedAct, act, "value", identValidate(&json::is_boolean));
}
ret.push_back(validatedAct);
}
}
return {true, ret};
}
std::optional<json> validateRule(std::string ruleSetName, const json &rule)
{
json ret = json::object();
if (!(
cast(ret, rule, "rule_id", identValidate(&json::is_string))
&& cast(ret, rule, "enabled", identValidate(&json::is_boolean))
&& cast(ret, rule, "default", identValidate(&json::is_boolean))
&& (!(ruleSetName == "override" || ruleSetName == "underride")
|| (!rule.contains("conditions") && ((ret["conditions"] = json::array()), true))
|| cast(ret, rule, "conditions", validateConditions))
&& cast(ret, rule, "actions", validateActions)
)) {
return std::nullopt;
}
return ret;
}
Event validatePushRules(const Event &e)
{
json ret{
{"type", "m.push_rules"},
{"content", {
{"global", {
{"override", {}},
{"content", {}},
{"room", {}},
{"sender", {}},
{"underride", {}},
}},
}},
};
const auto content = e.content().get();
for (const auto &ruleSetName : ruleSets) {
auto rules = getInJson(content, std::array<std::string, 2>{"global", ruleSetName});
if (rules.has_value() && rules.value().is_array()) {
for (const auto &rule : rules.value()) {
auto validated = validateRule(ruleSetName, rule);
if (validated.has_value()) {
ret["content"]["global"][ruleSetName].push_back(validated.value());
}
}
}
}
return Event(ret);
}
+ PushRulesDescPrivate::PushRulesDescPrivate(const Event &e)
+ : pushRulesEvent(validatePushRules(e))
+ {}
+
std::optional<std::pair<std::string, json>> PushRulesDescPrivate::matchRule(const Event &e, const RoomModel &room) const
{
for (const auto &ruleSetName : ruleSets) {
auto matched = matchRuleSet(ruleSetName, e, room);
if (matched.has_value()) {
return matched;
}
}
return std::nullopt;
}
std::optional<std::pair<std::string, json>> PushRulesDescPrivate::matchRuleSet(std::string ruleSetName, const Event &e, const RoomModel &room) const
{
auto rules = pushRulesEvent.content().get().at("global").at(ruleSetName);
for (const auto &rule : rules) {
if (matchP(ruleSetName, rule, e, room)) {
return std::make_pair(ruleSetName, rule);
}
}
return std::nullopt;
}
bool PushRulesDescPrivate::matchP(std::string ruleSetName, const json &rule, const Event &e, const RoomModel &room) const
{
if (!rule.at("enabled").template get<bool>()) {
return false;
}
if (ruleSetName == "override" || ruleSetName == "underride") {
return std::all_of(
rule.at("conditions").begin(),
rule.at("conditions").end(),
[&e](const auto &condition) {
if (condition.is_object()) {
if (condition.at("kind") == "event_property_is") {
auto path = splitPath(condition.at("key"));
auto valueInEvent = getInJson(e.originalJson().get(), path);
if (!valueInEvent.has_value() || !isNonCompoundCanonicalJsonValue(valueInEvent.value())) {
return false;
}
return valueInEvent.value() == condition.at("value");
} else if (condition.at("kind") == "event_match") {
auto path = splitPath(condition.at("key"));
auto valueInEvent = getInJson(e.originalJson().get(), path);
if (!valueInEvent.has_value() || !valueInEvent.value().is_string()) {
return false;
}
auto valueStr = valueInEvent.value().template get<std::string>();
auto patternStr = condition.at("pattern").template get<std::string>();
return matchGlob(valueStr, patternStr);
} else if (condition.at("kind") == "event_property_contains") {
auto path = splitPath(condition.at("key"));
auto valueInEvent = getInJson(e.originalJson().get(), path);
if (!valueInEvent.has_value() || !valueInEvent.value().is_array()) {
return false;
}
auto vals = valueInEvent.value();
return std::any_of(vals.begin(), vals.end(), [cond=condition.at("value")](const auto &val) {
return isNonCompoundCanonicalJsonValue(val) && val == cond;
});
}
}
return false;
}
);
}
return false;
}
PushAction PushRulesDescPrivate::handleRule(std::string ruleSetName, const json &rule, const Event &e, const RoomModel &room) const
{
auto actions = rule.at("actions");
if (std::find(actions.begin(), actions.end(), "notify") != actions.end()) {
return {true};
}
return {false};
}
PushRulesDesc::PushRulesDesc()
: m_d()
{
}
PushRulesDesc::PushRulesDesc(const Event &pushRulesEvent)
: m_d(new PushRulesDescPrivate{pushRulesEvent})
{
}
PushRulesDesc::~PushRulesDesc() = default;
KAZV_DEFINE_COPYABLE_UNIQUE_PTR(PushRulesDesc, m_d);
bool PushRulesDesc::valid() const
{
return !!m_d;
}
PushAction PushRulesDesc::handle(const Event &e, const RoomModel &room) const
{
auto ruleOpt = m_d->matchRule(e, room);
if (ruleOpt.has_value()) {
auto [ruleSetName, rule] = ruleOpt.value();
return m_d->handleRule(ruleSetName, rule, e, room);
}
return {false};
}
}
diff --git a/src/tests/client/push-rules-desc-test.cpp b/src/tests/client/push-rules-desc-test.cpp
index 7abf383..bc9cf86 100644
--- a/src/tests/client/push-rules-desc-test.cpp
+++ b/src/tests/client/push-rules-desc-test.cpp
@@ -1,579 +1,610 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch_test_macros.hpp>
#include <push-rules-desc-p.hpp>
#include <factory.hpp>
using namespace Kazv;
using namespace Kazv::Factory;
// adapted from https://spec.matrix.org/v1.8/client-server-api/#default-override-rules
static auto masterRule = R"({
"rule_id": ".m.rule.master",
"default": true,
"enabled": true,
"conditions": [],
"actions": []
})"_json;
static auto suppressNoticesRule = R"({
"rule_id": ".m.rule.suppress_notices",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "content.msgtype",
"pattern": "m.notice"
}
],
"actions": []
})"_json;
static auto inviteForMeRule = R"({
"rule_id": ".m.rule.invite_for_me",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
},
{
"key": "content.membership",
"kind": "event_match",
"pattern": "invite"
},
{
"key": "state_key",
"kind": "event_match",
"pattern": "@foo:example.com"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
})"_json;
static auto memberEventRule = R"({
"rule_id": ".m.rule.member_event",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
}
],
"actions": []
})"_json;
static auto isUserMentionRule = R"({
"rule_id": ".m.rule.is_user_mention",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_property_contains",
"key": "content.m\\.mentions.user_ids",
"value": "@foo:example.com"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
]
})"_json;
static auto callRule = R"({
"rule_id": ".m.rule.call",
"default": true,
"enabled": true,
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.call.invite"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "ring"
}
]
})"_json;
static auto encryptedOneToOneRule = R"({
"rule_id": ".m.rule.encrypted_room_one_to_one",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "room_member_count",
"is": "2"
},
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.encrypted"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
})"_json;
static auto oneToOneRule = R"({
"rule_id": ".m.rule.room_one_to_one",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "room_member_count",
"is": "2"
},
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
}
]
})"_json;
static auto messageRule = R"({
"rule_id": ".m.rule.message",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.message"
}
],
"actions": [
"notify"
]
})"_json;
static auto encryptedRule = R"({
"rule_id": ".m.rule.encrypted",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.encrypted"
}
],
"actions": [
"notify"
]
})"_json;
static auto catchAllRule = R"({
"rule_id": "moe.kazv.mxc.catch_all",
"default": true,
"enabled": true,
"conditions": [],
"actions": ["notify"]
})"_json;
static auto emptyRulesContent = json{
{"global", {
{"override", {}},
{"content", {}},
{"room", {}},
{"sender", {}},
{"underride", {}},
}},
};
static Event makePushRulesEvent(const json &pushRulesContent)
{
return makeEvent(
withEventContent(pushRulesContent)
| withEventType("m.push_rules")
);
}
TEST_CASE("splitPath()", "[client][push-rules]")
{
REQUIRE(splitPath("content.body") == std::vector<std::string>{"content", "body"});
REQUIRE(splitPath("content.m\\.relates_to") == std::vector<std::string>{"content", "m.relates_to"});
REQUIRE(splitPath("content.m\\\\foo") == std::vector<std::string>{"content", "m\\foo"});
REQUIRE(splitPath("content.m\\x") == std::vector<std::string>{"content", "m\\x"});
}
TEST_CASE("matchGlob()", "[client][push-rules]")
{
REQUIRE(matchGlob("m.notice", "m.notice"));
REQUIRE(!matchGlob("m.notice", "m.+notice"));
REQUIRE(!matchGlob("m.notice", "m.(notice)"));
REQUIRE(!matchGlob("mxnotice", "m.notice"));
REQUIRE(!matchGlob("m.notice", "^m.notice"));
REQUIRE(!matchGlob("m.notice", "m.notice$"));
REQUIRE(!matchGlob("m.notice", "m.notice|m.notice"));
REQUIRE(!matchGlob("m.notice", "m.notic[e]"));
REQUIRE(!matchGlob("m.notice", "m.notic\\e"));
REQUIRE(matchGlob("m\nnotice", "m?notice"));
REQUIRE(matchGlob("Lunch plans", "lunc?*"));
REQUIRE(matchGlob("LUNCH", "lunc?*"));
REQUIRE(!matchGlob(" lunch", "lunc?*"));
REQUIRE(!matchGlob("lunc", "lunc?*"));
}
TEST_CASE("validateRule()", "[client][push-rules]")
{
json rule = masterRule;
SECTION("minimum push rule")
{
auto validated = validateRule("override", rule);
REQUIRE(validated.has_value());
REQUIRE(validated.value() == rule);
}
SECTION("strips extra properties")
{
rule["something"] = "else";
auto validated = validateRule("override", rule);
REQUIRE(validated.has_value());
REQUIRE(validated.value() == masterRule);
}
SECTION("must have rule_id")
{
rule.erase("rule_id");
auto validated = validateRule("override", rule);
REQUIRE(!validated.has_value());
}
SECTION("must have default")
{
rule.erase("default");
auto validated = validateRule("override", rule);
REQUIRE(!validated.has_value());
}
SECTION("must have enabled")
{
rule.erase("enabled");
auto validated = validateRule("override", rule);
REQUIRE(!validated.has_value());
}
SECTION("must have conditions array")
{
rule["conditions"] = json::object();
auto validated = validateRule("override", rule);
REQUIRE(!validated.has_value());
}
SECTION("add empty array if conditions not present")
{
rule.erase("conditions");
auto validated = validateRule("override", rule);
REQUIRE(validated.has_value());
REQUIRE(validated.value() == masterRule);
}
SECTION("reject if there is unknown condition")
{
rule["conditions"] = json::array({{{"kind", "moe.kazv.mxc.unknown-condition"}}});
auto validated = validateRule("override", rule);
REQUIRE(!validated.has_value());
}
SECTION("event_match condition")
{
rule["conditions"] = json::array({{
{"kind", "event_match"},
{"key", "some"},
{"pattern", "patt*n"},
}});
auto validated = validateRule("override", rule);
REQUIRE(validated.has_value());
REQUIRE(validated.value() == rule);
auto ruleWithExtraProp = rule;
ruleWithExtraProp["conditions"][0]["some"] = "thing";
validated = validateRule("override", ruleWithExtraProp);
REQUIRE(validated.has_value());
REQUIRE(validated.value() == rule);
}
}
TEST_CASE("validatePushRules()", "[client][push-rules]")
{
json content = {
{"global", {
{"override", {masterRule, suppressNoticesRule, isUserMentionRule}},
{"content", {}},
{"room", {}},
{"sender", {}},
{"underride", {}},
}},
};
auto e = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
SECTION("nothing unrecognized")
{
auto validated = validatePushRules(e);
REQUIRE(validated == e);
}
SECTION("extra param in content")
{
content["something"] = "else";
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == e);
}
SECTION("extra param in global")
{
content["global"]["something"] = "else";
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == e);
}
SECTION("extra param in rule")
{
content["global"]["override"][0]["something"] = "else";
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == e);
}
SECTION("extra param in condition")
{
content["global"]["override"][1]["conditions"][0]["something"] = "else";
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == e);
}
SECTION("unrecognized condition")
{
content["global"]["override"][1]["conditions"].push_back(json::object({{"kind", "moe.kazv.mxc.unknown-condition"}}));
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
content["global"]["override"].erase(1);
auto expected = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == expected);
}
SECTION("unrecognized action")
{
content["global"]["override"][1]["actions"].push_back("moe.kazv.mxc.unknown-action");
auto extra = Event{json{
{"type", "m.push_rules"},
{"content", content},
}};
auto validated = validatePushRules(extra);
REQUIRE(validated == e);
}
}
TEST_CASE("PushRulesDescPrivate::matchP()", "[client][push-rules]")
{
- PushRulesDescPrivate rp;
+ PushRulesDescPrivate rp{Event()};
auto room = makeRoom();
SECTION("master rule")
{
REQUIRE(rp.matchP("override", masterRule, makeEvent(), room));
}
SECTION("suppress notices rule")
{
auto notice = makeEvent(withEventContent(json{{"msgtype", "m.notice"}}));
REQUIRE(rp.matchP("override", suppressNoticesRule, notice, room));
auto notNotice = makeEvent(withEventContent(json{{"msgtype", "m.noticexxx"}}));
REQUIRE(!rp.matchP("override", suppressNoticesRule, notNotice, room));
}
SECTION("invite for me rule")
{
auto invite = makeEvent(
withEventContent(json{{"membership", "invite"}})
| withEventType("m.room.member")
| withStateKey("@foo:example.com")
);
REQUIRE(rp.matchP("override", inviteForMeRule, invite, room));
auto inviteForOthers = makeEvent(
withEventContent(json{{"membership", "invite"}})
| withEventType("m.room.member")
| withStateKey("@foo:example.com.tw")
);
REQUIRE(!rp.matchP("override", inviteForMeRule, inviteForOthers, room));
}
SECTION("member event rule")
{
auto member = makeEvent(
withEventContent(json{{"membership", "join"}})
| withEventType("m.room.member")
| withStateKey("@foo:example.com")
);
REQUIRE(rp.matchP("override", memberEventRule, member, room));
}
SECTION("is user mention rule")
{
auto mentionedEvent = makeEvent(
withEventContent(json{
{"m.mentions", {
{"user_ids", {"@foo:example.com"}}
}}
})
);
auto notMentionedEvent = makeEvent(
withEventContent(json{
{"m.mentions", {
{"user_ids", {"@foo:example.com.tw"}}
}}
})
);
REQUIRE(rp.matchP("override", isUserMentionRule, mentionedEvent, room));
REQUIRE(!rp.matchP("override", isUserMentionRule, notMentionedEvent, room));
}
}
TEST_CASE("Push rules works with predefined rules", "[client][push-rules]")
{
auto rulesContent = emptyRulesContent;
auto room = makeRoom();
SECTION("master rule")
{
rulesContent["global"]["override"].push_back(masterRule);
rulesContent["global"]["underride"].push_back(catchAllRule);
auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
auto result = pushRules.handle(makeEvent(), room);
REQUIRE(!result.shouldNotify);
}
SECTION("suppress notices rule")
{
rulesContent["global"]["override"].push_back(suppressNoticesRule);
rulesContent["global"]["underride"].push_back(catchAllRule);
auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
auto result = pushRules.handle(makeEvent(withEventContent(json{{"msgtype", "m.notice"}})), room);
REQUIRE(!result.shouldNotify);
}
SECTION("invite for me rule")
{
rulesContent["global"]["override"].push_back(inviteForMeRule);
auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
auto result = pushRules.handle(makeEvent(
withEventContent(json{{"membership", "invite"}})
| withEventType("m.room.member")
| withStateKey("@foo:example.com")
), room);
REQUIRE(result.shouldNotify);
}
SECTION("member event rule")
{
rulesContent["global"]["override"].push_back(memberEventRule);
rulesContent["global"]["underride"].push_back(catchAllRule);
auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
auto result = pushRules.handle(makeEvent(
withEventContent(json{{"membership", "invite"}})
| withEventType("m.room.member")
| withStateKey("@foo:example.com")
), room);
REQUIRE(!result.shouldNotify);
}
SECTION("is user mention rule")
{
auto mentionedEvent = makeEvent(
withEventContent(json{
{"m.mentions", {
{"user_ids", {"@foo:example.com"}}
}}
})
);
auto notMentionedEvent = makeEvent(
withEventContent(json{
{"m.mentions", {
{"user_ids", {"@foo:example.com.tw"}}
}}
})
);
rulesContent["global"]["override"].push_back(isUserMentionRule);
auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
REQUIRE(pushRules.handle(mentionedEvent, room).shouldNotify);
REQUIRE(!pushRules.handle(notMentionedEvent, room).shouldNotify);
}
}
+
+TEST_CASE("Push rules would not crash with broken rules", "[client][push-rules]")
+{
+ auto rulesContent = emptyRulesContent;
+ auto room = makeRoom();
+ REQUIRE(!PushRulesDesc(Event()).handle(makeEvent(), room).shouldNotify);
+
+ SECTION("rule missing required param")
+ {
+ auto rule = catchAllRule;
+ rule.erase("rule_id");
+ rulesContent["global"]["override"].push_back(rule);
+ REQUIRE(!PushRulesDesc(makePushRulesEvent(rulesContent)).handle(makeEvent(), room).shouldNotify);
+ }
+
+ SECTION("rule containing invalid condition")
+ {
+ auto rule = catchAllRule;
+ rule["conditions"].push_back("something");
+ rulesContent["global"]["override"].push_back(rule);
+ REQUIRE(!PushRulesDesc(makePushRulesEvent(rulesContent)).handle(makeEvent(), room).shouldNotify);
+ }
+
+ SECTION("rule containing unknown action")
+ {
+ auto rule = catchAllRule;
+ rule["actions"].push_back("something");
+ rulesContent["global"]["override"].push_back(rule);
+ REQUIRE(PushRulesDesc(makePushRulesEvent(rulesContent)).handle(makeEvent(), room).shouldNotify);
+ }
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jul 19, 11:43 PM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1695390
Default Alt Text
(30 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline
Log In to Comment