Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for joining a new federated room #31

Merged
merged 15 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions server/ensure-room-joined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

const assert = require('assert');
const { URLSearchParams } = require('url');
const urlJoin = require('url-join');

const { fetchEndpointAsJson } = require('./lib/fetch-endpoint');

const config = require('./lib/config');
const matrixServerUrl = config.get('matrixServerUrl');
assert(matrixServerUrl);

async function ensureRoomJoined(accessToken, roomId, viaServers) {
let qs = new URLSearchParams();
[].concat(viaServers).forEach((viaServer) => {
qs.append('via', viaServer);
});

// TODO: Only join world_readable rooms. Perhaps we want to serve public rooms
// where we have been invited. GET
// /_matrix/client/v3/directory/list/room/{roomId} (Gets the visibility of a
// given room on the server’s public room directory.)
const joinEndpoint = urlJoin(
matrixServerUrl,
`_matrix/client/r0/join/${roomId}?${qs.toString()}`
);
await fetchEndpointAsJson(joinEndpoint, {
method: 'POST',
accessToken,
});
}

module.exports = ensureRoomJoined;
10 changes: 0 additions & 10 deletions server/fetch-events-in-range.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@ async function fetchEventsFromTimestampBackwards(accessToken, roomId, ts, limit)
assert(ts);
assert(limit);

// TODO: Only join world_readable rooms. Perhaps we want to serve public rooms
// where we have been invited. GET
// /_matrix/client/v3/directory/list/room/{roomId} (Gets the visibility of a
// given room on the server’s public room directory.)
const joinEndpoint = urlJoin(matrixServerUrl, `_matrix/client/r0/join/${roomId}`);
await fetchEndpointAsJson(joinEndpoint, {
method: 'POST',
accessToken,
});

