Skip to content
Snippets Groups Projects
client_api.cpp 67 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <boost/algorithm/string.hpp>
    
    #include <boost/variant.hpp>
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    #include <gtest/gtest.h>
    
    
    #include "mtx/events/collections.hpp"
    
    #include "mtx/events/encrypted.hpp"
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    #include "mtx/requests.hpp"
    #include "mtx/responses.hpp"
    
    #include "mtxclient/http/client.hpp"
    
    #include "test_helpers.hpp"
    
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    using namespace mtx::client;
    
    using namespace mtx::identifiers;
    
    using namespace mtx::events::collections;
    
    using namespace mtx::requests;
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    using namespace std;
    
    
    TEST(ClientAPI, Register)
    {
    
            auto user = make_test_client();
    
            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");
    
                      user->registration(
    
                        {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);
                        });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    TEST(ClientAPI, LoginSuccess)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
            mtx_client->login("alice", "secret", [](const mtx::responses::Login &res, RequestErr err) {
    
                    check_error(err);
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    validate_login("@alice:localhost", res);
            });
    
    
            mtx_client->login("bob", "secret", [](const mtx::responses::Login &res, RequestErr err) {
    
                    check_error(err);
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    validate_login("@bob:localhost", res);
            });
    
    
            mtx_client->login("carl", "secret", [](const mtx::responses::Login &res, RequestErr err) {
    
                    check_error(err);
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    validate_login("@carl:localhost", res);
            });
    
            mtx_client->close();
    }
    
    TEST(ClientAPI, LoginWrongPassword)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            mtx_client->login(
    
              "alice", "wrong_password", [](const mtx::responses::Login &res, RequestErr err) {
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                      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.user_id.to_string(), "");
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                      EXPECT_EQ(res.device_id, "");
                      EXPECT_EQ(res.access_token, "");
              });
    
            mtx_client->close();
    }
    
    TEST(ClientAPI, LoginWrongUsername)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
            mtx_client->login("john", "secret", [](const mtx::responses::Login &res, RequestErr err) {
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    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.user_id.to_string(), "");
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    EXPECT_EQ(res.device_id, "");
                    EXPECT_EQ(res.access_token, "");
            });
    
            mtx_client->close();
    }
    
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    TEST(ClientAPI, LoginFlows)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    
            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", 443);
    
            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");
    
    Nicolas Werner's avatar
    Nicolas Werner committed
            mtx_client->close();
    
            auto alice = make_test_client();
    
            alice->login("alice", "secret", [alice](const mtx::responses::Login &res, RequestErr err) {
    
                    ASSERT_FALSE(err);
    
                    auto const alice_id = res.user_id;
    
                    validate_login(alice_id.to_string(), res);
    
                    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_id.to_string(),
                              [](const mtx::responses::AvatarUrl &res, RequestErr err) {
    
                                      ASSERT_FALSE(err);
                                      ASSERT_TRUE(res.avatar_url.size() == 0);
                              });
                    });
            });
    
            alice->close();
    }
    
    
            auto alice = make_test_client();
    
            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";
    
    
                    validate_login(alice_id.to_string(), res);
    
                    alice->set_avatar_url(avatar_url, [alice, alice_id, avatar_url](RequestErr err) {
    
                            ASSERT_FALSE(err);
    
                            auto done = false;
    
                            alice->get_profile(
    
                              alice_id.to_string(),
    
                              [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_id.to_string(),
    
                              [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 = make_test_client();
    
              "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
    
                      check_error(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 = make_test_client();
    
              "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
    
                      check_error(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); });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    TEST(ClientAPI, CreateRoom)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            mtx_client->login(
    
              "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
    
                      check_error(err);
    
            while (mtx_client->access_token().empty())
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            mtx::requests::CreateRoom req;
            req.name  = "Name";
            req.topic = "Topic";
    
            mtx_client->create_room(req, [](const mtx::responses::CreateRoom &res, RequestErr err) {
    
                    check_error(err);
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                    ASSERT_TRUE(res.room_id.localpart().size() > 10);
                    EXPECT_EQ(res.room_id.hostname(), "localhost");
            });
    
            mtx_client->close();
    }
    
    
    TEST(ClientAPI, TagRoom)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
    
            mtx_client->login(
              "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
                      check_error(err);
              });
    
            while (mtx_client->access_token().empty())
                    sleep();
    
            mtx::requests::CreateRoom req;
            req.name  = "Name";
            req.topic = "Topic";
            mtx_client->create_room(
              req, [mtx_client](const mtx::responses::CreateRoom &res, RequestErr err) {
                      auto room_id = res.room_id;
    
                      check_error(err);
    
    
                      mtx_client->put_tag(
                        room_id.to_string(), "u.Test", {0.5}, [mtx_client, room_id](RequestErr err) {
                                check_error(err);
    
                                mtx_client->get_tags(
                                  room_id.to_string(),
                                  [mtx_client, room_id](mtx::events::account_data::Tags tags,
                                                        RequestErr err) {
                                          check_error(err);
    
                                          EXPECT_EQ(tags.tags.at("u.Test").order, 0.5);
    
                                          mtx_client->delete_tag(
                                            room_id.to_string(),
                                            "u.Test",
                                            [mtx_client, room_id](RequestErr err) {
                                                    check_error(err);
    
                                                    mtx_client->get_tags(
                                                      room_id.to_string(),
                                                      [mtx_client,
                                                       room_id](mtx::events::account_data::Tags tags,
                                                                RequestErr err) {
                                                              check_error(err);
                                                              EXPECT_EQ(tags.tags.count("u.Test"), 0);
                                                      });
                                            });
                                  });
                        });
              });
    
            mtx_client->close();
    }
    
    
    TEST(ClientAPI, LogoutSuccess)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
            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) {
    
                      check_error(err);
    
                      token = res.access_token;
              });
    
            mtx_client->set_access_token(token);
            mtx::requests::CreateRoom req;
            req.name  = "Test1";
            req.topic = "Topic1";
    
            mtx_client->create_room(
    
              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) {
    
                    check_error(err);
    
                    token.clear();
            });
    
            // 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);
    
    Nicolas Werner's avatar
    Nicolas Werner committed
                    EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_UNRECOGNIZED");
    
            });
    
            mtx_client->close();
    }
    
    TEST(ClientAPI, LogoutInvalidatesTokenOnServer)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
            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) {
    
                      check_error(err);
    
                      token = res.access_token;
              });
    
            mtx_client->set_access_token(token);
            mtx::requests::CreateRoom req;
            req.name  = "Test1";
            req.topic = "Topic1";
    
            mtx_client->create_room(
    
              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) {
    
                    check_error(err);
    
                    mtx_client->set_access_token(token);
                    token.clear();
            });
    
            // 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);
    
    Nicolas Werner's avatar
    Nicolas Werner committed
                    EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_UNRECOGNIZED");
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    TEST(ClientAPI, CreateRoomInvites)
    {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
            auto carl  = make_test_client();
    
            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);
            });
    
            carl->login("carl", "secret", [carl](const mtx::responses::Login &, RequestErr err) {
    
                    check_error(err);
    
            while (alice->access_token().empty() || bob->access_token().empty() ||
                   carl->access_token().empty())
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            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) {
    
                    check_error(err);
    
                    auto room_id = res.room_id.to_string();
    
                    bob->join_room(room_id, [](const mtx::responses::RoomId &, RequestErr err) {
                            check_error(err);
                    });
    
                    carl->join_room(room_id, [](const mtx::responses::RoomId &, RequestErr err) {
                            check_error(err);
                    });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
            });
    
            alice->close();
            bob->close();
            carl->close();
    }
    
    TEST(ClientAPI, JoinRoom)
    {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
            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())
    
            // Creating a random room alias.
            // TODO: add a type for room aliases.
            const auto alias = utils::random_token(20, false);
    
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
            mtx::requests::CreateRoom req;
    
            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 mtx::responses::RoomId &, RequestErr err) {
                              check_error(err);
                      });
    
                      using namespace mtx::identifiers;
    
                      bob->join_room("!random_room_id:localhost",
                                     [](const mtx::responses::RoomId &, RequestErr err) {
                                             ASSERT_TRUE(err);
                                             EXPECT_EQ(
                                               mtx::errors::to_string(err->matrix_error.errcode),
                                               "M_UNKNOWN");
                                     });
    
                      // Join the room using an alias.
    
                      bob->join_room(
                        "#" + alias + ":localhost",
                        [](const mtx::responses::RoomId &, RequestErr err) { check_error(err); });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            alice->close();
            bob->close();
    }
    
    
    TEST(ClientAPI, LeaveRoom)
    {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
            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.name   = "Name";
            req.topic  = "Topic";
            req.invite = {"@bob:localhost"};
    
            alice->create_room(req, [bob](const mtx::responses::CreateRoom &res, RequestErr err) {
    
                    check_error(err);
    
                    auto room_id = res.room_id;
    
    
                    bob->join_room(res.room_id.to_string(),
    
                                   [room_id, bob](const mtx::responses::RoomId &, RequestErr err) {
    
                                           bob->leave_room(room_id.to_string(),
    
                                                           [](mtx::responses::Empty, RequestErr err) {
                                                                   check_error(err);
                                                           });
    
            });
    
            // Trying to leave a non-existent room should fail.
    
            bob->leave_room("!random_room_id:localhost", [](mtx::responses::Empty, RequestErr err) {
                    ASSERT_TRUE(err);
                    EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode), "M_UNKNOWN");
                    EXPECT_EQ(err->matrix_error.error, "Not a known room");
            });
    
    
            alice->close();
            bob->close();
    }
    
    
    TEST(ClientAPI, InviteRoom)
    {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
            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.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 mtx::responses::RoomId &, RequestErr err) {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
    
            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 mtx::responses::RoomId &, 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 = make_test_client();
            auto bob   = make_test_client();
    
    
            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 mtx::responses::RoomId &, 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 = make_test_client();
            auto bob   = make_test_client();
    
            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.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");
                                       });
              });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    TEST(ClientAPI, Sync)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            mtx_client->login(
    
              "alice", "secret", [mtx_client](const mtx::responses::Login &, RequestErr err) {
    
                      check_error(err);
    
            while (mtx_client->access_token().empty())
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            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);
                      });
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    
            mtx_client->close();
    }
    
    
    TEST(ClientAPI, Versions)
    {
    
            std::shared_ptr<Client> mtx_client = make_test_client();
    
            mtx_client->versions([](const mtx::responses::Versions &res, RequestErr err) {
    
                    check_error(err);
    
                    EXPECT_EQ(res.versions.size(), 7);
    
                    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");
    
                    EXPECT_EQ(res.versions.at(6), "r0.6.0");
    
            });
    
            mtx_client->close();
    }
    
            auto alice = make_test_client();
    
            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) {
    
                    check_error(err);
    
                    alice->start_typing(res.room_id.to_string(), 10000, [alice, res](RequestErr err) {
    
                            check_error(err);
    
                            const auto room_id = res.room_id.to_string();
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
                            atomic_bool can_continue(false);
    
                            SyncOpts opts;
                            opts.timeout = 0;
    
                            alice->sync(
                              opts,
                              [room_id, &can_continue](const mtx::responses::Sync &res,
                                                       RequestErr err) {
                                      check_error(err);
    
                                      can_continue = true;
    
                                      auto room = res.rooms.join.at(room_id);
    
                                      EXPECT_EQ(room.ephemeral.events.size(), 1);
                                      EXPECT_EQ(
                                        std::get<
                                          mtx::events::EphemeralEvent<mtx::events::ephemeral::Typing>>(
                                          room.ephemeral.events.front())
                                          .content.user_ids.front(),
                                        "@alice:localhost");
                              });
    
                            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.events.size(), 1);
                                                EXPECT_EQ(std::get<mtx::events::EphemeralEvent<
                                                            mtx::events::ephemeral::Typing>>(
                                                            room.ephemeral.events.front())
                                                            .content.user_ids.size(),
                                                          0);
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    TEST(ClientAPI, Presence)
    {
    
            auto alice = make_test_client();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    
            alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                    check_error(err);
            });
    
            while (alice->access_token().empty())
                    sleep();
    
            alice->put_presence_status(
              mtx::presence::unavailable, "Is this thing on?", [alice](RequestErr err) {
                      check_error(err);
    
                      alice->presence_status(
                        alice->user_id().to_string(),
                        [alice](const mtx::events::presence::Presence &presence, RequestErr err) {
                                check_error(err);
    
                                EXPECT_EQ(presence.presence, mtx::presence::unavailable);
                                EXPECT_EQ(presence.status_msg, "Is this thing on?");
    
                                alice->put_presence_status(
                                  mtx::presence::offline, std::nullopt, [alice](RequestErr err) {
                                          check_error(err);
    
                                          alice->presence_status(
                                            alice->user_id().to_string(),
                                            [alice](const mtx::events::presence::Presence &presence,
                                                    RequestErr err) {
                                                    check_error(err);
    
                                                    EXPECT_EQ(presence.presence,
                                                              mtx::presence::offline);
                                                    EXPECT_TRUE(presence.status_msg.empty());
                                            });
                                  });
                        });
              });
    
            alice->close();
    }
    
    TEST(ClientAPI, PresenceOverSync)
    {
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    
            alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                    check_error(err);
            });
            bob->login(
              "bob", "secret", [](const mtx::responses::Login &, RequestErr err) { check_error(err); });
    
            while (alice->access_token().empty() && bob->access_token().empty())
                    sleep();
    
            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();
    
                      bob->join_room(
    
                        room_id, [alice, bob, room_id](const mtx::responses::RoomId &, RequestErr err) {
    
    Nicolas Werner's avatar
    Nicolas Werner committed
                                check_error(err);
                                alice->put_presence_status(
                                  mtx::presence::unavailable,
                                  "Is this thing on?",
                                  [alice, bob](RequestErr err) {
                                          check_error(err);
    
                                          SyncOpts opts;
                                          opts.timeout      = 10;
                                          opts.set_presence = mtx::presence::online;
                                          alice->sync(
                                            opts,
                                            [bob, opts](const mtx::responses::Sync &, RequestErr err) {
                                                    check_error(err);
    
                                                    bob->sync(
                                                      opts,
                                                      [bob](const mtx::responses::Sync &s,
                                                            RequestErr err) {
                                                              check_error(err);
    
                                                              ASSERT_GE(s.presence.size(), 1);
    
                                                              bool found = false;
                                                              for (const auto &p : s.presence) {
                                                                      if (p.sender ==
                                                                          "@alice:localhost") {
                                                                              found = true;
                                                                              EXPECT_EQ(
                                                                                p.content.presence,
                                                                                mtx::presence::online);
                                                                              EXPECT_EQ(
                                                                                p.content.status_msg,
                                                                                "Is this thing "
                                                                                "on?");
                                                                      }
                                                                      EXPECT_TRUE(found);
                                                              }
                                                      });
                                            });
                                  });
                        });
              });
    
            alice->close();
    }
    
    
            auto alice = make_test_client();
            auto bob   = make_test_client();
    
            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;