Skip to content
Snippets Groups Projects
client_api.cpp 67.9 KiB
Newer Older
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/crypto/types.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",
                       mtx::http::UIAHandler(
                         [](const mtx::http::UIAHandler &,
                            const mtx::user_interactive::Unauthorized &) { EXPECT_TRUE(false); }),
                       [](const mtx::responses::Register &, RequestErr err) {
                           ASSERT_TRUE(err);
                           EXPECT_EQ(mtx::errors::to_string(err->matrix_error.errcode),
                                     "M_USER_IN_USE");
                       });
    user->register_username_available(
      "alice", [](const mtx::responses::Available &, RequestErr err) {
          ASSERT_TRUE(err);
          EXPECT_EQ(err->matrix_error.errcode, mtx::errors::ErrorCode::M_USER_IN_USE);
      });

    auto username = utils::random_token(10, false);
    // Synapse converts the username to lowercase.
    for (auto &c : username) {
        c = (char)std::tolower(c);
    }
    user->register_username_available(
      username + "a", [](const mtx::responses::Available &available, RequestErr err) {
          check_error(err);
          EXPECT_TRUE(available.available);
      });

    user->registration(
      username,
      "secret",
      mtx::http::UIAHandler([](const mtx::http::UIAHandler &h,
                               const mtx::user_interactive::Unauthorized &unauthorized) {
          EXPECT_EQ(unauthorized.flows.size(), 1);
          EXPECT_EQ(unauthorized.flows[0].stages[0], "m.login.dummy");

          mtx::user_interactive::Auth auth{unauthorized.session,
                                           mtx::user_interactive::auth::Dummy{}};
          h.next(auth);
      }),
      [user, username](const mtx::responses::Register &res, RequestErr err) {
          if (!err || err->matrix_error.unauthorized.flows.size() == 0)
              return;
          check_error(err);
          const auto user_id = "@" + username + ":" + server_name();
          EXPECT_EQ(res.user_id.to_string(), user_id);
          EXPECT_EQ(user->user_id().to_string(), user_id);
          EXPECT_FALSE(user->access_token().empty());
          EXPECT_EQ(user->access_token(), res.access_token);
          EXPECT_FALSE(user->device_id().empty());
          EXPECT_EQ(user->device_id(), res.device_id);
    user->registration([](const mtx::responses::Register &, RequestErr err) {
        ASSERT_TRUE(err);
        EXPECT_EQ(err->status_code, 401);
        EXPECT_FALSE(err->matrix_error.unauthorized.flows.empty());
    });

    user->close();
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);
        validate_login("@alice:" + server_name(), res);
    });
    mtx_client->login("bob", "secret", [](const mtx::responses::Login &res, RequestErr err) {
        check_error(err);
        validate_login("@bob:" + server_name(), res);
    });
    mtx_client->login("carl", "secret", [](const mtx::responses::Login &res, RequestErr err) {
        check_error(err);
        validate_login("@carl:" + server_name(), res);
    });
    mtx_client->close();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
}

TEST(ClientAPI, LoginWrongPassword)
{
    std::shared_ptr<Client> mtx_client = make_test_client();
    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, 403);
          EXPECT_EQ(res.user_id.to_string(), "");
          EXPECT_EQ(res.device_id, "");
          EXPECT_EQ(res.access_token, "");
      });
    mtx_client->close();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
}

TEST(ClientAPI, LoginWrongUsername)
{
    std::shared_ptr<Client> mtx_client = make_test_client();
    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, 403);
        EXPECT_EQ(res.user_id.to_string(), "");
        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();
    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"),
