Page MenuHomePhorge

account-data-test.cpp
No OneTemporary

Size
18 KB
Referenced Files
None
Subscribers
None

account-data-test.cpp

/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch_test_macros.hpp>
#include <lager/event_loop/boost_asio.hpp>
#include <boost/asio.hpp>
#include <cprjobhandler.hpp>
#include <asio-promise-handler.hpp>
#include <lagerstoreeventemitter.hpp>
#include <cursorutil.hpp>
#include <sdk-model.hpp>
#include <client/client.hpp>
#include "client-test-util.hpp"
#include "action-mock-utils.hpp"
#include "factory.hpp"
using namespace Kazv::Factory;
Event accountDataEvent = R"({
"type": "moe.kazv.mxc.kazv.some-event",
"content": {
"test": 1
}
})"_json;
TEST_CASE("Send set account data by room job", "[client][account-data]")
{
ClientModel loggedInModel = makeClient({});
auto [resModel, dontCareEffect] = ClientModel::update(
loggedInModel, SetAccountDataPerRoomAction{"!room:example.com", accountDataEvent});
assert1Job(resModel);
for1stJob(resModel, [] (const auto &job) {
REQUIRE(job.jobId() == "SetAccountDataPerRoom");
REQUIRE(job.url().find("/rooms/!room:example.com") != std::string::npos);
REQUIRE(job.url().find("/account_data/moe.kazv.mxc.kazv.some-event") != std::string::npos);
auto jsonBody = json::parse(std::get<BytesBody>(job.requestBody()));
REQUIRE(jsonBody == accountDataEvent.content().get());
});
}
TEST_CASE("Process account data by room response", "[client][account-data]")
{
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
auto store = createTestClientStore(ph);
WHEN("Success response")
{
auto succResponse = makeResponse("SetAccountDataPerRoom");
store.dispatch(ProcessResponseAction{succResponse})
.then([] (auto stat) {
REQUIRE(stat.success());
});
}
WHEN("Failed response")
{
auto failResponse = makeResponse("SetAccountDataPerRoom", withResponseJsonBody(R"({
"errcode": "M_FORBIDDEN",
"error": "Cannot add account data for other users."
})"_json));
failResponse.statusCode = 403;
store.dispatch(ProcessResponseAction{failResponse})
.then([] (auto stat) {
REQUIRE(!stat.success());
REQUIRE(stat.dataStr("error") == "Cannot add account data for other users.");
REQUIRE(stat.dataStr("errorCode") == "M_FORBIDDEN");
});
}
io.run();
}
TEST_CASE("Room::setAccountData()", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!room:example.com")
))
);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
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");
r.setAccountData(accountDataEvent)
.then([&io](auto) {
io.stop();
});
io.run();
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"({
"type": "m.tag",
"content": {
"tags": {
"m.favourite": 0
}
}
})"_json;
TEST_CASE("Room::addOrSetTag()", "[client][account-data][tagging]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!room:example.com")
| withRoomAccountData({tagEvent})
))
);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto expectedEvent = Event();
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");
SECTION("adding new tag")
{
auto expectedJson = tagEvent.raw().get();
expectedJson["content"]["tags"]["u.xxx"] = {{"order", 0.2}};
expectedEvent = expectedJson;
r.addOrSetTag("u.xxx", 0.2)
.then([&io](auto) {
io.stop();
});
}
SECTION("adding new tag, no order")
{
auto expectedJson = tagEvent.raw().get();
expectedJson["content"]["tags"]["u.xxx"] = json::object();
expectedEvent = expectedJson;
r.addOrSetTag("u.xxx")
.then([&io](auto) {
io.stop();
});
}
SECTION("updating existing tag")
{
auto expectedJson = tagEvent.raw().get();
expectedJson["content"]["tags"]["m.favourite"] = {{"order", 0.5}};
expectedEvent = expectedJson;
r.addOrSetTag("m.favourite", 0.5)
.then([&io](auto) {
io.stop();
});
}
SECTION("updating existing tag, no order")
{
auto expectedJson = tagEvent.raw().get();
expectedJson["content"]["tags"]["m.favourite"] = json::object();
expectedEvent = expectedJson;
r.addOrSetTag("m.favourite")
.then([&io](auto) {
io.stop();
});
}
io.run();
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]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!room:example.com")
| withRoomAccountData({tagEvent})
))
);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto expectedEvent = Event();
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");
SECTION("removing existing tag")
{
auto expectedJson = tagEvent.raw().get();
expectedJson["content"]["tags"] = json::object();
expectedEvent = expectedJson;
r.removeTag("m.favourite")
.then([&io](auto) {
io.stop();
});
}
SECTION("removing non-existent tag")
{
expectedEvent = tagEvent;
r.removeTag("u.xxx")
.then([&io](auto) {
io.stop();
});
}
io.run();
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]")
{
ClientModel loggedInModel = makeClient({});
auto [resModel, dontCareEffect] = ClientModel::update(
loggedInModel, SetAccountDataAction{accountDataEvent});
assert1Job(resModel);
for1stJob(resModel, [loggedInModel] (const auto &job) {
REQUIRE(job.jobId() == "SetAccountData");
REQUIRE(job.url().find("/user/" + loggedInModel.userId) != std::string::npos);
REQUIRE(job.url().find("/account_data/moe.kazv.mxc.kazv.some-event") != std::string::npos);
auto jsonBody = json::parse(std::get<BytesBody>(job.requestBody()));
REQUIRE(jsonBody == accountDataEvent.content().get());
});
}
TEST_CASE("Process account data response", "[client][account-data]")
{
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
auto store = createTestClientStore(ph);
WHEN("Success response")
{
auto succResponse = makeResponse("SetAccountData");
store.dispatch(ProcessResponseAction{succResponse})
.then([] (auto stat) {
REQUIRE(stat.success());
});
}
WHEN("Failed response")
{
auto failResponse = makeResponse("SetAccountData", withResponseJsonBody(R"({
"errcode": "M_FORBIDDEN",
"error": "Cannot add account data for other users."
})"_json));
failResponse.statusCode = 403;
store.dispatch(ProcessResponseAction{failResponse})
.then([] (auto stat) {
REQUIRE(!stat.success());
REQUIRE(stat.dataStr("error") == "Cannot add account data for other users.");
REQUIRE(stat.dataStr("errorCode") == "M_FORBIDDEN");
});
}
io.run();
}
TEST_CASE("Client::accountData()", "[client][account-data]")
{
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
ClientModel m = makeClient(
withAccountData({accountDataEvent})
);
auto store = createTestClientStoreFrom(m, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store, std::nullopt);
REQUIRE(client.accountData().make().get() == m.accountData);
}
TEST_CASE("Client::setAccountData()", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.setAccountData(accountDataEvent)
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
auto a = dispatcher.template of<SetAccountDataAction>()[0];
REQUIRE(a.accountDataEvent == accountDataEvent);
}
TEST_CASE("Client::addDirectRoom()", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.addDirectRoom("@mew:example.org", "!somewhere:example.org")
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
auto a = dispatcher.template of<SetAccountDataAction>()[0];
Event mDirectEvent = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere:example.org"]
}
})"_json;
REQUIRE(a.accountDataEvent == mDirectEvent);
}
TEST_CASE("Client::addDirectRoom(), when roomId is already in m.direct", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
Event mDirectEvent = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere:example.org"]
}
})"_json;
m.accountData = m.accountData.set("m.direct", mDirectEvent);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.addDirectRoom("@mew:example.org", "!somewhere:example.org")
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 0);
}
TEST_CASE("Client::addDirectRoom(), when m.direct content is ill-format", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
Event mDirectEvent = R"({
"type": "m.direct",
"content": {
"@mew:example.org": "!somewrongwhere:example.org"
}
})"_json;
m.accountData = m.accountData.set("m.direct", mDirectEvent);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.addDirectRoom("@mew:example.org", "!somewhere:example.org")
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
auto a = dispatcher.template of<SetAccountDataAction>()[0];
Event mDirectEventNew = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere:example.org"]
}
})"_json;
REQUIRE(a.accountDataEvent == mDirectEventNew);
}
TEST_CASE("Client::addDirectRoom(), when m.direct content contains other user's rooms", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
Event mDirectEvent = R"({
"type": "m.direct",
"content": {
"@mew2:example.org": ["!somewhere2:example.org", "!somewhere3:example.org"]
}
})"_json;
m.accountData = m.accountData.set("m.direct", mDirectEvent);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.addDirectRoom("@mew:example.org", "!somewhere:example.org")
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
auto a = dispatcher.template of<SetAccountDataAction>()[0];
Event mDirectEventNew = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere:example.org"],
"@mew2:example.org": ["!somewhere2:example.org", "!somewhere3:example.org"]
}
})"_json;
REQUIRE(a.accountDataEvent == mDirectEventNew);
}
TEST_CASE("Client::addDirectRoom(), when m.direct content contains the user's other rooms", "[client][account-data]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient({});
Event mDirectEvent = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere4:example.org"]
}
})"_json;
m.accountData = m.accountData.set("m.direct", mDirectEvent);
auto jh = Kazv::CprJobHandler{io.get_executor()};
auto ee = Kazv::LagerStoreEventEmitter(lager::with_boost_asio_event_loop{io.get_executor()});
auto sdk = Kazv::makeSdk(
SdkModel{m},
jh,
ee,
Kazv::AsioPromiseHandler{io.get_executor()},
zug::identity
);
auto ctx = sdk.context();
auto dispatcher = getMockDispatcher(
ph,
ctx,
returnEmpty<SetAccountDataAction>()
);
auto mockContext = getMockContext(ph, dispatcher);
auto client = Client(Client::InEventLoopTag{}, mockContext, sdk.context());
client.addDirectRoom("@mew:example.org", "!somewhere:example.org")
.then([&io](auto) {
io.stop();
});
io.run();
REQUIRE(dispatcher.template calledTimes<SetAccountDataAction>() == 1);
auto a = dispatcher.template of<SetAccountDataAction>()[0];
Event mDirectEventNew = R"({
"type": "m.direct",
"content": {
"@mew:example.org": ["!somewhere4:example.org", "!somewhere:example.org"]
}
})"_json;
REQUIRE(a.accountDataEvent == mDirectEventNew);
}

File Metadata

Mime Type
text/x-c
Expires
Sat, Oct 25, 1:43 PM (3 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
553070
Default Alt Text
account-data-test.cpp (18 KB)

Event Timeline