Page MenuHomePhorge

paginate-test.cpp
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

paginate-test.cpp

/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch_all.hpp>
#include <boost/asio.hpp>
#include <asio-promise-handler.hpp>
#include <cursorutil.hpp>
#include <sdk-model.hpp>
#include <client/client.hpp>
#include "client-test-util.hpp"
#include "factory.hpp"
#include <iostream>
using namespace Kazv;
using namespace Kazv::Factory;
using Catch::Matchers::ContainsSubstring;
using Catch::Matchers::Contains;
// adapted from https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-rooms-roomid-messages
static json paginateResponseJson = R"({
"start": "prevBatchForEvent1",
"end": "anotherPrevBatch",
"chunk": [
{
"content": {
"body": "This is an example text message",
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"formatted_body": "<b>This is an example text message</b>"
},
"type": "m.room.message",
"event_id": "$third:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
},
{
"content": {
"name": "The room name"
},
"type": "m.room.name",
"event_id": "$second:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": ""
},
{
"content": {
"body": "Gangnam Style",
"url": "mxc://example.org/a526eYUSFFxlgbQYZmo442",
"info": {
"thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
"thumbnail_info": {
"mimetype": "image/jpeg",
"size": 46144,
"w": 300,
"h": 300
},
"w": 480,
"h": 320,
"duration": 2140786,
"size": 1563685,
"mimetype": "video/mp4"
},
"msgtype": "m.video"
},
"type": "m.room.message",
"event_id": "$first:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824651,
"unsigned": {
"age": 1234
}
}
]
})"_json;
static Event event1 = R"({
"type": "m.room.message",
"event_id": "$event1:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824654
})"_json;
static Event event2 = R"({
"type": "m.room.message",
"event_id": "$event2:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824656
})"_json;
static Event secEvent = R"({
"content": {
"name": "The room name"
},
"type": "m.room.name",
"event_id": "$second:example.org",
"room_id": "!foo:example.org",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": ""
})"_json;
TEST_CASE("Sending paginate job", "[client][paginate]")
{
ClientModel loggedInModel = makeClient(
withRoom(makeRoom(
withRoomId("!foo:example.com")
| withRoomTimelineGaps({{"$event", "some-opaque-token"}})
))
);
auto [resModel, dontCareEffect] = ClientModel::update(
loggedInModel, PaginateTimelineAction{"!foo:example.com", "$event", std::nullopt});
assert1Job(resModel);
for1stJob(resModel, [] (const auto &job) {
REQUIRE(job.jobId() == "GetRoomEvents");
REQUIRE_THAT(job.url(), ContainsSubstring("/rooms/!foo:example.com/"));
REQUIRE_THAT(job.requestQuery(), Contains(std::make_pair("dir"s, "b"s)));
REQUIRE_THAT(job.requestQuery(), Contains(std::make_pair("from"s, "some-opaque-token"s)));
});
}
TEST_CASE("Pagination should remove the original gap and record a new one", "[client][paginate]")
{
using namespace Kazv::CursorOp;
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!foo:example.org")
| withRoomTimeline({event1, event2})
| withRoomTimelineGaps({{event1.id(), "prevBatchForEvent1"}})
))
);
auto store = createTestClientStoreFrom(m, ph);
auto resp = makeResponse(
"GetRoomEvents",
withResponseJsonBody(paginateResponseJson)
| withResponseDataKV("roomId", "!foo:example.org")
| withResponseDataKV("gapEventId", "$event1:example.org")
);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
store.dispatch(ProcessResponseAction{resp});
io.run();
auto r = client.room("!foo:example.org");
auto timelineGaps = +r.timelineGaps();
REQUIRE(! timelineGaps.find("$event1:example.org"));
REQUIRE(timelineGaps.at("$first:example.org") == "anotherPrevBatch");
}
TEST_CASE("Pagination should erase all gaps in (start-of-fetched-batch, orig-gapped-event]", "[client][paginate]")
{
using namespace Kazv::CursorOp;
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!foo:example.org")
| withRoomTimeline({event1, event2, secEvent})
| withRoomTimelineGaps({
{event1.id(), "prevBatchForEvent1"},
{secEvent.id(), "prevBatchForSecEvent"},
})
))
);
auto store = createTestClientStoreFrom(m, ph);
auto resp = makeResponse(
"GetRoomEvents",
withResponseJsonBody(paginateResponseJson)
| withResponseDataKV("roomId", "!foo:example.org")
| withResponseDataKV("gapEventId", "$event1:example.org")
);
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store,
std::nullopt);
store.dispatch(ProcessResponseAction{resp});
io.run();
auto r = client.room("!foo:example.org");
auto timelineGaps = +r.timelineGaps();
REQUIRE(! timelineGaps.find("$second:example.org"));
REQUIRE(timelineGaps.at("$first:example.org") == "anotherPrevBatch");
}
TEST_CASE("Pagination should not crash if returned chunk's first event is the gapped event", "[client][paginate]")
{
using namespace Kazv::CursorOp;
boost::asio::io_context io;
AsioPromiseHandler ph{io.get_executor()};
ClientModel m = makeClient(
withRoom(makeRoom(
withRoomId("!foo:example.org")
| withRoomTimeline({event1, event2})
| withRoomTimelineGaps({{event1.id(), "prevBatchForEvent1"}})
))
);
auto store = createTestClientStoreFrom(m, ph);
auto paginateRespWithEvent1 = paginateResponseJson;
paginateRespWithEvent1["chunk"] = json::array({event1.raw().get()});
auto resp = makeResponse(
"GetRoomEvents",
withResponseJsonBody(paginateRespWithEvent1)
| withResponseDataKV("roomId", "!foo:example.org")
| withResponseDataKV("gapEventId", "$event1:example.org")
);
store.dispatch(ProcessResponseAction{resp});
io.run();
auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }), store, std::nullopt);
auto r = client.room("!foo:example.org");
auto timelineGaps = +r.timelineGaps();
REQUIRE(! timelineGaps.find("$event1:example.org"));
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Oct 2, 4:27 AM (20 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
471711
Default Alt Text
paginate-test.cpp (7 KB)

Event Timeline