const timestampToEventEndpoint = urlJoin(
matrixServerUrl,
`_matrix/client/unstable/org.matrix.msc3030/rooms/${roomId}/timestamp_to_event?ts=${ts}&dir=b`
Expand Down
10 changes: 8 additions & 2 deletions server/routes/install-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const StatusError = require('../lib/status-error');

const fetchRoomData = require('../fetch-room-data');
const fetchEventsInRange = require('../fetch-events-in-range');
const ensureRoomJoined = require('../ensure-room-joined');
const renderHydrogenToString = require('../render-hydrogen-to-string');

const config = require('../lib/config');
Expand Down Expand Up @@ -117,22 +118,27 @@ function installRoutes(app) {
// the format isn't correct, redirect to the correct hour range
if (hourRange && toHour !== fromHour + 1) {
res.redirect(
urlJoin(
// FIXME: Can we use the matrixPublicArchiveURLCreator here?
`${urlJoin(
basePath,
roomIdOrAlias,
'date',
req.params.yyyy,
req.params.mm,
req.params.dd,
`${fromHour}-${fromHour + 1}`
)
)}?${new URLSearchParams(req.query).toString()}`
);
return;
}

// TODO: Highlight tile that matches ?at=$xxx
//const aroundId = req.query.at;

// We have to wait for the room join to happen first before we can fetch
// any of the additional room info or messages.
await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved

// Do these in parallel to avoid the extra time in sequential round-trips
// (we want to display the archive page faster)
const [roomData, { events, stateEventMap }] = await Promise.all([
Expand Down
10 changes: 8 additions & 2 deletions shared/lib/url-creator.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
'use strict';

const urlJoin = require('url-join');
const { URLSearchParams } = require('url');

class URLCreator {
constructor(basePath) {
this._basePath = basePath;
}

archiveUrlForDate(roomId, date) {
archiveUrlForDate(roomId, date, { viaServers = [] } = {}) {
let qs = new URLSearchParams();
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved
[].concat(viaServers).forEach((viaServer) => {
qs.append('via', viaServer);
});

// Gives the date in YYYY/mm/dd format.
// date.toISOString() -> 2022-02-16T23:20:04.709Z
const urlDate = date.toISOString().split('T')[0].replaceAll('-', '/');

return urlJoin(this._basePath, `${roomId}/date/${urlDate}`);
return `${urlJoin(this._basePath, `${roomId}/date/${urlDate}`)}?${qs.toString()}`;
}
}

Expand Down
20 changes: 20 additions & 0 deletions test/client-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ function getTxnId() {
return `${new Date().getTime()}--${txnCount}`;
}

async function ensureUserRegistered({ matrixServerUrl, username }) {
const registerResponse = await fetchEndpointAsJson(
urlJoin(matrixServerUrl, '/_matrix/client/v3/register'),
{
method: 'POST',
body: {
type: 'm.login.dummy',
username,
},
}
);

const userId = registerResponse['user_id'];
assert(userId);
}

// Get client to act with for all of the client methods. This will use the
// application service access token and client methods will append `?user_id`
// for the specific user to act upon so we can use the `?ts` message timestamp
Expand Down Expand Up @@ -169,6 +185,9 @@ async function createMessagesInRoom({ client, roomId, numMessages, prefix, times
eventIds.push(eventId);
}

// Sanity check that we actually sent some messages
assert.strictEqual(eventIds.length, numMessages);

return eventIds;
}

Expand Down Expand Up @@ -248,6 +267,7 @@ async function uploadContent({ client, roomId, data, fileName, contentType }) {
}

module.exports = {
ensureUserRegistered,
getTestClientForHs,
createTestRoom,
joinRoom,
Expand Down
53 changes: 52 additions & 1 deletion test/e2e-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const { fetchEndpointAsText, fetchEndpointAsJson } = require('../server/lib/fetc
const config = require('../server/lib/config');

const {
ensureUserRegistered,
getTestClientForHs,
createTestRoom,
joinRoom,
Expand Down Expand Up @@ -106,6 +107,23 @@ describe('matrix-public-archive', () => {
});

describe('Archive', () => {
before(async () => {
// FIXME: This doesn't work because `400 Bad Request:
// {"errcode":"M_EXCLUSIVE","error":"This user ID is reserved by an
// application service."}` and we can't easily remove the AS registration,
// register the user, add the AS back, restart Synapse, and continue the
// tests.
//
// Make sure the application service archiver user itself is also
// explicitly registered otherwise we run into 404, `Profile was not
// found` errors when joining a remote federated room from the archiver
// user, see https://github.com/matrix-org/synapse/issues/4778
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved
await ensureUserRegistered({
matrixServerUrl: testMatrixServerUrl1,
username: 'archiver',
});
});

// Use a fixed date at the start of the UTC day so that the tests are
// consistent. Otherwise, the tests could fail when they start close to
// midnight and it rolls over to the next day.
Expand Down Expand Up @@ -163,6 +181,7 @@ describe('matrix-public-archive', () => {
`Coulomb's Law of Friction: Kinetic friction is independent of the sliding velocity.`,
];

// TODO: Can we use `createMessagesInRoom` here instead?
const eventIds = [];
for (const messageText of messageTextList) {
const eventId = await sendMessageOnArchiveDate({
Expand Down Expand Up @@ -379,7 +398,39 @@ describe('matrix-public-archive', () => {
);
});

it(`can render day back in time from room on remote homeserver we haven't backfilled from`);
it(`can render day back in time from room on remote homeserver we haven't backfilled from`, async () => {
//const hs1Client = await getTestClientForHs(testMatrixServerUrl1);
const hs2Client = await getTestClientForHs(testMatrixServerUrl2);

// Create a room on hs2
const hs2RoomId = await createTestRoom(hs2Client);
const room2EventIds = await createMessagesInRoom({
client: hs2Client,
roomId: hs2RoomId,
numMessages: 3,
prefix: HOMESERVER_URL_TO_PRETTY_NAME_MAP[hs2Client.homeserverUrl],
timestamp: archiveDate.getTime(),
});

archiveUrl = matrixPublicArchiveURLCreator.archiveUrlForDate(hs2RoomId, archiveDate, {
// Since hs1 doesn't know about this room on hs2 yet, we have to provide
// a via server to ask through.
viaServers: ['hs2'],
});

const archivePageHtml = await fetchEndpointAsText(archiveUrl);
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved

const dom = parseHTML(archivePageHtml);

// Make sure the messages are visible
for (let i = 0; i < room2EventIds.length; i++) {
const eventId = room2EventIds[i];
assert.strictEqual(
dom.document.querySelectorAll(`[data-event-id="${eventId}"]`).length,
room2EventIds.length
);
}
});

it(`will redirect to hour pagination when there are too many messages`);

Expand Down