Page MenuHomePhorge

send-test.cpp
No OneTemporary

Size
11 KB
Referenced Files
None
Subscribers
None

send-test.cpp

/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <lager/event_loop/boost_asio.hpp>
#include <catch2/catch_all.hpp>
#include <boost/asio.hpp>
#include <asio-promise-handler.hpp>
#include <cursorutil.hpp>
#include <sdk-model.hpp>
#include <client/client.hpp>
#include "client-test-util.hpp"
#include "factory.hpp"
using namespace Kazv::Factory;
TEST_CASE("Send a message", "[client][send]")
{
ClientModel loggedInModel = makeClient({});
auto [resModel, dontCareEffect] = ClientModel::update(
loggedInModel, SendMessageAction{"!foo:tusooa.xyz", json{
{"type", "m.room.message"},
{"content", {{"foo", "bar"}}},
}});
assert1Job(resModel);
for1stJob(resModel, [] (const auto &job) {
REQUIRE(job.jobId() == "SendMessage");
REQUIRE(job.dataStr("roomId") == "!foo:tusooa.xyz");
REQUIRE(job.dataStr("txnId") != "");
});
}
TEST_CASE("Send a message with specified txnId", "[client][send]")
{
ClientModel loggedInModel = makeClient({});
auto [resModel, dontCareEffect] = ClientModel::update(
loggedInModel, SendMessageAction{"!foo:tusooa.xyz", json{
{"type", "m.room.message"},
{"content", {{"foo", "bar"}}},
}, "some-txn-id"});
assert1Job(resModel);
for1stJob(resModel, [] (const auto &job) {
REQUIRE(job.jobId() == "SendMessage");
REQUIRE(job.dataStr("roomId") == "!foo:tusooa.xyz");
REQUIRE(job.dataStr("txnId") == "some-txn-id");
});
}
TEST_CASE("Refuse to send unencrypted event to encrypted room", "[client][send]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withCrypto(makeCrypto({}))
| withRoom(makeRoom(
withRoomId("!exampleroomid:example.com")
| withRoomEncrypted(true)
))
);
auto store = createTestClientStoreFrom(m, ph);
store.dispatch(SendMessageAction{
"!exampleroomid:example.com",
Event{json{{"content", {{"foo", "bar"}}}, {"type", "m.room.message"}}},
})
.then([](auto status) {
REQUIRE(!status.success());
REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_SENDING_UNENCRYPTED_EVENT_TO_ENCRYPTED_ROOM");
});
io.run();
}
TEST_CASE("Ok to send unencrypted state event to encrypted room", "[client][send]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withCrypto(makeCrypto({}))
| withRoom(makeRoom(
withRoomId("!exampleroomid:example.com")
| withRoomEncrypted(true)
))
);
auto store = createTestClientStoreFrom(m, ph);
store.dispatch(SendMessageAction{
"!exampleroomid:example.com",
Event{json{
{"content", {{"foo", "bar"}}},
{"type", "m.room.xxx"},
{"state_key", ""},
}},
})
.then([](auto status) {
REQUIRE(status.success());
});
io.run();
}
TEST_CASE("Refuse to send unencrypted m.room_key event", "[client][send]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withCrypto(makeCrypto({}))
| withRoom(makeRoom(
withRoomId("!exampleroomid:example.com")
| withRoomEncrypted(true)
))
);
auto store = createTestClientStoreFrom(m, ph);
store.dispatch(SendToDeviceMessageAction{
Event{json{{"content", {{"foo", "bar"}}}, {"type", "m.room_key"}}},
{},
})
.then([](auto status) {
REQUIRE(!status.success());
REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED");
});
io.run();
}
TEST_CASE("SendMultipleToDeviceMessagesAction", "[client][send]")
{
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
ClientModel m = makeClient(
withCrypto(makeCrypto({}))
| withRoom(makeRoom(
withRoomId("!exampleroomid:example.com")
| withRoomEncrypted(true)
))
);
auto store = createTestClientStoreFrom(m, ph);
SECTION("Reject unencrypted m.room_key event")
{
store.dispatch(SendMultipleToDeviceMessagesAction{
{{"@foo:example.com",
{{"device1", Event{json{{"content", {{"foo", "bar"}}}, {"type", "m.room_key"}}}}},
}},
})
.then([](auto status) {
REQUIRE(!status.success());
REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED");
});
}
SECTION("Make a job")
{
store.dispatch(SendMultipleToDeviceMessagesAction{
{{"@foo:example.com",
{{"device1", Event{json{{"content", {{"foo", "1"}}}, {"type", "moe.kazv.mxc.some-type"}}}},
{"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
}},
})
.then([&store](auto status) {
REQUIRE(status.success());
auto next = store.reader().make().get();
assert1Job(next);
for1stJob(next, [](const BaseJob &job) {
auto expectedBody = json{
{"messages", {
{"@foo:example.com", {
{"device1", {{"foo", "1"}}},
{"device2", {{"foo", "2"}}},
}},
}},
};
REQUIRE(json::parse(std::get<Bytes>(job.requestBody())) == expectedBody);
REQUIRE(job.url().find("/sendToDevice/moe.kazv.mxc.some-type/") != std::string::npos);
});
});
}
SECTION("Make no job if there is nothing to send")
{
store.dispatch(SendMultipleToDeviceMessagesAction{
{{"@foo:example.com",
{},
}},
})
.then([&store](auto status) {
REQUIRE(status.success());
auto next = store.reader().make().get();
REQUIRE(next.nextJobs.empty());
});
}
SECTION("Reject if events are not of the same type")
{
store.dispatch(SendMultipleToDeviceMessagesAction{
{{"@foo:example.com",
{{"device1", Event{json{{"content", {{"foo", "1"}}}, {"type", "moe.kazv.mxc.some-type-2"}}}},
{"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
}},
})
.then([](auto status) {
REQUIRE(!status.success());
REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_TYPE_NOT_SAME");
});
}
SECTION("Reject if some event is not well-formed")
{
store.dispatch(SendMultipleToDeviceMessagesAction{
{{"@foo:example.com",
{{"device1", Event{json{{"type", "moe.kazv.mxc.some-type"}}}},
{"device2", Event{json{{"content", {{"foo", "2"}}}, {"type", "moe.kazv.mxc.some-type"}}}}},
}},
})
.then([](auto status) {
REQUIRE(!status.success());
REQUIRE(status.dataStr("errorCode") == "MOE_KAZV_MXC_INVALID_EVENT_FORMAT");
});
}
io.run();
}
TEST_CASE("UpdateLocalEchoStatusAction: update one does not exist", "[client][send]")
{
ClientModel loggedInModel = makeClient(
withRoom(makeRoom(
withRoomId("!foo:tusooa.xyz")
| withRoomEncrypted(true)
))
);
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
auto store = createTestClientStoreFrom(loggedInModel, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
auto room = client.room("!foo:tusooa.xyz");
store.dispatch(UpdateLocalEchoStatusAction{
"!foo:tusooa.xyz",
"some-txn-id",
LocalEchoDesc::Failed,
})
.then([](auto status) {
REQUIRE(!status.success());
});
io.run();
auto localEchoes = room.localEchoes().make().get();
REQUIRE(localEchoes.size() == 0);
}
TEST_CASE("UpdateLocalEchoStatusAction: update one that exists", "[client][send]")
{
ClientModel loggedInModel = makeClient(
withRoom(makeRoom(
withRoomId("!foo:tusooa.xyz")
| withAttr(&RoomModel::localEchoes, {{"some-txn-id", Event()}})
))
);
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
auto store = createTestClientStoreFrom(loggedInModel, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
auto room = client.room("!foo:tusooa.xyz");
store.dispatch(UpdateLocalEchoStatusAction{
"!foo:tusooa.xyz",
"some-txn-id",
LocalEchoDesc::Failed,
})
.then([](auto status) {
REQUIRE(status.success());
});
io.run();
auto localEchoes = room.localEchoes().make().get();
REQUIRE(localEchoes.size() == 1);
REQUIRE(localEchoes[0].txnId == "some-txn-id");
REQUIRE(localEchoes[0].status == LocalEchoDesc::Failed);
}
TEST_CASE("UpdateLocalEchoStatusAction: room does not exist", "[client][send]")
{
ClientModel loggedInModel = makeClient({});
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
auto store = createTestClientStoreFrom(loggedInModel, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
auto room = client.room("!foo:tusooa.xyz");
store.dispatch(UpdateLocalEchoStatusAction{
"!foo:tusooa.xyz",
"some-txn-id",
LocalEchoDesc::Failed,
})
.then([](auto status) {
REQUIRE(!status.success());
});
io.run();
auto localEchoes = room.localEchoes().make().get();
REQUIRE(localEchoes.size() == 0);
}
TEST_CASE("Room::removeLocalEcho()", "[client][send]")
{
auto roomModel = makeRoom();
roomModel.localEchoes = roomModel.localEchoes.push_back({"txn-id-1", Event()});
ClientModel loggedInModel = makeClient(withRoom(roomModel));
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
auto store = createTestClientStoreFrom(loggedInModel, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
auto room = client.room(roomModel.roomId);
room.removeLocalEcho("txn-id-1")
.then([](auto status) {
REQUIRE(status.success());
});
io.run();
auto localEchoes = room.localEchoes().make().get();
REQUIRE(localEchoes.size() == 0);
}
TEST_CASE("Room::removeLocalEcho(): removing a non-existent local echo", "[client][send]")
{
auto roomModel = makeRoom();
roomModel.localEchoes = roomModel.localEchoes.push_back({"txn-id-1", Event()});
ClientModel loggedInModel = makeClient(withRoom(roomModel));
boost::asio::io_context io;
SingleTypePromiseInterface<EffectStatus> ph{AsioPromiseHandler{io.get_executor()}};
auto store = createTestClientStoreFrom(loggedInModel, ph);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
auto room = client.room(roomModel.roomId);
room.removeLocalEcho("txn-id-2")
.then([](auto status) {
REQUIRE(status.success());
});
io.run();
auto localEchoes = room.localEchoes().make().get();
REQUIRE(localEchoes.size() == 1);
}

File Metadata

Mime Type
text/x-c
Expires
Tue, Jun 24, 11:16 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
234529
Default Alt Text
send-test.cpp (11 KB)

Event Timeline