Nicolas Werner's avatar
Nicolas Werner committed
              "https://localhost:443/_matrix/client/v3/login/sso/"
              "redirect?redirectUrl=http%3A%2F%2Faaa%3A555%2Fsso");
    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);
            alice->get_profile(
              alice_id.to_string(),
              [alice, alice_id](const mtx::responses::Profile &res, RequestErr err) {
                  ASSERT_FALSE(err);
                  ASSERT_TRUE(res.avatar_url.size() == 0);
                  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();
    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);
            alice->get_profile(
              alice_id.to_string(),
              [avatar_url, alice, alice_id](const mtx::responses::Profile &res, RequestErr err) {
                  ASSERT_FALSE(err);
                  ASSERT_TRUE(res.avatar_url == avatar_url);
                  alice->get_avatar_url(
                    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();
    mtx_client->login(
      "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();
    mtx_client->login(
      "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); });
      });
    mtx_client->close();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
TEST(ClientAPI, CreateRoom)
{
    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, [](const mtx::responses::CreateRoom &res, RequestErr err) {
        check_error(err);
        ASSERT_TRUE(res.room_id.localpart().size() > 10);
        EXPECT_EQ(res.room_id.hostname(), server_name());
    });

    mtx_client->close();
TEST(ClientAPI, CreateRoomInitialState)
{
    mtx::requests::CreateRoom req;

    mtx::events::StrippedEvent<mtx::events::state::Encryption> enc;
    enc.type                         = mtx::events::EventType::RoomEncryption;
    enc.content.algorithm            = mtx::crypto::MEGOLM_ALGO;
    enc.content.rotation_period_ms   = 1000ULL * 60ULL * 60ULL * 777ULL;
    enc.content.rotation_period_msgs = 777;

    req.initial_state.emplace_back(enc);

    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_client->create_room(
      req, [mtx_client, enc](const mtx::responses::CreateRoom &res, RequestErr err) {
          check_error(err);
          ASSERT_TRUE(res.room_id.localpart().size() > 10);
          EXPECT_EQ(res.room_id.hostname(), server_name());

          mtx_client->get_state(
            res.room_id.to_string(), [enc](const mtx::responses::StateEvents &res, RequestErr err) {
                check_error(err);
                ASSERT_TRUE(res.events.size() > 0);
                bool found_enc_event = false;

                for (const auto &e : res.events) {
                    auto ev =
                      std::get_if<mtx::events::StateEvent<mtx::events::state::Encryption>>(&e);
                    if (ev) {
                        found_enc_event = true;
                        EXPECT_EQ(ev->content.algorithm, enc.content.algorithm);
                        EXPECT_EQ(ev->content.rotation_period_ms, enc.content.rotation_period_ms);
                        EXPECT_EQ(ev->content.rotation_period_msgs,
                                  enc.content.rotation_period_msgs);
                    }
                }
                EXPECT_TRUE(found_enc_event);
            });
      });

    mtx_client->close();
}

TEST(ClientAPI, Members)
{
    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) {
          check_error(err);
          ASSERT_TRUE(res.room_id.localpart().size() > 10);
          EXPECT_EQ(res.room_id.hostname(), server_name());

          mtx_client->members(res.room_id.to_string(),
                              [](const mtx::responses::Members &res, RequestErr err) {
                                  check_error(err);
                                  ASSERT_EQ(res.chunk.size(), 1);
                                  EXPECT_EQ(res.chunk[0].state_key, "@alice:" + server_name());
                              });
      });
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, RoomAccountData)
{
    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::events::account_data::nheko_extensions::HiddenEvents hiddenEv;
          hiddenEv.hidden_event_types = std::vector{
            mtx::events::EventType::RoomMember,
          };
          mtx_client->put_room_account_data(
            room_id.to_string(), hiddenEv, [mtx_client, room_id](RequestErr err) {
                check_error(err);
                mtx_client->get_room_account_data<
                  mtx::events::account_data::nheko_extensions::HiddenEvents>(
                  room_id.to_string(),
                  [](mtx::events::account_data::nheko_extensions::HiddenEvents hiddenEv,
                     RequestErr err) {
                      check_error(err);
                      ASSERT_EQ(hiddenEv.hidden_event_types->size(), 1);
                      EXPECT_EQ(hiddenEv.hidden_event_types->at(0),
                                mtx::events::EventType::RoomMember);
                  });
            });
      });
    mtx_client->close();
}

