Page MenuHomePhorge

push-rules-desc-test.cpp
No OneTemporary

Size
16 KB
Referenced Files
None
Subscribers
None

push-rules-desc-test.cpp

/*
* 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{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

Mime Type
text/x-c
Expires
Thu, Oct 2, 2:30 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
486953
Default Alt Text
push-rules-desc-test.cpp (16 KB)

Event Timeline