Page MenuHomePhorge

D64.1732558643.diff
No OneTemporary

Size
39 KB
Referenced Files
None
Subscribers
None

D64.1732558643.diff

diff --git a/src/tests/client/account-data-test.cpp b/src/tests/client/account-data-test.cpp
--- a/src/tests/client/account-data-test.cpp
+++ b/src/tests/client/account-data-test.cpp
@@ -19,7 +19,7 @@
#include <client/client.hpp>
#include "client-test-util.hpp"
-
+#include "action-mock-utils.hpp"
#include "factory.hpp"
using namespace Kazv::Factory;
@@ -104,17 +104,12 @@
);
auto ctx = sdk.context();
- auto setAccountDataCalled = false;
- auto mockContext = typename Client::ContextT([&ph, &setAccountDataCalled](const auto &action) {
- if (std::holds_alternative<SetAccountDataPerRoomAction>(action)) {
- setAccountDataCalled = true;
- auto a = std::get<SetAccountDataPerRoomAction>(action);
- REQUIRE(a.roomId == "!room:example.com");
- REQUIRE(a.accountDataEvent == accountDataEvent);
- return ph.createResolved(EffectStatus(true, json::object()));
- }
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
+ auto dispatcher = getMockDispatcher(
+ ph,
+ ctx,
+ returnEmpty<SetAccountDataPerRoomAction>()
+ );
+ auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!room:example.com");
@@ -125,7 +120,10 @@
io.run();
- REQUIRE(setAccountDataCalled);
+ REQUIRE(dispatcher.template calledTimes<SetAccountDataPerRoomAction>() == 1);
+ auto a = dispatcher.template of<SetAccountDataPerRoomAction>()[0];
+ REQUIRE(a.roomId == "!room:example.com");
+ REQUIRE(a.accountDataEvent == accountDataEvent);
}
Event tagEvent = R"({
@@ -161,18 +159,13 @@
);
auto ctx = sdk.context();
- auto setAccountDataCalled = false;
auto expectedEvent = Event();
- auto mockContext = typename Client::ContextT([&ph, &expectedEvent, &setAccountDataCalled](const auto &action) {
- if (std::holds_alternative<SetAccountDataPerRoomAction>(action)) {
- setAccountDataCalled = true;
- auto a = std::get<SetAccountDataPerRoomAction>(action);
- REQUIRE(a.roomId == "!room:example.com");
- REQUIRE(a.accountDataEvent == expectedEvent);
- return ph.createResolved(EffectStatus(true, json::object()));
- }
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
+ auto dispatcher = getMockDispatcher(
+ ph,
+ ctx,
+ returnEmpty<SetAccountDataPerRoomAction>()
+ );
+ auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!room:example.com");
@@ -227,7 +220,10 @@
io.run();
- REQUIRE(setAccountDataCalled);
+ REQUIRE(dispatcher.template calledTimes<SetAccountDataPerRoomAction>() == 1);
+ auto a = dispatcher.template of<SetAccountDataPerRoomAction>()[0];
+ REQUIRE(a.roomId == "!room:example.com");
+ REQUIRE(a.accountDataEvent == expectedEvent);
}
TEST_CASE("Room::removeTag()", "[client][account-data][tagging]")
@@ -253,19 +249,15 @@
);
auto ctx = sdk.context();
- auto setAccountDataCalled = false;
auto expectedEvent = Event();
- auto mockContext = typename Client::ContextT([&ph, &expectedEvent, &setAccountDataCalled](const auto &action) {
- if (std::holds_alternative<SetAccountDataPerRoomAction>(action)) {
- setAccountDataCalled = true;
- auto a = std::get<SetAccountDataPerRoomAction>(action);
- REQUIRE(a.roomId == "!room:example.com");
- REQUIRE(a.accountDataEvent == expectedEvent);
- return ph.createResolved(EffectStatus(true, json::object()));
- }
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
+ auto dispatcher = getMockDispatcher(
+ ph,
+ ctx,
+ returnEmpty<SetAccountDataPerRoomAction>()
+ );
+
+ auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!room:example.com");
@@ -293,7 +285,10 @@
io.run();
- REQUIRE(setAccountDataCalled);
+ REQUIRE(dispatcher.template calledTimes<SetAccountDataPerRoomAction>() == 1);
+ auto a = dispatcher.template of<SetAccountDataPerRoomAction>()[0];
+ REQUIRE(a.roomId == "!room:example.com");
+ REQUIRE(a.accountDataEvent == expectedEvent);
}
TEST_CASE("Send set account data job", "[client][account-data]")
@@ -380,16 +375,12 @@
);
auto ctx = sdk.context();
- auto setAccountDataCalled = false;
- auto mockContext = typename Client::ContextT([&ph, &setAccountDataCalled](const auto &action) {
- if (std::holds_alternative<SetAccountDataAction>(action)) {
- setAccountDataCalled = true;
- auto a = std::get<SetAccountDataAction>(action);
- REQUIRE(a.accountDataEvent == accountDataEvent);
- return ph.createResolved(EffectStatus(true, json::object()));
- }
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
+ auto dispatcher = getMockDispatcher(
+ ph,
+ ctx,
+ returnEmpty<SetAccountDataAction>()
+ );
+ auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.setAccountData(accountDataEvent)
@@ -399,5 +390,7 @@
io.run();
- REQUIRE(setAccountDataCalled);
+ REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
+ auto a = dispatcher.template of<SetAccountDataAction>()[0];
+ REQUIRE(a.accountDataEvent == accountDataEvent);
}
diff --git a/src/tests/client/action-mock-utils.hpp b/src/tests/client/action-mock-utils.hpp
new file mode 100644
--- /dev/null
+++ b/src/tests/client/action-mock-utils.hpp
@@ -0,0 +1,242 @@
+/*
+ * This file is part of libkazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+
+#include <vector>
+#include <boost/hana.hpp>
+#include <boost/core/demangle.hpp>
+#include <zug/into_vector.hpp>
+#include <zug/transducer/map.hpp>
+#include <zug/transducer/filter.hpp>
+#include <store/store.hpp>
+#include <client/client.hpp>
+
+template<class Action>
+struct PassDownTag
+{};
+
+template<class Action>
+constexpr PassDownTag<Action> passDown()
+{
+ return {};
+};
+
+template<class Action, class DataT = Kazv::EffectStatus>
+struct ReturnResolvedTag
+{
+ DataT data;
+};
+
+template<class Action, class DataT = Kazv::EffectStatus>
+constexpr ReturnResolvedTag<Action, DataT> returnResolved(DataT data)
+{
+ return {data};
+}
+
+template<class Action, class DataT = Kazv::EffectStatus>
+constexpr ReturnResolvedTag<Action, DataT> returnEmpty()
+{
+ return {{}};
+}
+
+template<class SubAction, class FuncT>
+struct HandlerTag : public FuncT
+{
+ HandlerTag(FuncT f) : FuncT(std::move(f)) {}
+};
+
+template<class SubAction, class FuncT>
+constexpr auto makeHandler(FuncT f)
+{
+ return HandlerTag<SubAction, FuncT>(std::move(f));
+};
+
+template<class R, class F, class = int, class ...Args>
+struct IsExactInvocableHelper : public std::false_type
+{};
+
+template<class R, class F, class ...Args>
+struct IsExactInvocableHelper<
+ R,
+ F,
+ std::enable_if_t<std::is_same_v<std::invoke_result_t<F, Args...>, R>, int>,
+ Args...> : public std::true_type
+{};
+
+template<class R, class F, class ...Args>
+constexpr auto isExactInvocable = IsExactInvocableHelper<R, F, int, Args...>::value;
+
+static_assert(isExactInvocable<int, std::function<int(long long)>, long long>);
+
+template<class T>
+std::string getTypeName(const T &a)
+{
+ return boost::core::demangle(typeid(a).name());
+}
+
+template<class Variant>
+std::string getVariantTypeName(const Variant &v)
+{
+ return std::visit([](const auto &a) {
+ return getTypeName(a);
+ }, v);
+}
+
+template<class Promise>
+struct HandlerResult
+{
+ std::optional<Promise> retVal;
+};
+
+template<class PH, class Context, class Action = Kazv::Client::ActionT>
+struct MockDispatcher
+{
+ using ContextT = Context;
+ using ActionT = Action;
+ using PromiseT = typename ContextT::PromiseT;
+ using DataT = typename PromiseT::DataT;
+ using HandlerResultT = HandlerResult<PromiseT>;
+
+ struct Handler : public std::function<HandlerResultT(PH &, ContextT &, ActionT)>
+ {
+ using BaseT = std::function<HandlerResultT(PH &, ContextT &, ActionT)>;
+ using BaseT::BaseT;
+ using BaseT::operator();
+
+ template<class SubAction, class FuncT>
+ static BaseT make(FuncT func)
+ {
+ using Func = std::decay_t<FuncT>;
+ if constexpr (isExactInvocable<HandlerResultT, Func, PH &, ContextT &, SubAction>) {
+ return [func](PH &ph, ContextT &ctx, ActionT action) mutable -> HandlerResultT {
+ static_assert(boost::hana::is_valid([]() {
+ return boost::hana::type_c<
+ decltype(std::get<SubAction>(action))>;
+ })(),
+ "SubAction must be a variant alternative of ActionT");
+ if (std::holds_alternative<SubAction>(action)) {
+ return func(ph, ctx, std::get<SubAction>(action));
+ }
+ return {std::nullopt};
+ };
+ } else if constexpr (isExactInvocable<PromiseT, Func, PH &, ContextT &, SubAction>) {
+ return make<SubAction>([func](PH &ph, ContextT &ctx, SubAction subAction) mutable {
+ return HandlerResultT{func(ph, ctx, subAction)};
+ });
+ } else if constexpr (isExactInvocable<HandlerResultT, Func, SubAction>) {
+ return make<SubAction>([func](PH &, ContextT &, SubAction subAction) mutable {
+ return func(subAction);
+ });
+ } else if constexpr (isExactInvocable<PromiseT, Func, SubAction>) {
+ return make<SubAction>([func](PH &, ContextT &, SubAction subAction) mutable {
+ return func(subAction);
+ });
+ } else if constexpr (isExactInvocable<DataT, Func, SubAction>) {
+ return make<SubAction>([func](PH &ph, ContextT &, SubAction subAction) mutable {
+ return ph.createResolved(func(subAction));
+ });
+ } else if constexpr (isExactInvocable<void, Func, SubAction>) {
+ return make<SubAction>([func](PH &ph, ContextT &, SubAction subAction) mutable {
+ func(subAction);
+ return ph.createResolved({});
+ });
+ } else {
+ // This is a trick to avoid compilers reporting failure
+ // even when this branch is never executed for any Func
+ // https://stackoverflow.com/questions/38304847/how-does-a-failed-static-assert-work-in-an-if-constexpr-false-block
+ static_assert(!sizeof(Func), "Function is not convertible to an action handler");
+ return [func](PH &, ContextT &, ActionT) mutable -> HandlerResultT {
+ return {std::nullopt};
+ };
+ }
+ }
+
+ template<class SubAction>
+ Handler(PassDownTag<SubAction>)
+ : BaseT(make<SubAction>([](PH &, ContextT &ctx, SubAction action) mutable {
+ Kazv::kzo.client.dbg() << "PassDown" << std::endl;
+ return ctx.dispatch(action);
+ }))
+ {}
+
+ template<class SubAction>
+ Handler(ReturnResolvedTag<SubAction, DataT> tag)
+ : BaseT(make<SubAction>([data=tag.data](SubAction) mutable {
+ Kazv::kzo.client.dbg() << "ReturnResolved" << std::endl;
+ return data;
+ }))
+ {}
+
+ template<class SubAction, class FuncT>
+ Handler(HandlerTag<SubAction, FuncT> func)
+ : BaseT(make<SubAction>(func))
+ {}
+ };
+
+ PromiseT operator()(ActionT action)
+ {
+ actions->push_back(action);
+ std::string typeName = getVariantTypeName(action);
+ Kazv::kzo.client.dbg() << "Handling action " << typeName << std::endl;
+ for (auto &handler : handlers) {
+ auto res = handler(ph, ctx, action);
+ if (res.retVal.has_value()) {
+ return res.retVal.value();
+ }
+ }
+ throw std::runtime_error{"unhandled action: " + typeName};
+ }
+
+ template<class SubAction>
+ auto calledTimes()
+ {
+ return std::accumulate(actions->begin(), actions->end(),
+ 0,
+ [](auto acc, auto cur) {
+ return acc + (std::holds_alternative<SubAction>(cur) ? 1 : 0);
+ }
+ );
+ }
+
+ template<class SubAction>
+ std::vector<SubAction> of()
+ {
+ return zug::into_vector(
+ zug::filter([](const auto &a) { return std::holds_alternative<SubAction>(a); })
+ | zug::map([](const auto &a) { return std::get<SubAction>(a); }),
+ *actions
+ );
+ }
+
+ auto clear() { actions->clear(); }
+
+ PH &ph;
+ ContextT &ctx;
+ std::vector<Handler> handlers;
+ std::shared_ptr<std::vector<ActionT>> actions{std::make_shared<std::vector<ActionT>>()};
+};
+
+template<class PH, class ContextT, class ...Handlers>
+auto getMockDispatcher(PH &ph, ContextT &ctx, Handlers ...handlers)
+{
+ using ResType = MockDispatcher<PH, ContextT>;
+ return ResType{
+ ph,
+ ctx,
+ std::vector<typename ResType::Handler>{ {handlers...} }
+ };
+}
+
+template<
+ class ContextT = typename Kazv::Client::ContextT,
+ class ActionT = typename Kazv::Client::ActionT,
+ class PH,
+ class Func>
+auto getMockContext(PH &ph, Func &&func)
+{
+ return ContextT(std::forward<Func>(func), ph, lager::deps<>{});
+};
diff --git a/src/tests/client/create-room-test.cpp b/src/tests/client/create-room-test.cpp
--- a/src/tests/client/create-room-test.cpp
+++ b/src/tests/client/create-room-test.cpp
@@ -18,23 +18,12 @@
#include <client/client.hpp>
#include "client-test-util.hpp"
+#include "action-mock-utils.hpp"
#include "factory.hpp"
using namespace Kazv;
using namespace Kazv::Factory;
-template<class Store, class Func>
-static auto getMockContext(SingleTypePromiseInterface<EffectStatus> &ph, Store &store, Func func)
-{
- return typename Client::ContextT([&ph, &store, func](const auto &action) {
- auto [cont, res] = func(action);
- if (!cont) {
- return ph.createResolved(res);
- }
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
-}
-
TEST_CASE("Client::createRoom()", "[client][create-room]")
{
ClientModel m = makeClient();
@@ -53,17 +42,14 @@
);
auto ctx = sdk.context();
- auto createRoomCalled = 0;
- std::optional<CreateRoomAction> action = std::nullopt;
- auto mockContext = getMockContext(ph, ctx, [&createRoomCalled, &action](const auto &a) {
- if (std::holds_alternative<CreateRoomAction>(a)) {
- ++createRoomCalled;
- action = std::get<CreateRoomAction>(a);
- return std::make_pair(/* cont = */ false, EffectStatus());
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+ auto dispatcher = getMockDispatcher(
+ ph,
+ ctx,
+ returnEmpty<CreateRoomAction>()
+ );
+
+ auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
@@ -83,15 +69,16 @@
io.run();
- REQUIRE(createRoomCalled == 1);
- REQUIRE(action->visibility == RoomVisibility::Private);
- REQUIRE(action->name == "some name");
- REQUIRE(action->roomAliasName == "alias");
- REQUIRE(action->invite == immer::array<std::string>{"@invited:example.com"});
- REQUIRE(action->isDirect == true);
- REQUIRE(action->creationContent.get() == json::object({
+ REQUIRE(dispatcher.template calledTimes<CreateRoomAction>() == 1);
+ auto action = dispatcher.template of<CreateRoomAction>()[0];
+ REQUIRE(action.visibility == RoomVisibility::Private);
+ REQUIRE(action.name == "some name");
+ REQUIRE(action.roomAliasName == "alias");
+ REQUIRE(action.invite == immer::array<std::string>{"@invited:example.com"});
+ REQUIRE(action.isDirect == true);
+ REQUIRE(action.creationContent.get() == json::object({
{"m.federate", true},
}));
- REQUIRE(action->topic == "some topic");
- REQUIRE(action->preset == CreateRoomPreset::TrustedPrivateChat);
+ REQUIRE(action.topic == "some topic");
+ REQUIRE(action.preset == CreateRoomPreset::TrustedPrivateChat);
}
diff --git a/src/tests/client/redact-test.cpp b/src/tests/client/redact-test.cpp
--- a/src/tests/client/redact-test.cpp
+++ b/src/tests/client/redact-test.cpp
@@ -15,26 +15,12 @@
#include <sdk.hpp>
#include <cprjobhandler.hpp>
#include <lagerstoreeventemitter.hpp>
-
+#include "action-mock-utils.hpp"
#include "client-test-util.hpp"
#include "factory.hpp"
using namespace Kazv::Factory;
-template<class Store, class Func>
-static auto getMockContext(SingleTypePromiseInterface<EffectStatus> &ph, Store &store, Func func)
-{
- return typename Client::ContextT([&ph, &store, func](const auto &action) {
- auto [cont, res] = func(action);
- if (!cont) {
- return ph.createResolved(res);
- }
-
- kzo.client.err() << "Unhandled action: index " << action.index();
- throw std::runtime_error{"unhandled action"};
- }, ph, lager::deps<>{});
-}
-
TEST_CASE("Redact a message", "[client][redact]")
{
ClientModel loggedInModel = makeClient({});
@@ -85,16 +71,14 @@
);
auto ctx = sdk.context();
- auto redactCalled = 0;
- auto redactAction = RedactEventAction{};
- auto mockContext = getMockContext(sgph, ctx, [&redactCalled, &redactAction](const auto &a) {
- if (std::holds_alternative<RedactEventAction>(a)) {
- ++redactCalled;
- redactAction = std::get<RedactEventAction>(a);
- return std::make_pair(/* cont = */ false, EffectStatus());
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+
+ auto dispatcher = getMockDispatcher(
+ sgph,
+ ctx,
+ returnEmpty<RedactEventAction>()
+ );
+
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!exampleroomid:example.com");
@@ -107,7 +91,10 @@
io.run();
- REQUIRE(redactCalled == 1);
+ REQUIRE(dispatcher.template calledTimes<RedactEventAction>() == 1);
+
+ auto redactAction = dispatcher.template of<RedactEventAction>()[0];
+
REQUIRE(redactAction.roomId == "!exampleroomid:example.com");
REQUIRE(redactAction.eventId == "$some-event");
REQUIRE(redactAction.reason == std::optional<std::string>("some reason"));
diff --git a/src/tests/client/room/local-echo-test.cpp b/src/tests/client/room/local-echo-test.cpp
--- a/src/tests/client/room/local-echo-test.cpp
+++ b/src/tests/client/room/local-echo-test.cpp
@@ -24,6 +24,7 @@
#include <sdk.hpp>
#include "client-test-util.hpp"
+#include "action-mock-utils.hpp"
#include "factory.hpp"
using namespace Kazv;
@@ -37,28 +38,22 @@
{"type", "m.room.message"},
};
-template<class Store, class Func>
-static auto getMockContext(SingleTypePromiseInterface<EffectStatus> &ph, Store &store, Func func)
+template<class Store, class ...Handlers>
+static auto makeDispatcher(SingleTypePromiseInterface<EffectStatus> &ph, Store &store, Handlers ...handlers)
{
- return typename Client::ContextT([&ph, &store, func](const auto &action) {
- static unsigned long long nextTxnId;
- kzo.client.dbg() << "dispatched: index " << action.index() << std::endl;
- auto [cont, res] = func(action);
- if (!cont) {
- return ph.createResolved(res);
- }
- if (std::holds_alternative<GetRoomStatesAction>(action)
- || std::holds_alternative<QueryKeysAction>(action)
- || std::holds_alternative<SendMessageAction>(action)) {
- return ph.createResolved(EffectStatus(true, json::object()));
- } else if (std::holds_alternative<SendMultipleToDeviceMessagesAction>(action)) {
- auto a = std::get<SendMultipleToDeviceMessagesAction>(action);
- return ph.createResolved(EffectStatus(true, json::object()));
- } else if (std::holds_alternative<ClaimKeysAction>(action)) {
- return ph.createResolved(EffectStatus(true, json::object({{"keyEvent", json::object({})}})));
- } else if (std::holds_alternative<PrepareForSharingRoomKeyAction>(action)) {
- auto a = std::get<PrepareForSharingRoomKeyAction>(action);
- auto txnId = std::to_string(nextTxnId++);
+ auto nextTxnId = std::make_shared<int>(0);
+ return getMockDispatcher(
+ ph,
+ store,
+ handlers...,
+ returnEmpty<GetRoomStatesAction>(),
+ returnEmpty<QueryKeysAction>(),
+ returnEmpty<SendMessageAction>(),
+ returnEmpty<SendMultipleToDeviceMessagesAction>(),
+ returnResolved<ClaimKeysAction>(EffectStatus(true,
+ json::object({{"keyEvent", json::object({})}}))),
+ makeHandler<PrepareForSharingRoomKeyAction>([nextTxnId]([[maybe_unused]] auto &ph, auto &store, const PrepareForSharingRoomKeyAction &a) {
+ auto txnId = std::to_string((*nextTxnId)++);
return store.dispatch(UpdateRoomAction{
a.roomId,
AddPendingRoomKeyAction{
@@ -72,16 +67,12 @@
}).then([txnId](auto &&) {
return EffectStatus{true, json::object({{"txnId", txnId}})};
});
- } else if (std::holds_alternative<SaveLocalEchoAction>(action)
- || std::holds_alternative<EncryptMegOlmEventAction>(action)
- || std::holds_alternative<RoomListAction>(action)
- || std::holds_alternative<UpdateLocalEchoStatusAction>(action)) {
- return store.dispatch(action);
- } else {
- kzo.client.err() << "Unhandled action: index " << action.index();
- throw std::runtime_error{"unhandled action"};
- }
- }, ph, lager::deps<>{});
+ }),
+ passDown<SaveLocalEchoAction>(),
+ passDown<EncryptMegOlmEventAction>(),
+ passDown<RoomListAction>(),
+ passDown<UpdateLocalEchoStatusAction>()
+ );
}
TEST_CASE("Local echo", "[client][room]")
@@ -188,21 +179,8 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto txnId = std::string();
- auto sendMessageEvent = Event();
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &txnId, &sendMessageEvent](const auto &a) {
- if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- sendMessageEvent = std::get<SendMessageAction>(a).event;
- REQUIRE(std::get<SendMessageAction>(a).txnId.has_value());
- txnId = std::get<SendMessageAction>(a).txnId.value();
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+ auto dispatcher = makeDispatcher(sgph, ctx);
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
@@ -218,12 +196,13 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 2);
- REQUIRE(sendMessageCalled == 1);
- REQUIRE(sendMessageEvent.encrypted());
- REQUIRE(sendMessageEvent.decrypted());
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 2);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 1);
+ auto sendMessageAction = dispatcher.template of<SendMessageAction>()[0];
+ REQUIRE(sendMessageAction.event.encrypted());
+ REQUIRE(sendMessageAction.event.decrypted());
REQUIRE(r.localEchoes().make().get().size() == 1);
- REQUIRE(r.localEchoes().make().get()[0].txnId == txnId);
+ REQUIRE(r.localEchoes().make().get()[0].txnId == sendMessageAction.txnId);
}
TEST_CASE("Local echo with encrypted event, loading room member failed", "[client][room]")
@@ -249,23 +228,10 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &megOlmEncryptCalled](const auto &a) {
- if (std::holds_alternative<GetRoomStatesAction>(a)) {
- auto err = json::object({{"errorCode", "400"}, {"error", "Cannot get room states"}});
- return std::make_pair(/* cont = */ false, EffectStatus(false, err));
- } else if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
-
+ auto dispatcher = makeDispatcher(sgph, ctx,
+ returnResolved<GetRoomStatesAction>(EffectStatus(false, json::object({{"errorCode", "400"}, {"error", "Cannot get room states"}})))
+ );
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!exampleroomid:example.com");
@@ -281,9 +247,9 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 1);
- REQUIRE(sendMessageCalled == 0);
- REQUIRE(megOlmEncryptCalled == 0);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 0);
REQUIRE(r.localEchoes().make().get().size() == 1);
REQUIRE(r.localEchoes().make().get()[0].status == LocalEchoDesc::Failed);
}
@@ -311,22 +277,10 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &megOlmEncryptCalled](const auto &a) {
- if (std::holds_alternative<QueryKeysAction>(a)) {
- auto err = json::object({{"errorCode", "400"}, {"error", "Cannot get room states"}});
- return std::make_pair(/* cont = */ false, EffectStatus(false, err));
- } else if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+ auto dispatcher = makeDispatcher(sgph, ctx,
+ returnResolved<QueryKeysAction>(EffectStatus(false, json::object({{"errorCode", "400"}, {"error", "Cannot get room states"}})))
+ );
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
@@ -343,9 +297,9 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 1);
- REQUIRE(sendMessageCalled == 0);
- REQUIRE(megOlmEncryptCalled == 0);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 0);
REQUIRE(r.localEchoes().make().get().size() == 1);
REQUIRE(r.localEchoes().make().get()[0].status == LocalEchoDesc::Failed);
}
@@ -385,21 +339,9 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &megOlmEncryptCalled](const auto &a) {
- if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- REQUIRE(std::get<SendMessageAction>(a).txnId.has_value());
- REQUIRE(std::get<SendMessageAction>(a).txnId.value() == "some-txn-id");
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+
+ auto dispatcher = makeDispatcher(sgph, ctx);
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
@@ -415,11 +357,14 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 0);
- REQUIRE(megOlmEncryptCalled == 0);
- REQUIRE(sendMessageCalled == 1);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 0);
REQUIRE(r.localEchoes().make().get().size() == 1);
REQUIRE(r.localEchoes().make().get()[0].txnId == "some-txn-id");
+ auto a = dispatcher.template of<SendMessageAction>()[0];
+ REQUIRE(a.txnId.has_value());
+ REQUIRE(a.txnId.value() == "some-txn-id");
}
TEST_CASE("Encrypted room: Resend encrypted local echo AND failed key share event", "[client][room]")
@@ -445,35 +390,23 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto sendToDeviceCalled = 0;
- auto olmEncryptCalled = 0;
auto resending = false;
-
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled,
- &sendMessageCalled, &megOlmEncryptCalled, &sendToDeviceCalled, &olmEncryptCalled, &resending](const auto &a) {
- if (std::holds_alternative<SendMultipleToDeviceMessagesAction>(a)) {
- auto sendKeyData = json{
- {"error", "Bad request"},
- {"errorCode", "400"},
- };
- ++sendToDeviceCalled;
+ auto dispatcher = makeDispatcher(sgph, ctx,
+ makeHandler<SendMultipleToDeviceMessagesAction>([&resending](auto &&ph, [[maybe_unused]] auto &&store, SendMultipleToDeviceMessagesAction) {
if (!resending) {
- return std::make_pair(/* cont = */ false, EffectStatus(/* succ = */ false, sendKeyData));
+ auto sendKeyData = json{
+ {"error", "Bad request"},
+ {"errorCode", "400"},
+ };
+ return HandlerResult<typename Client::PromiseT>{
+ ph.createResolved(EffectStatus(/* succ = */ false, sendKeyData))
+ };
}
- } else if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- } else if (std::holds_alternative<PrepareForSharingRoomKeyAction>(a)) {
- ++olmEncryptCalled;
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+ return HandlerResult<typename Client::PromiseT>{std::nullopt};
+ })
+ );
+
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
@@ -489,21 +422,18 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 2);
- REQUIRE(megOlmEncryptCalled == 1);
- REQUIRE(sendToDeviceCalled == 1);
- REQUIRE(olmEncryptCalled == 1);
- REQUIRE(sendMessageCalled == 0);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 2);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<SendMultipleToDeviceMessagesAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<PrepareForSharingRoomKeyAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 0);
+
REQUIRE(r.localEchoes().make().get().size() == 1);
auto savedEvent = r.localEchoes().make().get()[0];
auto txnId = savedEvent.txnId;
REQUIRE(savedEvent.event.encrypted());
- saveLocalEchoCalled = 0;
- megOlmEncryptCalled = 0;
- sendMessageCalled = 0;
- sendToDeviceCalled = 0;
- olmEncryptCalled = 0;
+ dispatcher.clear();
resending = true;
r.resendMessage(txnId)
@@ -515,11 +445,11 @@
io.restart();
io.run();
- REQUIRE(saveLocalEchoCalled == 0);
- REQUIRE(megOlmEncryptCalled == 0);
- REQUIRE(sendMessageCalled == 1);
- REQUIRE(olmEncryptCalled == 0);
- REQUIRE(sendToDeviceCalled == 1);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<PrepareForSharingRoomKeyAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<SendMultipleToDeviceMessagesAction>() == 1);
REQUIRE(r.pendingRoomKeyEvents().make().get().size() == 0);
}
@@ -554,26 +484,10 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto roomListActionCalled = 0;
- auto txnId = std::string();
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &megOlmEncryptCalled, &roomListActionCalled, &txnId](const auto &a) {
- if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<RoomListAction>(a)) {
- ++roomListActionCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- REQUIRE(std::get<SendMessageAction>(a).txnId.has_value());
- txnId = std::get<SendMessageAction>(a).txnId.value();
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
+ auto dispatcher = makeDispatcher(sgph, ctx);
+
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!exampleroomid:example.com");
@@ -589,13 +503,16 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 2);
- REQUIRE(roomListActionCalled == 2); // once for removing the existing local echo, once for removing pending key event
- REQUIRE(megOlmEncryptCalled == 1);
- REQUIRE(sendMessageCalled == 1);
+
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 2);
+ REQUIRE(dispatcher.template calledTimes<RoomListAction>() == 2); // once for removing the existing local echo, once for removing pending key event
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 1);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 1);
+ auto sendMessageAction = dispatcher.template of<SendMessageAction>()[0];
+ REQUIRE(sendMessageAction.txnId.has_value());
REQUIRE(r.localEchoes().make().get().size() == 2);
REQUIRE(r.localEchoes().make().get()[0].txnId == "some-other-txn-id");
- REQUIRE(r.localEchoes().make().get()[1].txnId == txnId);
+ REQUIRE(r.localEchoes().make().get()[1].txnId == sendMessageAction.txnId.value());
}
TEST_CASE("Unencrypted room: Resend unencrypted local echo", "[client][room]")
@@ -628,22 +545,8 @@
);
auto ctx = sdk.context();
- auto saveLocalEchoCalled = 0;
- auto sendMessageCalled = 0;
- auto megOlmEncryptCalled = 0;
- auto mockContext = getMockContext(sgph, ctx, [&saveLocalEchoCalled, &sendMessageCalled, &megOlmEncryptCalled](const auto &a) {
- if (std::holds_alternative<SaveLocalEchoAction>(a)) {
- ++saveLocalEchoCalled;
- } else if (std::holds_alternative<EncryptMegOlmEventAction>(a)) {
- ++megOlmEncryptCalled;
- } else if (std::holds_alternative<SendMessageAction>(a)) {
- ++sendMessageCalled;
- REQUIRE(std::get<SendMessageAction>(a).txnId.has_value());
- REQUIRE(std::get<SendMessageAction>(a).txnId.value() == "some-txn-id");
- }
- return std::make_pair(/* cont = */ true, EffectStatus());
- });
-
+ auto dispatcher = makeDispatcher(sgph, ctx);
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto r = client.room("!exampleroomid:example.com");
@@ -658,10 +561,12 @@
io.run();
- REQUIRE(saveLocalEchoCalled == 0);
- REQUIRE(megOlmEncryptCalled == 0);
- REQUIRE(sendMessageCalled == 1);
+ REQUIRE(dispatcher.template calledTimes<SaveLocalEchoAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<EncryptMegOlmEventAction>() == 0);
+ REQUIRE(dispatcher.template calledTimes<SendMessageAction>() == 1);
REQUIRE(r.localEchoes().make().get().size() == 1);
+ auto a = dispatcher.template of<SendMessageAction>()[0];
+ REQUIRE(a.txnId.value() == "some-txn-id");
REQUIRE(r.localEchoes().make().get()[0].txnId == "some-txn-id");
}
diff --git a/src/tests/client/room/read-receipt-test.cpp b/src/tests/client/room/read-receipt-test.cpp
--- a/src/tests/client/room/read-receipt-test.cpp
+++ b/src/tests/client/room/read-receipt-test.cpp
@@ -24,6 +24,7 @@
#include <testfixtures/factory.hpp>
#include "client-test-util.hpp"
+#include "action-mock-utils.hpp"
using namespace Kazv;
using namespace Kazv::Factory;
@@ -181,18 +182,12 @@
auto ctx = sdk.context();
- auto postReceiptCalled = 0;
- std::string postReceiptRoomId;
- std::string postReceiptEventId;
- auto mockContext = typename Client::ContextT([&sgph, &postReceiptCalled, &postReceiptRoomId, &postReceiptEventId](const auto &action) {
- if (std::holds_alternative<PostReceiptAction>(action)) {
- ++postReceiptCalled;
- postReceiptRoomId = std::get<PostReceiptAction>(action).roomId;
- postReceiptEventId = std::get<PostReceiptAction>(action).eventId;
- return sgph.createResolved(EffectStatus(true, json::object()));
- }
- throw std::runtime_error{"unhandled action"};
- }, sgph, lager::deps<>{});
+ auto dispatcher = getMockDispatcher(
+ sgph,
+ ctx,
+ returnEmpty<PostReceiptAction>()
+ );
+ auto mockContext = getMockContext(sgph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
auto room = client.room(r.roomId);
@@ -203,9 +198,10 @@
});
io.run();
- REQUIRE(postReceiptCalled == 1);
- REQUIRE(postReceiptRoomId == r.roomId);
- REQUIRE(postReceiptEventId == "$1");
+ REQUIRE(dispatcher.template calledTimes<PostReceiptAction>() == 1);
+ auto action = dispatcher.template of<PostReceiptAction>()[0];
+ REQUIRE(action.roomId == r.roomId);
+ REQUIRE(action.eventId == "$1");
}
TEST_CASE("PostReceiptAction", "[client][room][receipt]")

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 10:17 AM (13 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39345
Default Alt Text
D64.1732558643.diff (39 KB)

Event Timeline