Changeset View
Changeset View
Standalone View
Standalone View
src/tests/client/storage-actions-test.cpp
| /* | /* | ||||
| * This file is part of libkazv. | * This file is part of libkazv. | ||||
| * SPDX-FileCopyrightText: 2025 tusooa <tusooa@kazv.moe> | * SPDX-FileCopyrightText: 2025 tusooa <tusooa@kazv.moe> | ||||
| * SPDX-License-Identifier: AGPL-3.0-or-later | * SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| */ | */ | ||||
| #include <libkazv-config.hpp> | #include <libkazv-config.hpp> | ||||
| #include "actions/storage.hpp" | #include "actions/storage.hpp" | ||||
| #include <catch2/catch_test_macros.hpp> | #include <catch2/catch_test_macros.hpp> | ||||
| #include <catch2/matchers/catch_matchers_quantifiers.hpp> | #include <catch2/matchers/catch_matchers_quantifiers.hpp> | ||||
| #include <catch2/matchers/catch_matchers_range_equals.hpp> | |||||
| #include <catch2/matchers/catch_matchers_predicate.hpp> | #include <catch2/matchers/catch_matchers_predicate.hpp> | ||||
| #include <factory.hpp> | #include <factory.hpp> | ||||
| using namespace Kazv; | using namespace Kazv; | ||||
| using namespace Kazv::Factory; | using namespace Kazv::Factory; | ||||
| using Catch::Matchers::AllMatch; | using Catch::Matchers::AllMatch; | ||||
| using Catch::Matchers::NoneMatch; | |||||
| using Catch::Matchers::Predicate; | using Catch::Matchers::Predicate; | ||||
| using Catch::Matchers::UnorderedRangeEquals; | |||||
| static const std::string roomId = "!test:example.com"; | static const std::string roomId = "!test:example.com"; | ||||
| template<class T> | template<class T> | ||||
| static auto makeEventInRoom(T modifiers) | static auto makeEventInRoom(T modifiers) | ||||
| { | { | ||||
| return makeEvent(withEventKV("/room_id"_json_pointer, roomId) | std::move(modifiers)); | return makeEvent(withEventKV("/room_id"_json_pointer, roomId) | std::move(modifiers)); | ||||
| }; | }; | ||||
| Show All 30 Lines | auto [next, _] = updateClient(c, LoadEventsFromStorageAction{ | ||||
| auto room = next.roomList.rooms[roomId]; | auto room = next.roomList.rooms[roomId]; | ||||
| REQUIRE(room.timeline == intoImmer(immer::flex_vector<std::string>{}, zug::map(&Event::id), existingEvents + timelineEvents)); | REQUIRE(room.timeline == intoImmer(immer::flex_vector<std::string>{}, zug::map(&Event::id), existingEvents + timelineEvents)); | ||||
| REQUIRE_THAT(existingEvents + timelineEvents + relatedEvents, | REQUIRE_THAT(existingEvents + timelineEvents + relatedEvents, | ||||
| AllMatch(Predicate<Event>([&room](const auto &e) { return !!room.messages.count(e.id()); }, "should be in messages"))); | AllMatch(Predicate<Event>([&room](const auto &e) { return !!room.messages.count(e.id()); }, "should be in messages"))); | ||||
| REQUIRE(room.reverseEventRelationships.count(timelineEvents[0].id())); | REQUIRE(room.reverseEventRelationships.count(timelineEvents[0].id())); | ||||
| REQUIRE(room.reverseEventRelationships.count(existingEvents[0].id())); | REQUIRE(room.reverseEventRelationships.count(existingEvents[0].id())); | ||||
| // should not give out any SaveEventsRequested triggers | |||||
| REQUIRE_THAT(next.nextTriggers, NoneMatch(Predicate<KazvEvent>([](const auto &t) { | |||||
| return std::holds_alternative<SaveEventsRequested>(t); | |||||
| }))); | |||||
| } | } | ||||
| // Verify the load works when loading into timeline an event in | // Verify the load works when loading into timeline an event in | ||||
| // messages but not in timeline | // messages but not in timeline | ||||
| auto nextTimelineEvents = relatedEvents + EventList{makeEventInRoom()}; | auto nextTimelineEvents = relatedEvents + EventList{makeEventInRoom()}; | ||||
| std::tie(next, std::ignore) = updateClient(next, LoadEventsFromStorageAction{ | std::tie(next, std::ignore) = updateClient(next, LoadEventsFromStorageAction{ | ||||
| {{roomId, nextTimelineEvents}}, | {{roomId, nextTimelineEvents}}, | ||||
| {}, | {}, | ||||
| }); | }); | ||||
| { | { | ||||
| auto room = next.roomList.rooms[roomId]; | auto room = next.roomList.rooms[roomId]; | ||||
| REQUIRE(room.timeline == intoImmer(immer::flex_vector<std::string>{}, zug::map(&Event::id), existingEvents + timelineEvents + nextTimelineEvents)); | REQUIRE(room.timeline == intoImmer(immer::flex_vector<std::string>{}, zug::map(&Event::id), existingEvents + timelineEvents + nextTimelineEvents)); | ||||
| } | |||||
| } | |||||
| TEST_CASE("maybeAddSaveEventsTrigger", "[client][storage-actions]") | |||||
| { | |||||
| WHEN("compare with self") { | |||||
| auto c = makeClient(withRoom(makeRoom(withRoomTimeline({ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| })))); | |||||
| c.maybeAddSaveEventsTrigger(c); | |||||
| REQUIRE(c.nextTriggers.empty()); | |||||
| } | |||||
| WHEN("new room, no events") { | |||||
| auto old = makeClient(withRoom(makeRoom(withRoomTimeline({ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| })))); | |||||
| auto c = old; | |||||
| withRoom(makeRoom())(c); | |||||
| c.maybeAddSaveEventsTrigger(old); | |||||
| REQUIRE(c.nextTriggers.empty()); | |||||
| } | |||||
| WHEN("new room, with events") { | |||||
| auto old = makeClient(withRoom(makeRoom(withRoomTimeline({ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| })))); | |||||
| auto c = old; | |||||
| auto tl = EventList{ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| }; | |||||
| auto room = makeRoom(withRoomTimeline(tl)); | |||||
| withRoom(room)(c); | |||||
| c.maybeAddSaveEventsTrigger(old); | |||||
| REQUIRE(c.nextTriggers.size() == 1); | |||||
| REQUIRE(std::holds_alternative<SaveEventsRequested>(c.nextTriggers[0])); | |||||
| auto t = std::get<SaveEventsRequested>(c.nextTriggers[0]); | |||||
| REQUIRE(t.nonTimelineEvents.empty()); | |||||
| REQUIRE(t.timelineEvents.size() == 1); | |||||
| REQUIRE_THAT(t.timelineEvents[room.roomId], UnorderedRangeEquals(tl)); | |||||
| } | |||||
| WHEN("new room, with tl and non-tl events") { | |||||
| auto old = makeClient(withRoom(makeRoom(withRoomTimeline({ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| })))); | |||||
| auto c = old; | |||||
| auto tl = EventList{ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| }; | |||||
| auto nonTl = EventList{makeEvent(), makeEvent()}; | |||||
| auto room = makeRoom(withRoomTimeline(tl)); | |||||
| room = RoomModel::update(std::move(room), AddMessagesAction{nonTl}); | |||||
| withRoom(room)(c); | |||||
| c.maybeAddSaveEventsTrigger(old); | |||||
| REQUIRE(c.nextTriggers.size() == 1); | |||||
| REQUIRE(std::holds_alternative<SaveEventsRequested>(c.nextTriggers[0])); | |||||
| auto t = std::get<SaveEventsRequested>(c.nextTriggers[0]); | |||||
| REQUIRE(t.nonTimelineEvents.size() == 1); | |||||
| REQUIRE_THAT(t.nonTimelineEvents[room.roomId], UnorderedRangeEquals(nonTl)); | |||||
| REQUIRE(t.timelineEvents.size() == 1); | |||||
| REQUIRE_THAT(t.timelineEvents[room.roomId], UnorderedRangeEquals(tl)); | |||||
| } | |||||
| WHEN("existing room with new and changed events") { | |||||
| auto origTl = EventList{ | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| }; | |||||
| auto origNonTl = EventList{makeEvent(), makeEvent()}; | |||||
| auto room = makeRoom(withRoomTimeline(origTl)); | |||||
| room = RoomModel::update(std::move(room), AddMessagesAction{origNonTl}); | |||||
| auto old = makeClient(withRoom(room)); | |||||
| auto c = old; | |||||
| auto tl = EventList{ | |||||
| makeEvent(withEventId(origTl[0].id()) | withEventContent(json{{"mew", "mew"}})), | |||||
| makeEvent(), | |||||
| makeEvent(), | |||||
| }; | |||||
| withRoomTimeline(tl)(room); | |||||
| auto nonTl = EventList{ | |||||
| makeEvent(withEventId(origNonTl[0].id()) | withEventContent(json{{"mew", "mew"}})), | |||||
| makeEvent(), | |||||
| }; | |||||
| room = RoomModel::update(std::move(room), AddMessagesAction{nonTl}); | |||||
| withRoom(room)(c); | |||||
| c.maybeAddSaveEventsTrigger(old); | |||||
| REQUIRE(c.nextTriggers.size() == 1); | |||||
| REQUIRE(std::holds_alternative<SaveEventsRequested>(c.nextTriggers[0])); | |||||
| auto t = std::get<SaveEventsRequested>(c.nextTriggers[0]); | |||||
| REQUIRE(t.timelineEvents.size() == 1); | |||||
| REQUIRE_THAT(t.timelineEvents[room.roomId], UnorderedRangeEquals(tl)); | |||||
| REQUIRE(t.nonTimelineEvents.size() == 1); | |||||
| REQUIRE_THAT(t.nonTimelineEvents[room.roomId], UnorderedRangeEquals(nonTl)); | |||||
| } | } | ||||
| } | } | ||||