TEST(ClientAPI, AccountData)
{
    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::events::account_data::nheko_extensions::HiddenEvents hiddenEv;
    hiddenEv.hidden_event_types = std::vector{
      mtx::events::EventType::RoomMember,
    };
    mtx_client->put_account_data(hiddenEv, [mtx_client](RequestErr err) {
        check_error(err);
        mtx_client->get_account_data<mtx::events::account_data::nheko_extensions::HiddenEvents>(
          [mtx_client](mtx::events::account_data::nheko_extensions::HiddenEvents hiddenEv,
                       RequestErr err) {
              check_error(err);
              ASSERT_EQ(hiddenEv.hidden_event_types->size(), 1);
              EXPECT_EQ(hiddenEv.hidden_event_types->at(0), mtx::events::EventType::RoomMember);
    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;
      });

    while (token.empty())
        sleep();

    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();
    });

    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_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;
      });

    while (token.empty())
        sleep();

    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();
    });

    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_UNRECOGNIZED");
    });

    mtx_client->close();
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())
        sleep();

    mtx::requests::CreateRoom req;
    req.name   = "Name";
    req.topic  = "Topic";
    req.invite = {"@bob:" + server_name(), "@carl:" + server_name()};
    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); });
    });

    alice->close();
    bob->close();
    carl->close();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
}

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())
        sleep();

    // Creating a random room alias.
    // TODO: add a type for room aliases.
    const auto alias = utils::random_token(20, false);

    mtx::requests::CreateRoom req;
    req.name            = "Name";
    req.topic           = "Topic";
    req.invite          = {"@bob:" + server_name()};
    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:" + server_name(), [](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 + ":" + server_name(),
                       [](const mtx::responses::RoomId &, RequestErr err) { check_error(err); });
    });

    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())
        sleep();
    mtx::requests::CreateRoom req;
    req.name   = "Name";
    req.topic  = "Topic";
    req.invite = {"@bob:" + server_name()};
    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) {
              check_error(err);
              bob->leave_room(room_id.to_string(),
                              [](mtx::responses::Empty, RequestErr err) { check_error(err); });
Nicolas Werner's avatar
Nicolas Werner committed
          });
    });

    // Trying to leave a non-existent room should fail.
    bob->leave_room("!random_room_id:" + server_name(), [](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())
        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:" + server_name(),
                           [room_id, bob](const mtx::responses::Empty &, RequestErr err) {
                               check_error(err);

                               bob->join_room(room_id,
                                              [](const mtx::responses::RoomId &, RequestErr err) {
                                                  check_error(err);
                                              });
                           });
    });
    alice->close();
    bob->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())
        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:" + server_name(),
                           [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:" + server_name(),
                                                      [](const mtx::responses::Empty &,
                                                         RequestErr err) { check_error(err); });
                                 });
                           });
    });
    alice->close();
    bob->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())
        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:" + server_name(),
          [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:" + server_name(),
                      [alice, room_id](const mtx::responses::Empty &, RequestErr err) {
                          check_error(err);
                          alice->unban_user(
                            room_id,
                            "@bob:" + server_name(),
                            [](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())
        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();

        bob->invite_user(room_id,
                         "@carl:" + server_name(),
                         [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();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
TEST(ClientAPI, Sync)
{
    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 &, 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);
        });
    });
    mtx_client->close();
Konstantinos Sideris's avatar
Konstantinos Sideris committed
}
TEST(ClientAPI, State)
{
    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 &r, RequestErr err) {
        check_error(err);

        mtx_client->get_state(
          r.room_id.to_string(), [](const mtx::responses::StateEvents &res, RequestErr err) {
              check_error(err);
              ASSERT_TRUE(res.events.size() > 0);
              bool found_name_event = false;

              for (const auto &e : res.events) {
                  auto ev = std::get_if<mtx::events::StateEvent<mtx::events::state::Name>>(&e);
                  if (ev && ev->content.name == "Name")
                      found_name_event = true;
              }
              EXPECT_TRUE(found_name_event);
          });
    });

    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(), 10);
        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");
        EXPECT_EQ(res.versions.at(7), "r0.6.1");
        EXPECT_EQ(res.versions.at(8), "v1.1");
        EXPECT_EQ(res.versions.at(9), "v1.2");
    });

    mtx_client->close();
TEST(ClientAPI, Capabilities)
{
    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);
          mtx_client->capabilities(
            [](const mtx::responses::capabilities::Capabilities &res, RequestErr err) {
                check_error(err);

                EXPECT_GE(res.room_versions.default_.size(), 1);
                EXPECT_GE(res.room_versions.available.size(), 1);
                EXPECT_EQ(res.room_versions.available.at(res.room_versions.default_),