diff --git a/CMakeLists.txt b/CMakeLists.txt index 390ef83..7e2e33a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.1) project(nheko CXX) +option(BUILD_TESTS "Build all tests" OFF) + find_package(Qt5Widgets REQUIRED) find_package(Qt5Network REQUIRED) @@ -114,8 +116,22 @@ set(SRC_FILES src/ui/ThemeManager.cc ) +set(MATRIX_EVENTS + src/events/AliasesEventContent.cc + src/events/AvatarEventContent.cc + src/events/CanonicalAliasEventContent.cc + src/events/CreateEventContent.cc + src/events/HistoryVisibilityEventContent.cc + src/events/JoinRulesEventContent.cc + src/events/MemberEventContent.cc + src/events/NameEventContent.cc + src/events/PowerLevelsEventContent.cc + src/events/TopicEventContent.cc +) + include_directories(include) include_directories(include/ui) +include_directories(include/events) qt5_wrap_ui (UI_HEADERS forms/ChatPage.ui @@ -161,9 +177,24 @@ qt5_wrap_cpp(MOC_HEADERS qt5_add_resources(QRC resources/res.qrc) -add_executable (nheko ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC}) -target_link_libraries (nheko Qt5::Widgets Qt5::Network) +add_library(matrix_events ${MATRIX_EVENTS} src/Deserializable.cc) +target_link_libraries(matrix_events Qt5::Core) + +if (BUILD_TESTS) + enable_testing() -if(WIN32) - target_link_libraries(nheko Qt5::WinMain) -endif(WIN32) + find_package(GTest REQUIRED) + include_directories(${GTEST_INCLUDE_DIRS}) + + add_executable(events_test tests/events.cc) + target_link_libraries(events_test matrix_events ${GTEST_BOTH_LIBRARIES}) + + add_test(MatrixEvents events_test) +else() + add_executable (nheko ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC}) + target_link_libraries (nheko matrix_events Qt5::Widgets Qt5::Network) + + if(WIN32) + target_link_libraries(nheko Qt5::WinMain) + endif(WIN32) +endif() diff --git a/Makefile b/Makefile index 8bf98a4..477ec85 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ SRC := $(shell find include src -type f -type f \( -iname "*.cc" -o -iname "*.h" \)) debug: - @cmake -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Debug + @cmake -DBUILD_TESTS=OFF -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Debug @cmake --build build release-debug: - @cmake -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo + @cmake -DBUILD_TESTS=OFF -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo @cmake --build build run: @@ -14,6 +14,11 @@ run: lint: @clang-format -i $(SRC) +test: + @cmake -DBUILD_TESTS=ON -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release + @cmake --build build + @cd build && GTEST_COLOR=1 ctest --verbose + clean: rm -rf build diff --git a/include/events/AliasesEventContent.h b/include/events/AliasesEventContent.h new file mode 100644 index 0000000..0321871 --- /dev/null +++ b/include/events/AliasesEventContent.h @@ -0,0 +1,42 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ALIASES_EVENT_CONTENT_H +#define ALIASES_EVENT_CONTENT_H + +#include +#include + +#include "Deserializable.h" + +class AliasesEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QList aliases() const; + +private: + QList aliases_; +}; + +inline QList AliasesEventContent::aliases() const +{ + return aliases_; +} + +#endif // ALIASES_EVENT_CONTENT_H diff --git a/include/events/AvatarEventContent.h b/include/events/AvatarEventContent.h new file mode 100644 index 0000000..c77cb6d --- /dev/null +++ b/include/events/AvatarEventContent.h @@ -0,0 +1,46 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef AVATAR_EVENT_CONTENT_H +#define AVATAR_EVENT_CONTENT_H + +#include +#include + +#include "Deserializable.h" + +/* + * A picture that is associated with the room. + */ + +class AvatarEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QUrl url() const; + +private: + QUrl url_; +}; + +inline QUrl AvatarEventContent::url() const +{ + return url_; +} + +#endif // AVATAR_EVENT_CONTENT_H diff --git a/include/events/CanonicalAliasEventContent.h b/include/events/CanonicalAliasEventContent.h new file mode 100644 index 0000000..3c031b8 --- /dev/null +++ b/include/events/CanonicalAliasEventContent.h @@ -0,0 +1,49 @@ + +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CANONICAL_ALIAS_EVENT_CONTENT_H +#define CANONICAL_ALIAS_EVENT_CONTENT_H + +#include + +#include "CanonicalAliasEventContent.h" +#include "Deserializable.h" + +/* + * This event is used to inform the room about which alias should be considered + * the canonical one. This could be for display purposes or as suggestion to + * users which alias to use to advertise the room. + */ + +class CanonicalAliasEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QString alias() const; + +private: + QString alias_; +}; + +inline QString CanonicalAliasEventContent::alias() const +{ + return alias_; +} + +#endif // CANONICAL_ALIAS_EVENT_CONTENT_H diff --git a/include/events/CreateEventContent.h b/include/events/CreateEventContent.h new file mode 100644 index 0000000..ed1a8ce --- /dev/null +++ b/include/events/CreateEventContent.h @@ -0,0 +1,46 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CREATE_EVENT_CONTENT_H +#define CREATE_EVENT_CONTENT_H + +#include + +#include "Deserializable.h" + +/* + * This is the first event in a room and cannot be changed. It acts as the root of all other events. + */ + +class CreateEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QString creator() const; + +private: + // The user_id of the room creator. This is set by the homeserver. + QString creator_; +}; + +inline QString CreateEventContent::creator() const +{ + return creator_; +} + +#endif // CREATE_EVENT_CONTENT_H diff --git a/include/events/HistoryVisibilityEventContent.h b/include/events/HistoryVisibilityEventContent.h new file mode 100644 index 0000000..ac7316f --- /dev/null +++ b/include/events/HistoryVisibilityEventContent.h @@ -0,0 +1,48 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HISTORY_VISIBILITY_EVENT_CONTENT_H +#define HISTORY_VISIBILITY_EVENT_CONTENT_H + +#include + +#include "Deserializable.h" + +enum HistoryVisibility { + Invited, + Joined, + Shared, + WorldReadable, +}; + +class HistoryVisibilityEventContent : public Deserializable +{ +public: + inline HistoryVisibility historyVisibility() const; + + void deserialize(const QJsonValue &data) override; + +private: + HistoryVisibility history_visibility_; +}; + +inline HistoryVisibility HistoryVisibilityEventContent::historyVisibility() const +{ + return history_visibility_; +} + +#endif // HISTORY_VISIBILITY_EVENT_CONTENT_H diff --git a/include/events/JoinRulesEventContent.h b/include/events/JoinRulesEventContent.h new file mode 100644 index 0000000..8b647fb --- /dev/null +++ b/include/events/JoinRulesEventContent.h @@ -0,0 +1,60 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef JOIN_RULES_EVENT_CONTENT_H +#define JOIN_RULES_EVENT_CONTENT_H + +#include + +#include "Deserializable.h" + +enum JoinRule { + // A user who wishes to join the room must first receive + // an invite to the room from someone already inside of the room. + Invite, + + // Reserved but not yet implemented by the Matrix specification. + Knock, + + // Reserved but not yet implemented by the Matrix specification. + Private, + + /// Anyone can join the room without any prior action. + Public, +}; + +/* + * Describes how users are allowed to join the room. + */ + +class JoinRulesEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline JoinRule joinRule() const; + +private: + JoinRule join_rule_; +}; + +inline JoinRule JoinRulesEventContent::joinRule() const +{ + return join_rule_; +} + +#endif // JOIN_RULES_EVENT_CONTENT_H diff --git a/include/events/MemberEventContent.h b/include/events/MemberEventContent.h new file mode 100644 index 0000000..046dda0 --- /dev/null +++ b/include/events/MemberEventContent.h @@ -0,0 +1,77 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MEMBER_EVENT_CONTENT_H +#define MEMBER_EVENT_CONTENT_H + +#include +#include + +#include "Deserializable.h" + +enum Membership { + // The user is banned. + BanState, + + // The user has been invited. + InviteState, + + // The user has joined. + JoinState, + + // The user has requested to join. + KnockState, + + // The user has left. + LeaveState, +}; + +/* + * The current membership state of a user in the room. + */ + +class MemberEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QUrl avatarUrl() const; + inline QString displayName() const; + inline Membership membershipState() const; + +private: + QUrl avatar_url_; + QString display_name_; + Membership membership_state_; +}; + +inline QUrl MemberEventContent::avatarUrl() const +{ + return avatar_url_; +} + +inline QString MemberEventContent::displayName() const +{ + return display_name_; +} + +inline Membership MemberEventContent::membershipState() const +{ + return membership_state_; +} + +#endif // MEMBER_EVENT_CONTENT_H diff --git a/include/events/NameEventContent.h b/include/events/NameEventContent.h new file mode 100644 index 0000000..d6f60e2 --- /dev/null +++ b/include/events/NameEventContent.h @@ -0,0 +1,45 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NAME_EVENT_CONTENT_H +#define NAME_EVENT_CONTENT_H + +#include + +#include "Deserializable.h" + +/* + * A human-friendly room name designed to be displayed to the end-user. + */ + +class NameEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QString name() const; + +private: + QString name_; +}; + +inline QString NameEventContent::name() const +{ + return name_; +} + +#endif // NAME_EVENT_CONTENT_H diff --git a/include/events/PowerLevelsEventContent.h b/include/events/PowerLevelsEventContent.h new file mode 100644 index 0000000..c2a3f1f --- /dev/null +++ b/include/events/PowerLevelsEventContent.h @@ -0,0 +1,102 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef POWER_LEVELS_EVENT_CONTENT_H +#define POWER_LEVELS_EVENT_CONTENT_H + +#include +#include + +#include "Deserializable.h" + +enum PowerLevels { + User = 0, + Moderator = 50, + Admin = 100, +}; + +/* + * Defines the power levels (privileges) of users in the room. + */ + +class PowerLevelsEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline int banLevel() const; + inline int inviteLevel() const; + inline int kickLevel() const; + inline int redactLevel() const; + + inline int eventsDefaultLevel() const; + inline int stateDefaultLevel() const; + inline int usersDefaultLevel() const; + + int eventLevel(QString event_type) const; + int userLevel(QString user_id) const; + +private: + int ban_ = PowerLevels::Moderator; + int invite_ = PowerLevels::Moderator; + int kick_ = PowerLevels::Moderator; + int redact_ = PowerLevels::Moderator; + + int events_default_ = PowerLevels::User; + int state_default_ = PowerLevels::Moderator; + int users_default_ = PowerLevels::User; + + QMap events_; + QMap users_; +}; + +inline int PowerLevelsEventContent::banLevel() const +{ + return ban_; +} + +inline int PowerLevelsEventContent::inviteLevel() const +{ + return invite_; +} + +inline int PowerLevelsEventContent::kickLevel() const +{ + return kick_; +} + +inline int PowerLevelsEventContent::redactLevel() const +{ + return redact_; +} + +inline int PowerLevelsEventContent::eventsDefaultLevel() const +{ + return events_default_; +} + +inline int PowerLevelsEventContent::stateDefaultLevel() const +{ + return state_default_; +} + +inline int PowerLevelsEventContent::usersDefaultLevel() const +{ + return users_default_; +} + +#endif // POWER_LEVELS_EVENT_CONTENT_H diff --git a/include/events/TopicEventContent.h b/include/events/TopicEventContent.h new file mode 100644 index 0000000..fd12de9 --- /dev/null +++ b/include/events/TopicEventContent.h @@ -0,0 +1,45 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TOPIC_EVENT_CONTENT_H +#define TOPIC_EVENT_CONTENT_H + +#include + +#include "Deserializable.h" + +/* + * A topic is a short message detailing what is currently being discussed in the room. + */ + +class TopicEventContent : public Deserializable +{ +public: + void deserialize(const QJsonValue &data) override; + + inline QString topic() const; + +private: + QString topic_; +}; + +inline QString TopicEventContent::topic() const +{ + return topic_; +} + +#endif // TOPIC_EVENT_CONTENT_H diff --git a/src/events/AliasesEventContent.cc b/src/events/AliasesEventContent.cc new file mode 100644 index 0000000..ccc4f2c --- /dev/null +++ b/src/events/AliasesEventContent.cc @@ -0,0 +1,36 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "AliasesEventContent.h" + +void AliasesEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("AliasesEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("aliases") == QJsonValue::Undefined) + throw DeserializationException("aliases key is missing"); + + auto aliases = object.value("aliases").toArray(); + + for (const auto &alias : aliases) + aliases_.push_back(alias.toString()); +} diff --git a/src/events/AvatarEventContent.cc b/src/events/AvatarEventContent.cc new file mode 100644 index 0000000..51a60a1 --- /dev/null +++ b/src/events/AvatarEventContent.cc @@ -0,0 +1,36 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "AvatarEventContent.h" + +void AvatarEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("AvatarEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("url") == QJsonValue::Undefined) + throw DeserializationException("url key is missing"); + + url_ = QUrl(object.value("url").toString()); + + if (!url_.isValid()) + qWarning() << "Invalid avatar url" << url_; +} diff --git a/src/events/CanonicalAliasEventContent.cc b/src/events/CanonicalAliasEventContent.cc new file mode 100644 index 0000000..23a3201 --- /dev/null +++ b/src/events/CanonicalAliasEventContent.cc @@ -0,0 +1,31 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CanonicalAliasEventContent.h" + +void CanonicalAliasEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("CanonicalAliasEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("alias") == QJsonValue::Undefined) + throw DeserializationException("alias key is missing"); + + alias_ = object.value("alias").toString(); +} diff --git a/src/events/CreateEventContent.cc b/src/events/CreateEventContent.cc new file mode 100644 index 0000000..e44be13 --- /dev/null +++ b/src/events/CreateEventContent.cc @@ -0,0 +1,31 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CreateEventContent.h" + +void CreateEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("CreateEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("creator") == QJsonValue::Undefined) + throw DeserializationException("creator key is missing"); + + creator_ = object.value("creator").toString(); +} diff --git a/src/events/HistoryVisibilityEventContent.cc b/src/events/HistoryVisibilityEventContent.cc new file mode 100644 index 0000000..b9eb7ff --- /dev/null +++ b/src/events/HistoryVisibilityEventContent.cc @@ -0,0 +1,42 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "HistoryVisibilityEventContent.h" + +void HistoryVisibilityEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("HistoryVisibilityEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("history_visibility") == QJsonValue::Undefined) + throw DeserializationException("history_visibility key is missing"); + + auto value = object.value("history_visibility").toString(); + + if (value == "invited") + history_visibility_ = HistoryVisibility::Invited; + else if (value == "joined") + history_visibility_ = HistoryVisibility::Joined; + else if (value == "shared") + history_visibility_ = HistoryVisibility::Shared; + else if (value == "world_readable") + history_visibility_ = HistoryVisibility::WorldReadable; + else + throw DeserializationException(QString("Unknown history_visibility value: %1").arg(value).toUtf8().constData()); +} diff --git a/src/events/JoinRulesEventContent.cc b/src/events/JoinRulesEventContent.cc new file mode 100644 index 0000000..ad30a1a --- /dev/null +++ b/src/events/JoinRulesEventContent.cc @@ -0,0 +1,42 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "JoinRulesEventContent.h" + +void JoinRulesEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("JoinRulesEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("join_rule") == QJsonValue::Undefined) + throw DeserializationException("join_rule key is missing"); + + auto value = object.value("join_rule").toString(); + + if (value == "invite") + join_rule_ = JoinRule::Invite; + else if (value == "knock") + join_rule_ = JoinRule::Knock; + else if (value == "private") + join_rule_ = JoinRule::Private; + else if (value == "public") + join_rule_ = JoinRule::Public; + else + throw DeserializationException(QString("Unknown join_rule value: %1").arg(value).toUtf8().constData()); +} diff --git a/src/events/MemberEventContent.cc b/src/events/MemberEventContent.cc new file mode 100644 index 0000000..e7814e3 --- /dev/null +++ b/src/events/MemberEventContent.cc @@ -0,0 +1,55 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "MemberEventContent.h" + +void MemberEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("MemberEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (!object.contains("membership")) + throw DeserializationException("membership key is missing"); + + auto value = object.value("membership").toString(); + + if (value == "ban") + membership_state_ = Membership::BanState; + else if (value == "invite") + membership_state_ = Membership::InviteState; + else if (value == "join") + membership_state_ = Membership::JoinState; + else if (value == "knock") + membership_state_ = Membership::KnockState; + else if (value == "leave") + membership_state_ = Membership::LeaveState; + else + throw DeserializationException(QString("Unknown membership value: %1").arg(value).toUtf8().constData()); + + if (object.contains("avatar_url")) + avatar_url_ = QUrl(object.value("avatar_url").toString()); + + if (!avatar_url_.toString().isEmpty() && !avatar_url_.isValid()) + qWarning() << "Invalid avatar url" << avatar_url_; + + if (object.contains("displayname")) + display_name_ = object.value("displayname").toString(); +} diff --git a/src/events/NameEventContent.cc b/src/events/NameEventContent.cc new file mode 100644 index 0000000..09b655a --- /dev/null +++ b/src/events/NameEventContent.cc @@ -0,0 +1,31 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "NameEventContent.h" + +void NameEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("NameEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("name") == QJsonValue::Undefined) + throw DeserializationException("name key is missing"); + + name_ = object.value("name").toString(); +} diff --git a/src/events/PowerLevelsEventContent.cc b/src/events/PowerLevelsEventContent.cc new file mode 100644 index 0000000..a6e5846 --- /dev/null +++ b/src/events/PowerLevelsEventContent.cc @@ -0,0 +1,80 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "Deserializable.h" +#include "PowerLevelsEventContent.h" + +void PowerLevelsEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("PowerLevelsEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("ban") != QJsonValue::Undefined) + ban_ = object.value("ban").toInt(); + + if (object.value("invite") != QJsonValue::Undefined) + invite_ = object.value("invite").toInt(); + + if (object.value("kick") != QJsonValue::Undefined) + kick_ = object.value("kick").toInt(); + + if (object.value("redact") != QJsonValue::Undefined) + redact_ = object.value("redact").toInt(); + + if (object.value("events_default") != QJsonValue::Undefined) + events_default_ = object.value("events_default").toInt(); + + if (object.value("state_default") != QJsonValue::Undefined) + state_default_ = object.value("state_default").toInt(); + + if (object.value("users_default") != QJsonValue::Undefined) + users_default_ = object.value("users_default").toInt(); + + if (object.value("users").isObject()) { + auto users = object.value("users").toObject(); + + for (auto it = users.constBegin(); it != users.constEnd(); it++) + users_.insert(it.key(), it.value().toInt()); + } + + if (object.value("events").isObject()) { + auto events = object.value("events").toObject(); + + for (auto it = events.constBegin(); it != events.constEnd(); it++) + events_.insert(it.key(), it.value().toInt()); + } +} + +int PowerLevelsEventContent::eventLevel(QString event_type) const +{ + if (events_.contains(event_type)) + return events_[event_type]; + + return events_default_; +} + +int PowerLevelsEventContent::userLevel(QString userid) const +{ + if (users_.contains(userid)) + return users_[userid]; + + return users_default_; +} diff --git a/src/events/TopicEventContent.cc b/src/events/TopicEventContent.cc new file mode 100644 index 0000000..1abda15 --- /dev/null +++ b/src/events/TopicEventContent.cc @@ -0,0 +1,31 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TopicEventContent.h" + +void TopicEventContent::deserialize(const QJsonValue &data) +{ + if (!data.isObject()) + throw DeserializationException("TopicEventContent is not a JSON object"); + + auto object = data.toObject(); + + if (object.value("topic") == QJsonValue::Undefined) + throw DeserializationException("topic key is missing"); + + topic_ = object.value("topic").toString(); +} diff --git a/tests/events.cc b/tests/events.cc new file mode 100644 index 0000000..79319f3 --- /dev/null +++ b/tests/events.cc @@ -0,0 +1,499 @@ +#include +#include + +#include "AliasesEventContent.h" +#include "AvatarEventContent.h" +#include "CanonicalAliasEventContent.h" +#include "CreateEventContent.h" +#include "HistoryVisibilityEventContent.h" +#include "JoinRulesEventContent.h" +#include "MemberEventContent.h" +#include "NameEventContent.h" +#include "PowerLevelsEventContent.h" +#include "TopicEventContent.h" + +TEST(AliasesEventContent, Deserialization) +{ + auto data = QJsonObject{ + {"aliases", QJsonArray{"#test:matrix.org", "#test2:matrix.org"}}}; + + AliasesEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.aliases().size(), 2); +} + +TEST(AliasesEventContent, NotAnObject) +{ + auto data = QJsonArray{"#test:matrix.org", "#test2:matrix.org"}; + + AliasesEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(AliasesEventContent, MissingKey) +{ + auto data = QJsonObject{ + {"key", QJsonArray{"#test:matrix.org", "#test2:matrix.org"}}}; + + AliasesEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("aliases key is missing", e.what()); + } +} + +TEST(AvatarEventContent, Deserialization) +{ + auto data = QJsonObject{{"url", "https://matrix.org/avatar.png"}}; + + AvatarEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.url().toString(), "https://matrix.org/avatar.png"); +} + +TEST(AvatarEventContent, NotAnObject) +{ + auto data = QJsonArray{"key", "url"}; + + AvatarEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(AvatarEventContent, MissingKey) +{ + auto data = QJsonObject{{"key", "https://matrix.org"}}; + + AvatarEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("url key is missing", e.what()); + } +} + +TEST(CreateEventContent, Deserialization) +{ + auto data = QJsonObject{{"creator", "@alice:matrix.org"}}; + + CreateEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.creator(), "@alice:matrix.org"); +} + +TEST(CreateEventContent, NotAnObject) +{ + auto data = QJsonArray{"creator", "alice"}; + + CreateEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(CreateEventContent, MissingKey) +{ + auto data = QJsonObject{{"key", "@alice:matrix.org"}}; + + CreateEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("creator key is missing", e.what()); + } +} + +TEST(HistoryVisibilityEventContent, Deserialization) +{ + auto data = QJsonObject{{"history_visibility", "invited"}}; + + HistoryVisibilityEventContent content; + content.deserialize(data); + EXPECT_EQ(content.historyVisibility(), HistoryVisibility::Invited); + + data = QJsonObject{{"history_visibility", "joined"}}; + + content.deserialize(data); + EXPECT_EQ(content.historyVisibility(), HistoryVisibility::Joined); + + data = QJsonObject{{"history_visibility", "shared"}}; + + content.deserialize(data); + EXPECT_EQ(content.historyVisibility(), HistoryVisibility::Shared); + + data = QJsonObject{{"history_visibility", "world_readable"}}; + + content.deserialize(data); + EXPECT_EQ(content.historyVisibility(), HistoryVisibility::WorldReadable); +} + +TEST(HistoryVisibilityEventContent, NotAnObject) +{ + auto data = QJsonArray{"history_visibility", "alice"}; + + HistoryVisibilityEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(HistoryVisibilityEventContent, InvalidHistoryVisibility) +{ + auto data = QJsonObject{{"history_visibility", "wrong"}}; + + HistoryVisibilityEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("Unknown history_visibility value: wrong", e.what()); + } +} + +TEST(HistoryVisibilityEventContent, MissingKey) +{ + auto data = QJsonObject{{"key", "joined"}}; + + HistoryVisibilityEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("history_visibility key is missing", e.what()); + } +} + +TEST(JoinRulesEventContent, Deserialization) +{ + auto data = QJsonObject{{"join_rule", "invite"}}; + + JoinRulesEventContent content; + content.deserialize(data); + EXPECT_EQ(content.joinRule(), JoinRule::Invite); + + data = QJsonObject{{"join_rule", "knock"}}; + + content.deserialize(data); + EXPECT_EQ(content.joinRule(), JoinRule::Knock); + + data = QJsonObject{{"join_rule", "private"}}; + + content.deserialize(data); + EXPECT_EQ(content.joinRule(), JoinRule::Private); + + data = QJsonObject{{"join_rule", "public"}}; + + content.deserialize(data); + EXPECT_EQ(content.joinRule(), JoinRule::Public); +} + +TEST(JoinRulesEventContent, NotAnObject) +{ + auto data = QJsonArray{"rule", "alice"}; + + JoinRulesEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(JoinRulesEventContent, InvalidHistoryVisibility) +{ + auto data = QJsonObject{{"join_rule", "wrong"}}; + + JoinRulesEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("Unknown join_rule value: wrong", e.what()); + } +} + +TEST(JoinRulesEventContent, MissingKey) +{ + auto data = QJsonObject{{"key", "invite"}}; + + JoinRulesEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("join_rule key is missing", e.what()); + } +} + +TEST(CanonicalAliasEventContent, Deserialization) +{ + auto data = QJsonObject{{"alias", "Room Alias"}}; + + CanonicalAliasEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.alias(), "Room Alias"); +} + +TEST(CanonicalAliasEventContent, NotAnObject) +{ + auto data = QJsonArray{"alias", "Room Alias"}; + + CanonicalAliasEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(CanonicalAliasEventContent, MissingKey) +{ + auto data = QJsonObject{{"key", "alias"}}; + + CanonicalAliasEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("alias key is missing", e.what()); + } +} + +TEST(MemberEventContent, Deserialization) +{ + MemberEventContent content; + + auto data = QJsonObject{{"membership", "join"}}; + + content.deserialize(data); + EXPECT_EQ(content.membershipState(), Membership::JoinState); + + data = QJsonObject{{"membership", "invite"}, {"displayname", "Username"}}; + + content.deserialize(data); + EXPECT_EQ(content.membershipState(), Membership::InviteState); + EXPECT_EQ(content.displayName(), "Username"); + + data = QJsonObject{{"membership", "leave"}, {"avatar_url", "https://matrix.org"}}; + + content.deserialize(data); + EXPECT_EQ(content.membershipState(), Membership::LeaveState); + EXPECT_EQ(content.avatarUrl().toString(), "https://matrix.org"); + + data = QJsonObject{{"membership", "ban"}}; + + content.deserialize(data); + EXPECT_EQ(content.membershipState(), Membership::BanState); + + data = QJsonObject{{"membership", "knock"}}; + + content.deserialize(data); + EXPECT_EQ(content.membershipState(), Membership::KnockState); +} + +TEST(MemberEventContent, InvalidMembership) +{ + auto data = QJsonObject{{"membership", "wrong"}}; + + MemberEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("Unknown membership value: wrong", e.what()); + } +} + +TEST(MemberEventContent, NotAnObject) +{ + auto data = QJsonArray{"name", "join"}; + + MemberEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(MemberEventContent, MissingName) +{ + auto data = QJsonObject{{"key", "random"}}; + + MemberEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("membership key is missing", e.what()); + } +} + +TEST(NameEventContent, Deserialization) +{ + auto data = QJsonObject{{"name", "Room Name"}}; + + NameEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.name(), "Room Name"); +} + +TEST(NameEventContent, NotAnObject) +{ + auto data = QJsonArray{"name", "Room Name"}; + + NameEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(NameEventContent, MissingName) +{ + auto data = QJsonObject{{"key", "Room Name"}}; + + NameEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("name key is missing", e.what()); + } +} + +TEST(PowerLevelsEventContent, DefaultValues) +{ + PowerLevelsEventContent power_levels; + + EXPECT_EQ(power_levels.banLevel(), PowerLevels::Moderator); + EXPECT_EQ(power_levels.inviteLevel(), PowerLevels::Moderator); + EXPECT_EQ(power_levels.kickLevel(), PowerLevels::Moderator); + EXPECT_EQ(power_levels.redactLevel(), PowerLevels::Moderator); + + EXPECT_EQ(power_levels.eventsDefaultLevel(), PowerLevels::User); + EXPECT_EQ(power_levels.usersDefaultLevel(), PowerLevels::User); + EXPECT_EQ(power_levels.stateDefaultLevel(), PowerLevels::Moderator); + + // Default levels. + EXPECT_EQ(power_levels.userLevel("@joe:matrix.org"), PowerLevels::User); + EXPECT_EQ(power_levels.eventLevel("m.room.message"), PowerLevels::User); +} + +TEST(PowerLevelsEventContent, FullDeserialization) +{ + auto data = QJsonObject{ + {"ban", 1}, + {"invite", 2}, + {"kick", 3}, + {"redact", 4}, + + {"events_default", 5}, + {"state_default", 6}, + {"users_default", 7}, + + {"events", QJsonObject{{"m.message.text", 8}, {"m.message.image", 9}}}, + {"users", QJsonObject{{"@alice:matrix.org", 10}, {"@bob:matrix.org", 11}}}, + }; + + PowerLevelsEventContent power_levels; + power_levels.deserialize(data); + + EXPECT_EQ(power_levels.banLevel(), 1); + EXPECT_EQ(power_levels.inviteLevel(), 2); + EXPECT_EQ(power_levels.kickLevel(), 3); + EXPECT_EQ(power_levels.redactLevel(), 4); + + EXPECT_EQ(power_levels.eventsDefaultLevel(), 5); + EXPECT_EQ(power_levels.stateDefaultLevel(), 6); + EXPECT_EQ(power_levels.usersDefaultLevel(), 7); + + EXPECT_EQ(power_levels.userLevel("@alice:matrix.org"), 10); + EXPECT_EQ(power_levels.userLevel("@bob:matrix.org"), 11); + EXPECT_EQ(power_levels.userLevel("@carl:matrix.org"), 7); + + EXPECT_EQ(power_levels.eventLevel("m.message.text"), 8); + EXPECT_EQ(power_levels.eventLevel("m.message.image"), 9); + EXPECT_EQ(power_levels.eventLevel("m.message.gif"), 5); +} + +TEST(PowerLevelsEventContent, PartialDeserialization) +{ + auto data = QJsonObject{ + {"ban", 1}, + {"invite", 2}, + + {"events_default", 5}, + {"users_default", 7}, + + {"users", QJsonObject{{"@alice:matrix.org", 10}, {"@bob:matrix.org", 11}}}, + }; + + PowerLevelsEventContent power_levels; + power_levels.deserialize(data); + + EXPECT_EQ(power_levels.banLevel(), 1); + EXPECT_EQ(power_levels.inviteLevel(), 2); + EXPECT_EQ(power_levels.kickLevel(), PowerLevels::Moderator); + EXPECT_EQ(power_levels.redactLevel(), PowerLevels::Moderator); + + EXPECT_EQ(power_levels.eventsDefaultLevel(), 5); + EXPECT_EQ(power_levels.stateDefaultLevel(), PowerLevels::Moderator); + EXPECT_EQ(power_levels.usersDefaultLevel(), 7); + + EXPECT_EQ(power_levels.userLevel("@alice:matrix.org"), 10); + EXPECT_EQ(power_levels.userLevel("@bob:matrix.org"), 11); + EXPECT_EQ(power_levels.userLevel("@carl:matrix.org"), 7); + + EXPECT_EQ(power_levels.eventLevel("m.message.text"), 5); + EXPECT_EQ(power_levels.eventLevel("m.message.image"), 5); + EXPECT_EQ(power_levels.eventLevel("m.message.gif"), 5); +} + +TEST(PowerLevelsEventContent, NotAnObject) +{ + auto data = QJsonArray{"test", "test2"}; + + PowerLevelsEventContent power_levels; + + ASSERT_THROW(power_levels.deserialize(data), DeserializationException); +} + +TEST(TopicEventContent, Deserialization) +{ + auto data = QJsonObject{{"topic", "Room Topic"}}; + + TopicEventContent content; + content.deserialize(data); + + EXPECT_EQ(content.topic(), "Room Topic"); +} + +TEST(TopicEventContent, NotAnObject) +{ + auto data = QJsonArray{"topic", "Room Topic"}; + + TopicEventContent content; + + ASSERT_THROW(content.deserialize(data), DeserializationException); +} + +TEST(TopicEventContent, MissingName) +{ + auto data = QJsonObject{{"key", "Room Name"}}; + + TopicEventContent content; + ASSERT_THROW(content.deserialize(data), DeserializationException); + + try { + content.deserialize(data); + } catch (const DeserializationException &e) { + ASSERT_STREQ("topic key is missing", e.what()); + } +}