Newer
Older
#include <boost/algorithm/string.hpp>
#include "mtx/events/collections.hpp"
#include "mtx/events/encrypted.hpp"
#include "mtx/requests.hpp"
#include "mtx/responses.hpp"
#include "mtxclient/http/client.hpp"
#include "test_helpers.hpp"
using namespace mtx::http;
using namespace mtx::events::collections;
using namespace mtx::requests;
TEST(ClientAPI, Register)
{
auto user = std::make_shared<Client>("localhost");
user->registration("alice", "secret", [](const mtx::responses::Register &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_USER_IN_USE");
});
auto username = utils::random_token(10, false);
// Synapse converts the username to lowercase.
boost::algorithm::to_lower(username);
user->registration(
username, "secret", [user, username](const mtx::responses::Register &, RequestErr err) {
if (!err || err->matrix_error.unauthorized.flows.size() == 0)
EXPECT_EQ(err->matrix_error.unauthorized.flows.size(), 1);
EXPECT_EQ(err->matrix_error.unauthorized.flows[0].stages[0], "m.login.dummy");
{err->matrix_error.unauthorized.session, mtx::user_interactive::auth::Dummy{}},
[username](const mtx::responses::Register &res, RequestErr err) {
const auto user_id = "@" + username + ":localhost";
check_error(err);
EXPECT_EQ(res.user_id.to_string(), user_id);
});
TEST(ClientAPI, LoginSuccess)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login("alice", "secret", [](const mtx::responses::Login &res, RequestErr err) {
mtx_client->login("bob", "secret", [](const mtx::responses::Login &res, RequestErr err) {
mtx_client->login("carl", "secret", [](const mtx::responses::Login &res, RequestErr err) {
validate_login("@carl:localhost", res);
});
mtx_client->close();
}
TEST(ClientAPI, LoginWrongPassword)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login(
"alice", "wrong_password", [](const mtx::responses::Login &res, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_FORBIDDEN");
EXPECT_EQ(err->status_code, boost::beast::http::status::forbidden);
EXPECT_EQ(res.device_id, "");
EXPECT_EQ(res.access_token, "");
});
mtx_client->close();
}
TEST(ClientAPI, LoginWrongUsername)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login("john", "secret", [](const mtx::responses::Login &res, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_FORBIDDEN");
EXPECT_EQ(err->status_code, boost::beast::http::status::forbidden);
EXPECT_EQ(res.device_id, "");
EXPECT_EQ(res.access_token, "");
});
mtx_client->close();
}
TEST(ClientAPI, LoginFlows)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->get_login([](const mtx::responses::LoginFlows &res, RequestErr err) {
ASSERT_FALSE(err);
EXPECT_EQ(res.flows[0].type, mtx::user_interactive::auth_types::password);
});
mtx_client->close();
}
TEST(ClientAPI, SSORedirect)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
EXPECT_EQ(mtx_client->login_sso_redirect("http://aaa:555/sso"),
"https://localhost:443/_matrix/client/r0/login/sso/"
"redirect?redirectUrl=http%3A%2F%2Faaa%3A555%2Fsso");
TEST(ClientAPI, EmptyUserAvatar)
{
auto alice = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &res, RequestErr err) {
ASSERT_FALSE(err);
auto const alice_id = res.user_id;
alice->set_avatar_url("", [alice, alice_id](RequestErr err) {
ASSERT_FALSE(err);
auto done = false;
alice->get_profile(
alice_id.to_string(),
[&done](const mtx::responses::Profile &res, RequestErr err) {
ASSERT_FALSE(err);
ASSERT_TRUE(res.avatar_url.size() == 0);
done = true;
});
while (!done)
alice->get_avatar_url(
alice_id.to_string(),
[](const mtx::responses::AvatarUrl &res, RequestErr err) {
ASSERT_FALSE(err);
ASSERT_TRUE(res.avatar_url.size() == 0);
});
});
});
alice->close();
}
TEST(ClientAPI, RealUserAvatar)
{
auto alice = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &res, RequestErr err) {
ASSERT_FALSE(err);
auto const alice_id = res.user_id;
auto const avatar_url = "mxc://matrix.org/wefh34uihSDRGhw34";
alice->set_avatar_url(avatar_url, [alice, alice_id, avatar_url](RequestErr err) {
ASSERT_FALSE(err);
auto done = false;
alice->get_profile(
[avatar_url, &done](const mtx::responses::Profile &res, RequestErr err) {
ASSERT_FALSE(err);
ASSERT_TRUE(res.avatar_url == avatar_url);
done = true;
});
while (!done)
alice->get_avatar_url(
[avatar_url](const mtx::responses::AvatarUrl &res, RequestErr err) {
ASSERT_FALSE(err);
ASSERT_TRUE(res.avatar_url == avatar_url);
});
});
});
alice->close();
}
TEST(ClientAPI, ChangeDisplayName)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login(
"alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
// Change the display name to Arthur Dent and verify its success through the lack
// of an error
mtx_client->set_displayname("Arthur Dent",
[](RequestErr err) { check_error(err); });
});
mtx_client->close();
}
TEST(ClientAPI, EmptyDisplayName)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login(
"alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
// Change the display name to an empty string and verify its success through the
// lack of an error
mtx_client->set_displayname("", [](RequestErr err) { check_error(err); });
});
mtx_client->close();
}
TEST(ClientAPI, CreateRoom)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login(
"alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
while (mtx_client->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
mtx_client->create_room(req, [](const mtx::responses::CreateRoom &res, RequestErr err) {
ASSERT_TRUE(res.room_id.localpart().size() > 10);
EXPECT_EQ(res.room_id.hostname(), "localhost");
});
mtx_client->close();
}
TEST(ClientAPI, LogoutSuccess)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
std::string token;
// Login and prove that login was successful by creating a room
mtx_client->login(
"alice", "secret", [&token](const mtx::responses::Login &res, RequestErr err) {
while (token.empty())
sleep();
mtx_client->set_access_token(token);
mtx::requests::CreateRoom req;
req.name = "Test1";
req.topic = "Topic1";
req, [](const mtx::responses::CreateRoom &, RequestErr err) { check_error(err); });
// Logout and prove that logout was successful and deleted the access_token_ for the client
mtx_client->logout([mtx_client, &token](const mtx::responses::Logout &, RequestErr err) {
while (token.size())
sleep();
// Verify that sending requests with this mtx_client fails after logout
mtx::requests::CreateRoom failReq;
failReq.name = "42";
failReq.topic = "LifeUniverseEverything";
mtx_client->create_room(failReq, [](const mtx::responses::CreateRoom &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_MISSING_TOKEN");
});
mtx_client->close();
}
TEST(ClientAPI, LogoutInvalidatesTokenOnServer)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
std::string token;
// Login and prove that login was successful by creating a room
mtx_client->login(
"alice", "secret", [&token](const mtx::responses::Login &res, RequestErr err) {
while (token.empty())
sleep();
mtx_client->set_access_token(token);
mtx::requests::CreateRoom req;
req.name = "Test1";
req.topic = "Topic1";
req, [](const mtx::responses::CreateRoom &, RequestErr err) { check_error(err); });
// Logout and prove that logout was successful by verifying the old access_token_ is no
// longer valid
mtx_client->logout([mtx_client, &token](const mtx::responses::Logout &, RequestErr err) {
mtx_client->set_access_token(token);
token.clear();
});
while (token.size())
sleep();
// Verify that creating a room with the old access_token_ no longer succeeds after logout
mtx::requests::CreateRoom failReq;
failReq.name = "42";
failReq.topic = "LifeUniverseEverything";
mtx_client->create_room(failReq, [](const mtx::responses::CreateRoom &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_UNKNOWN_TOKEN");
});
mtx_client->close();
}
TEST(ClientAPI, CreateRoomInvites)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
auto carl = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
carl->login("carl", "secret", [carl](const mtx::responses::Login &, RequestErr err) {
while (alice->access_token().empty() || bob->access_token().empty() ||
carl->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {"@bob:localhost", "@carl:localhost"};
alice->create_room(req, [bob, carl](const mtx::responses::CreateRoom &res, RequestErr err) {
auto room_id = res.room_id.to_string();
bob->join_room(room_id,
[](const nlohmann::json &, RequestErr err) { check_error(err); });
carl->join_room(room_id,
[](const nlohmann::json &, RequestErr err) { check_error(err); });
});
alice->close();
bob->close();
carl->close();
}
TEST(ClientAPI, JoinRoom)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
// Creating a random room alias.
// TODO: add a type for room aliases.
const auto alias = utils::random_token(20, false);
req.name = "Name";
req.topic = "Topic";
req.invite = {"@bob:localhost"};
req.room_alias_name = alias;
alice->create_room(
req, [bob, alias](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
bob->join_room(room_id,
[](const nlohmann::json &, RequestErr err) { check_error(err); });
using namespace mtx::identifiers;
bob->join_room(
"!random_room_id:localhost", [](const nlohmann::json &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode),
"M_UNRECOGNIZED");
});
// Join the room using an alias.
bob->join_room("#" + alias + ":localhost",
[](const nlohmann::json &, RequestErr err) { check_error(err); });
});
TEST(ClientAPI, LeaveRoom)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {"@bob:localhost"};
alice->create_room(req, [bob](const mtx::responses::CreateRoom &res, RequestErr err) {
bob->join_room(res.room_id.to_string(),
[room_id, bob](const nlohmann::json &, RequestErr err) {
check_error(err);
bob->leave_room(room_id.to_string(),
[](const nlohmann::json &, RequestErr err) {
check_error(err);
});
});
});
// Trying to leave a non-existent room should fail.
bob->leave_room("!random_room_id:localhost", [](const nlohmann::json &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_UNRECOGNIZED");
EXPECT_EQ(err->matrix_error.error, "Not a known room");
});
alice->close();
bob->close();
}
TEST(ClientAPI, InviteRoom)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
alice->invite_user(room_id,
"@bob:localhost",
[room_id, bob](const mtx::responses::Empty &, RequestErr err) {
check_error(err);
bob->join_room(
room_id, [](const nlohmann::json &, RequestErr err) {
check_error(err);
});
});
});
alice->close();
bob->close();
}
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
TEST(ClientAPI, KickRoom)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
sleep();
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
alice->invite_user(
room_id,
"@bob:localhost",
[room_id, alice, bob](const mtx::responses::Empty &, RequestErr err) {
check_error(err);
bob->join_room(
room_id, [alice, room_id](const nlohmann::json &, RequestErr err) {
check_error(err);
alice->kick_user(room_id,
"@bob:localhost",
[](const mtx::responses::Empty &,
RequestErr err) { check_error(err); });
});
});
});
alice->close();
bob->close();
}
TEST(ClientAPI, BanRoom)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
sleep();
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
alice->invite_user(
room_id,
"@bob:localhost",
[room_id, alice, bob](const mtx::responses::Empty &, RequestErr err) {
check_error(err);
bob->join_room(
room_id, [alice, room_id](const nlohmann::json &, RequestErr err) {
check_error(err);
alice->ban_user(
room_id,
"@bob:localhost",
[alice, room_id](const mtx::responses::Empty &,
RequestErr err) {
check_error(err);
alice->unban_user(
room_id,
"@bob:localhost",
[](const mtx::responses::Empty &,
RequestErr err) { check_error(err); },
"You not bad anymore!");
},
"You bad!");
});
});
});
alice->close();
bob->close();
}
TEST(ClientAPI, InvalidInvite)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
req.invite = {};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
bob->invite_user(room_id,
"@carl:localhost",
[room_id, bob](const mtx::responses::Empty &, RequestErr err) {
ASSERT_TRUE(err);
EXPECT_EQ(
mtx::errors::to_string(err->matrix_error.errcode),
"M_FORBIDDEN");
});
});
alice->close();
bob->close();
}
TEST(ClientAPI, Sync)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->login(
"alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
while (mtx_client->access_token().empty())
mtx::requests::CreateRoom req;
req.name = "Name";
req.topic = "Topic";
mtx_client->create_room(
req, [mtx_client](const mtx::responses::CreateRoom &, RequestErr err) {
check_error(err);
SyncOpts opts;
opts.timeout = 0;
mtx_client->sync(opts, [](const mtx::responses::Sync &res, RequestErr err) {
check_error(err);
ASSERT_TRUE(res.rooms.join.size() > 0);
ASSERT_TRUE(res.next_batch.size() > 0);
});
TEST(ClientAPI, Versions)
{
std::shared_ptr<Client> mtx_client = std::make_shared<Client>("localhost");
mtx_client->versions([](const mtx::responses::Versions &res, RequestErr err) {
EXPECT_EQ(res.versions.size(), 6);
EXPECT_EQ(res.versions.at(0), "r0.0.1");
EXPECT_EQ(res.versions.at(1), "r0.1.0");
EXPECT_EQ(res.versions.at(2), "r0.2.0");
EXPECT_EQ(res.versions.at(3), "r0.3.0");
EXPECT_EQ(res.versions.at(4), "r0.4.0");
EXPECT_EQ(res.versions.at(5), "r0.5.0");
TEST(ClientAPI, Typing)
{
auto alice = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty())
mtx::requests::CreateRoom req;
alice->create_room(req, [alice](const mtx::responses::CreateRoom &res, RequestErr err) {
alice->start_typing(res.room_id.to_string(), 10000, [alice, res](RequestErr err) {
const auto room_id = res.room_id.to_string();
SyncOpts opts;
opts.timeout = 0;
alice->sync(opts,
[room_id, &can_continue](const mtx::responses::Sync &res,
RequestErr err) {
check_error(err);
auto room = res.rooms.join.at(room_id);
EXPECT_EQ(room.ephemeral.typing.size(), 1);
EXPECT_EQ(room.ephemeral.typing.front(),
"@alice:localhost");
});
while (!can_continue)
alice->stop_typing(
res.room_id.to_string(), [alice, room_id](RequestErr err) {
check_error(err);
SyncOpts opts;
opts.timeout = 0;
alice->sync(
opts,
[room_id](const mtx::responses::Sync &res, RequestErr err) {
check_error(err);
auto room = res.rooms.join.at(room_id);
EXPECT_EQ(room.ephemeral.typing.size(), 0);
});
});
});
});
alice->close();
}
TEST(ClientAPI, SendMessages)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
mtx::requests::CreateRoom req;
req.invite = {"@bob:localhost"};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
room_id, [alice, bob, room_id](const nlohmann::json &, RequestErr err) {
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
// Flag to indicate when those messages would be ready to be read by
// alice.
vector<string> event_ids;
mtx::events::msg::Text text;
text.body = "hello alice!";
bob->send_room_message<mtx::events::msg::Text,
mtx::events::EventType::RoomMessage>(
room_id,
text,
[&event_ids](const mtx::responses::EventId &res, RequestErr err) {
event_ids.push_back(res.event_id.to_string());
check_error(err);
});
mtx::events::msg::Emote emote;
emote.body = "*bob tests";
bob->send_room_message<mtx::events::msg::Emote,
mtx::events::EventType::RoomMessage>(
room_id,
emote,
[&event_ids](const mtx::responses::EventId &res, RequestErr err) {
event_ids.push_back(res.event_id.to_string());
check_error(err);
});
while (event_ids.size() != 2)
SyncOpts opts;
opts.timeout = 0;
alice->sync(opts,
[room_id, event_ids](const mtx::responses::Sync &res,
RequestErr err) {
check_error(err);
auto ids = get_event_ids<TimelineEvents>(
res.rooms.join.at(room_id).timeline.events);
// The sent event ids should be visible in the
// timeline.
for (const auto &event_id : event_ids)
ASSERT_TRUE(std::find(ids.begin(),
ids.end(),
event_id) !=
std::end(ids));
});
alice->close();
bob->close();
}
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
TEST(ClientAPI, RedactEvent)
{
auto alice = std::make_shared<Client>("localhost");
alice->login("alice", "secret", check_login);
while (alice->access_token().empty())
sleep();
mtx::requests::CreateRoom req;
alice->create_room(req, [alice](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id.to_string();
mtx::events::msg::Text text;
text.body = "hello alice!";
alice
->send_room_message<mtx::events::msg::Text, mtx::events::EventType::RoomMessage>(
room_id,
text,
[room_id, alice](const mtx::responses::EventId &res, RequestErr err) {
check_error(err);
alice->redact_event(
room_id,
res.event_id.to_string(),
[](const mtx::responses::EventId &res, RequestErr err) {
check_error(err);
ASSERT_FALSE(res.event_id.to_string().empty());
});
});
});
alice->close();
}
TEST(ClientAPI, SendStateEvents)
{
auto alice = std::make_shared<Client>("localhost");
auto bob = std::make_shared<Client>("localhost");
alice->login("alice", "secret", [alice](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
bob->login("bob", "secret", [bob](const mtx::responses::Login &, RequestErr err) {
check_error(err);
});
while (alice->access_token().empty() || bob->access_token().empty())
mtx::requests::CreateRoom req;
req.invite = {"@bob:localhost"};
alice->create_room(
req, [alice, bob](const mtx::responses::CreateRoom &res, RequestErr err) {
check_error(err);
auto room_id = res.room_id;
// Flag to indicate when those messages would be ready to be read by
// alice.
vector<string> event_ids;
mtx::events::state::Name event;
event.name = "Bob's room";
bob->send_state_event<mtx::events::state::Name, mtx::events::EventType::RoomName>(
room_id.to_string(),
event,
[](const mtx::responses::EventId &, RequestErr err) {
ASSERT_TRUE(err);
ASSERT_EQ("M_FORBIDDEN",
mtx::errors::to_string(err->matrix_error.errcode));
});
mtx::events::state::Name name_event;
name_event.name = "Alice's room";
alice
->send_state_event<mtx::events::state::Name, mtx::events::EventType::RoomName>(
room_id.to_string(),
name_event,
[&event_ids](const mtx::responses::EventId &res, RequestErr err) {
check_error(err);
event_ids.push_back(res.event_id.to_string());
});
mtx::events::state::Avatar avatar;
avatar.url = "mxc://localhost/random";
alice->send_state_event<mtx::events::state::Avatar,
mtx::events::EventType::RoomAvatar>(
room_id.to_string(),
avatar,
[&event_ids](const mtx::responses::EventId &res, RequestErr err) {
check_error(err);
event_ids.push_back(res.event_id.to_string());
});
while (event_ids.size() != 2)
SyncOpts opts;
opts.timeout = 0;
opts, [room_id, event_ids](const mtx::responses::Sync &res, RequestErr err) {
auto ids = get_event_ids<TimelineEvents>(
res.rooms.join.at(room_id.to_string()).timeline.events);
// The sent event ids should be visible in the timeline.
for (const auto &event_id : event_ids)
ASSERT_TRUE(std::find(ids.begin(), ids.end(), event_id) !=
std::end(ids));
});
});
alice->close();
bob->close();
}
TEST(ClientAPI, Pagination)
{