Remove `matrix-bot-sdk` usage in tests (#15)
Remove `matrix-bot-sdk` usage in tests because it didn't have timestamp massaging `?ts` and it's not really necessary to rely on since we can just call the API directly 🤷. `matrix-bot-sdk` is also very annoying having to build rust crypto packages. We're now using direct `fetch` requests against the Matrix API and lightweight `client` object. All 3 current tests pass ✅
This commit is contained in:
parent
7dfe8cabc9
commit
9fc71a3412
File diff suppressed because it is too large
Load Diff
|
@ -31,8 +31,6 @@
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"hydrogen-view-sdk": "^0.0.4",
|
"hydrogen-view-sdk": "^0.0.4",
|
||||||
"linkedom": "^0.14.1",
|
"linkedom": "^0.14.1",
|
||||||
"matrix-bot-sdk": "^0.5.19",
|
|
||||||
"matrix-js-sdk": "^15.5.2",
|
|
||||||
"matrix-public-archive-shared": "file:./shared/",
|
"matrix-public-archive-shared": "file:./shared/",
|
||||||
"nconf": "^0.11.3",
|
"nconf": "^0.11.3",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
|
|
|
@ -16,7 +16,7 @@ assert(matrixServerUrl);
|
||||||
// Consider this scenario: dayStart(fromTs) <---- msg1 <- msg2 <-- msg3 <---- dayEnd(toTs)
|
// Consider this scenario: dayStart(fromTs) <---- msg1 <- msg2 <-- msg3 <---- dayEnd(toTs)
|
||||||
// - ❌ If we start from dayStart and look backwards, we will find nothing.
|
// - ❌ If we start from dayStart and look backwards, we will find nothing.
|
||||||
// - ❌ If we start from dayStart and look forwards, we will find msg1, but federated backfill won't be able to paginate forwards
|
// - ❌ If we start from dayStart and look forwards, we will find msg1, but federated backfill won't be able to paginate forwards
|
||||||
// - ✅ If we start from dayEnd and look backwards, we will msg3
|
// - ✅ If we start from dayEnd and look backwards, we will find msg3
|
||||||
// - ❌ If we start from dayEnd and look forwards, we will find nothing
|
// - ❌ If we start from dayEnd and look forwards, we will find nothing
|
||||||
//
|
//
|
||||||
// Returns events in reverse-chronological order.
|
// Returns events in reverse-chronological order.
|
||||||
|
|
|
@ -20,7 +20,7 @@ function configureNodeEnv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeEnv = configureNodeEnv();
|
const nodeEnv = configureNodeEnv();
|
||||||
console.log('nodeEnv', nodeEnv);
|
console.log(`Config is using nodeEnv=${nodeEnv}`);
|
||||||
const configDir = path.join(__dirname, '../../config');
|
const configDir = path.join(__dirname, '../../config');
|
||||||
|
|
||||||
nconf.argv().env('__');
|
nconf.argv().env('__');
|
||||||
|
|
|
@ -33,6 +33,7 @@ async function fetchEndpoint(endpoint, options = {}) {
|
||||||
const res = await fetch(endpoint, {
|
const res = await fetch(endpoint, {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
|
body: options.body,
|
||||||
});
|
});
|
||||||
await checkResponseStatus(res);
|
await checkResponseStatus(res);
|
||||||
|
|
||||||
|
@ -49,11 +50,16 @@ async function fetchEndpointAsJson(endpoint, options) {
|
||||||
const opts = {
|
const opts = {
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...(options.headers || {}),
|
...(options.headers || {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.body) {
|
||||||
|
opts.body = JSON.stringify(options.body);
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetchEndpoint(endpoint, opts);
|
const res = await fetchEndpoint(endpoint, opts);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -174,7 +174,7 @@ async function mountHydrogen() {
|
||||||
const memberEvent = workingStateEventMap[event.user_id];
|
const memberEvent = workingStateEventMap[event.user_id];
|
||||||
return makeEventEntryFromEventJson(event, memberEvent);
|
return makeEventEntryFromEventJson(event, memberEvent);
|
||||||
});
|
});
|
||||||
console.log('eventEntries', eventEntries.length);
|
//console.log('eventEntries', eventEntries.length);
|
||||||
|
|
||||||
// Map of `event_id` to `EventEntry`
|
// Map of `event_id` to `EventEntry`
|
||||||
const eventEntriesByEventId = eventEntries.reduce((currentMap, eventEntry) => {
|
const eventEntriesByEventId = eventEntries.reduce((currentMap, eventEntry) => {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
```
|
```sh
|
||||||
$ docker pull matrixdotorg/synapse:latest
|
$ docker pull matrixdotorg/synapse:latest
|
||||||
$ docker build -t matrix-public-archive-test-homeserver -f test/dockerfiles/Synapse.Dockerfile test/dockerfiles/
|
$ docker build -t matrix-public-archive-test-homeserver -f test/dockerfiles/Synapse.Dockerfile test/dockerfiles/
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```sh
|
||||||
docker-compose -f test/docker-compose.yml up -d --no-recreate
|
$ docker-compose --project-name matrix_public_archive_test -f test/docker-compose.yml up -d --no-recreate
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```sh
|
||||||
$ docker ps --all | grep test_hs
|
$ docker ps --all | grep test_hs
|
||||||
$ docker logs test_hs1_1
|
$ docker logs -f --tail 10 matrix_public_archive_test_hs1_1
|
||||||
$ docker logs test_hs2_1
|
$ docker logs -f --tail 10 matrix_public_archive_test_hs2_1
|
||||||
|
|
||||||
$ docker stop test_hs1_1 test_hs2_1
|
$ docker stop matrix_public_archive_test_hs1_1 matrix_public_archive_test_hs2_1
|
||||||
$ docker rm test_hs1_1 test_hs2_1
|
$ docker rm matrix_public_archive_test_hs1_1 matrix_public_archive_test_hs2_1
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { URLSearchParams } = require('url');
|
||||||
|
const urlJoin = require('url-join');
|
||||||
|
const { fetchEndpointAsJson, fetchEndpoint } = require('../server/lib/fetch-endpoint');
|
||||||
|
|
||||||
|
const config = require('../server/lib/config');
|
||||||
|
const matrixAccessToken = config.get('matrixAccessToken');
|
||||||
|
assert(matrixAccessToken);
|
||||||
|
|
||||||
|
let txnCount = 0;
|
||||||
|
function getTxnId() {
|
||||||
|
txnCount++;
|
||||||
|
return `${new Date().getTime()}--${txnCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// massaging when sending.
|
||||||
|
async function getTestClientForHs(testMatrixServerUrl) {
|
||||||
|
// Register the virtual user
|
||||||
|
const username = `user-t${new Date().getTime()}-r${Math.floor(Math.random() * 1000000000)}`;
|
||||||
|
const registerResponse = await fetchEndpointAsJson(
|
||||||
|
urlJoin(testMatrixServerUrl, '/_matrix/client/v3/register'),
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
type: 'm.login.application_service',
|
||||||
|
username,
|
||||||
|
},
|
||||||
|
accessToken: matrixAccessToken,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const userId = registerResponse['user_id'];
|
||||||
|
assert(userId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
homeserverUrl: testMatrixServerUrl,
|
||||||
|
// We use the application service AS token because we need to be able to use
|
||||||
|
// the `?ts` timestamp massaging when sending events
|
||||||
|
accessToken: matrixAccessToken,
|
||||||
|
userId: userId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a public room to test in
|
||||||
|
async function createTestRoom(client) {
|
||||||
|
let qs = new URLSearchParams();
|
||||||
|
if (client.userId) {
|
||||||
|
qs.append('user_id', client.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createRoomResponse = await fetchEndpointAsJson(
|
||||||
|
urlJoin(client.homeserverUrl, `/_matrix/client/v3/createRoom?${qs.toString()}`),
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
preset: 'public_chat',
|
||||||
|
name: 'the hangout spot',
|
||||||
|
initial_state: [
|
||||||
|
{
|
||||||
|
type: 'm.room.history_visibility',
|
||||||
|
state_key: '',
|
||||||
|
content: {
|
||||||
|
history_visibility: 'world_readable',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
accessToken: client.accessToken,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const roomId = createRoomResponse['room_id'];
|
||||||
|
assert(roomId);
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function joinRoom({ client, roomId, viaServers }) {
|
||||||
|
let qs = new URLSearchParams();
|
||||||
|
if (viaServers) {
|
||||||
|
[].concat(viaServers).forEach((viaServer) => {
|
||||||
|
qs.append('server_name', viaServer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.userId) {
|
||||||
|
qs.append('user_id', client.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinRoomResponse = await fetchEndpointAsJson(
|
||||||
|
urlJoin(client.homeserverUrl, `/_matrix/client/v3/join/${roomId}?${qs.toString()}`),
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
accessToken: client.accessToken,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const joinedRoomId = joinRoomResponse['room_id'];
|
||||||
|
assert(joinedRoomId);
|
||||||
|
return joinedRoomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendEvent({ client, roomId, eventType, content, timestamp }) {
|
||||||
|
assert(client);
|
||||||
|
assert(roomId);
|
||||||
|
assert(content);
|
||||||
|
|
||||||
|
let qs = new URLSearchParams();
|
||||||
|
if (timestamp) {
|
||||||
|
assert(
|
||||||
|
timestamp && client.userId,
|
||||||
|
'We can only do `?ts` massaging from an application service access token. ' +
|
||||||
|
'Expected `client.userId` to be defined so we can act on behalf of that user'
|
||||||
|
);
|
||||||
|
|
||||||
|
qs.append('ts', timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.userId) {
|
||||||
|
qs.append('user_id', client.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendResponse = await fetchEndpointAsJson(
|
||||||
|
urlJoin(
|
||||||
|
client.homeserverUrl,
|
||||||
|
`/_matrix/client/v3/rooms/${roomId}/send/${eventType}/${getTxnId()}?${qs.toString()}`
|
||||||
|
),
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
body: content,
|
||||||
|
accessToken: client.accessToken,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventId = sendResponse['event_id'];
|
||||||
|
assert(eventId);
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessage({ client, roomId, content, timestamp }) {
|
||||||
|
return sendEvent({ client, roomId, eventType: 'm.room.message', content, timestamp });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a number of messages in the given room
|
||||||
|
async function createMessagesInRoom({ client, roomId, numMessages, prefix, timestamp }) {
|
||||||
|
let eventIds = [];
|
||||||
|
for (let i = 0; i < numMessages; i++) {
|
||||||
|
const eventId = await sendMessage({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
|
msgtype: 'm.text',
|
||||||
|
body: `${prefix} - message${i}`,
|
||||||
|
},
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
eventIds.push(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uploads the given data Buffer and returns the MXC URI of the uploaded content
|
||||||
|
async function uploadContent({ client, roomId, data, fileName, contentType }) {
|
||||||
|
assert(client);
|
||||||
|
assert(roomId);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
let qs = new URLSearchParams();
|
||||||
|
if (client.userId) {
|
||||||
|
qs.append('user_id', client.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName) {
|
||||||
|
qs.append('filename', fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to use `fetchEndpointAsJson` here because it will
|
||||||
|
// `JSON.stringify(...)` the body data
|
||||||
|
const uploadResponse = await fetchEndpoint(
|
||||||
|
urlJoin(client.homeserverUrl, `/_matrix/media/v3/upload`),
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: data,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': contentType || 'application/octet-stream',
|
||||||
|
},
|
||||||
|
accessToken: client.accessToken,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const uploadResponseData = await uploadResponse.json();
|
||||||
|
|
||||||
|
const mxcUri = uploadResponseData['content_uri'];
|
||||||
|
assert(mxcUri);
|
||||||
|
return mxcUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getTestClientForHs,
|
||||||
|
createTestRoom,
|
||||||
|
joinRoom,
|
||||||
|
sendEvent,
|
||||||
|
sendMessage,
|
||||||
|
createMessagesInRoom,
|
||||||
|
uploadContent,
|
||||||
|
};
|
|
@ -24,7 +24,7 @@ COPY keys/* /ca/
|
||||||
RUN openssl genrsa -out /conf/server.tls.key 2048
|
RUN openssl genrsa -out /conf/server.tls.key 2048
|
||||||
|
|
||||||
# generate a signing key
|
# generate a signing key
|
||||||
RUN generate_signing_key.py -o /conf/server.signing.key
|
RUN generate_signing_key -o /conf/server.signing.key
|
||||||
|
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ report_stats: False
|
||||||
signing_key_path: /conf/server.signing.key
|
signing_key_path: /conf/server.signing.key
|
||||||
trusted_key_servers: []
|
trusted_key_servers: []
|
||||||
enable_registration: true
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
||||||
## Listeners ##
|
## Listeners ##
|
||||||
|
|
||||||
|
@ -94,6 +95,13 @@ rc_joins:
|
||||||
|
|
||||||
federation_rr_transactions_per_room_per_second: 9999
|
federation_rr_transactions_per_room_per_second: 9999
|
||||||
|
|
||||||
|
## Media Store ##
|
||||||
|
|
||||||
|
# Whether to generate new thumbnails on the fly. This lets the image thumbnails
|
||||||
|
# load in the tests.
|
||||||
|
#
|
||||||
|
dynamic_thumbnails: true
|
||||||
|
|
||||||
## API Configuration ##
|
## API Configuration ##
|
||||||
|
|
||||||
# A list of application service config files to use
|
# A list of application service config files to use
|
||||||
|
|
|
@ -3,16 +3,26 @@
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const path = require('path');
|
||||||
const urlJoin = require('url-join');
|
const urlJoin = require('url-join');
|
||||||
const escapeStringRegexp = require('escape-string-regexp');
|
const escapeStringRegexp = require('escape-string-regexp');
|
||||||
const { MatrixAuth } = require('matrix-bot-sdk');
|
|
||||||
const { parseHTML } = require('linkedom');
|
const { parseHTML } = require('linkedom');
|
||||||
|
const { readFile } = require('fs').promises;
|
||||||
const { fetchEndpointAsText, fetchEndpointAsJson } = require('../server/lib/fetch-endpoint');
|
|
||||||
|
|
||||||
const MatrixPublicArchiveURLCreator = require('matrix-public-archive-shared/lib/url-creator');
|
const MatrixPublicArchiveURLCreator = require('matrix-public-archive-shared/lib/url-creator');
|
||||||
|
const { fetchEndpointAsText, fetchEndpointAsJson } = require('../server/lib/fetch-endpoint');
|
||||||
const config = require('../server/lib/config');
|
const config = require('../server/lib/config');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getTestClientForHs,
|
||||||
|
createTestRoom,
|
||||||
|
joinRoom,
|
||||||
|
sendEvent,
|
||||||
|
sendMessage,
|
||||||
|
createMessagesInRoom,
|
||||||
|
uploadContent,
|
||||||
|
} = require('./client-utils');
|
||||||
|
|
||||||
const testMatrixServerUrl1 = config.get('testMatrixServerUrl1');
|
const testMatrixServerUrl1 = config.get('testMatrixServerUrl1');
|
||||||
const testMatrixServerUrl2 = config.get('testMatrixServerUrl2');
|
const testMatrixServerUrl2 = config.get('testMatrixServerUrl2');
|
||||||
assert(testMatrixServerUrl1);
|
assert(testMatrixServerUrl1);
|
||||||
|
@ -28,48 +38,6 @@ const HOMESERVER_URL_TO_PRETTY_NAME_MAP = {
|
||||||
[testMatrixServerUrl2]: 'hs2',
|
[testMatrixServerUrl2]: 'hs2',
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getTestClientForHs(testMatrixServerUrl) {
|
|
||||||
const auth = new MatrixAuth(testMatrixServerUrl);
|
|
||||||
|
|
||||||
const client = await auth.passwordRegister(
|
|
||||||
`user-t${new Date().getTime()}-r${Math.floor(Math.random() * 1000000000)}`,
|
|
||||||
'password'
|
|
||||||
);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createMessagesInRoom(client, roomId, numMessages, prefix) {
|
|
||||||
let eventIds = [];
|
|
||||||
for (let i = 0; i < numMessages; i++) {
|
|
||||||
const eventId = await client.sendMessage(roomId, {
|
|
||||||
msgtype: 'm.text',
|
|
||||||
body: `${prefix} - message${i}`,
|
|
||||||
});
|
|
||||||
eventIds.push(eventId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createTestRoom(client) {
|
|
||||||
const roomId = await client.createRoom({
|
|
||||||
preset: 'public_chat',
|
|
||||||
name: 'the hangout spot',
|
|
||||||
initial_state: [
|
|
||||||
{
|
|
||||||
type: 'm.room.history_visibility',
|
|
||||||
state_key: '',
|
|
||||||
content: {
|
|
||||||
history_visibility: 'world_readable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return roomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('matrix-public-archive', () => {
|
describe('matrix-public-archive', () => {
|
||||||
let server;
|
let server;
|
||||||
before(() => {
|
before(() => {
|
||||||
|
@ -83,24 +51,28 @@ describe('matrix-public-archive', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Test fixture homeservers', () => {
|
||||||
// Sanity check that our test homeservers can actually federate with each
|
// Sanity check that our test homeservers can actually federate with each
|
||||||
// other. The rest of the tests won't work properly if this isn't working.
|
// other. The rest of the tests won't work properly if this isn't working.
|
||||||
it('Test federation between fixture homeservers', async () => {
|
it('Test federation between fixture homeservers', async () => {
|
||||||
try {
|
|
||||||
const hs1Client = await getTestClientForHs(testMatrixServerUrl1);
|
const hs1Client = await getTestClientForHs(testMatrixServerUrl1);
|
||||||
const hs2Client = await getTestClientForHs(testMatrixServerUrl2);
|
const hs2Client = await getTestClientForHs(testMatrixServerUrl2);
|
||||||
|
|
||||||
// Create a room on hs2
|
// Create a room on hs2
|
||||||
const hs2RoomId = await createTestRoom(hs2Client);
|
const hs2RoomId = await createTestRoom(hs2Client);
|
||||||
const room2EventIds = await createMessagesInRoom(
|
const room2EventIds = await createMessagesInRoom({
|
||||||
hs2Client,
|
client: hs2Client,
|
||||||
hs2RoomId,
|
roomId: hs2RoomId,
|
||||||
10,
|
numMessages: 10,
|
||||||
HOMESERVER_URL_TO_PRETTY_NAME_MAP[hs2Client.homeserverUrl]
|
prefix: HOMESERVER_URL_TO_PRETTY_NAME_MAP[hs2Client.homeserverUrl],
|
||||||
);
|
});
|
||||||
|
|
||||||
// Join hs1 to a room on hs2 (federation)
|
// Join hs1 to a room on hs2 (federation)
|
||||||
await hs1Client.joinRoom(hs2RoomId, 'hs2');
|
await joinRoom({
|
||||||
|
client: hs1Client,
|
||||||
|
roomId: hs2RoomId,
|
||||||
|
viaServers: 'hs2',
|
||||||
|
});
|
||||||
|
|
||||||
// From, hs1, make sure we can fetch messages from hs2
|
// From, hs1, make sure we can fetch messages from hs2
|
||||||
const messagesEndpoint = urlJoin(
|
const messagesEndpoint = urlJoin(
|
||||||
|
@ -129,31 +101,59 @@ describe('matrix-public-archive', () => {
|
||||||
}") to be in room on hs2=${JSON.stringify(room2EventIds)}`
|
}") to be in room on hs2=${JSON.stringify(room2EventIds)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
});
|
||||||
if (err.body) {
|
|
||||||
// FIXME: Remove this try/catch once the matrix-bot-sdk no longer throws
|
|
||||||
// huge response objects as errors, see
|
|
||||||
// https://github.com/turt2live/matrix-bot-sdk/pull/158
|
|
||||||
throw new Error(
|
|
||||||
`Error occured in matrix-bot-sdk (this new error is to stop it from logging the huge response) statusCode=${
|
|
||||||
err.statusCode
|
|
||||||
} body=${JSON.stringify(err.body)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Archive', () => {
|
||||||
|
// 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.
|
||||||
|
const archiveDate = new Date(Date.UTC(2022, 0, 3));
|
||||||
|
let archiveUrl;
|
||||||
|
let numMessagesSent = 0;
|
||||||
|
afterEach(() => {
|
||||||
|
if (interactive) {
|
||||||
|
console.log('Interactive URL for test', archiveUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset `numMessagesSent` between tests so each test starts from the
|
||||||
|
// beginning of the day and we don't run out of minutes in the day to send
|
||||||
|
// messages in (we space messages out by a minute so the timestamp visibly
|
||||||
|
// changes in the UI).
|
||||||
|
numMessagesSent = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sends a message and makes sure that a timestamp was provided
|
||||||
|
async function sendMessageOnArchiveDate(options) {
|
||||||
|
const minute = 1000 * 60;
|
||||||
|
// Adjust the timestamp by a minute each time so there is some visual difference.
|
||||||
|
options.timestamp = archiveDate.getTime() + minute * numMessagesSent;
|
||||||
|
numMessagesSent++;
|
||||||
|
|
||||||
|
return sendMessage(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a message and makes sure that a timestamp was provided
|
||||||
|
async function sendEventOnArchiveDate(options) {
|
||||||
|
const minute = 1000 * 60;
|
||||||
|
// Adjust the timestamp by a minute each time so there is some visual difference.
|
||||||
|
options.timestamp = archiveDate.getTime() + minute * numMessagesSent;
|
||||||
|
numMessagesSent++;
|
||||||
|
|
||||||
|
return sendEvent(options);
|
||||||
|
}
|
||||||
|
|
||||||
it('shows all events in a given day', async () => {
|
it('shows all events in a given day', async () => {
|
||||||
try {
|
|
||||||
const client = await getTestClientForHs(testMatrixServerUrl1);
|
const client = await getTestClientForHs(testMatrixServerUrl1);
|
||||||
const roomId = await createTestRoom(client);
|
const roomId = await createTestRoom(client);
|
||||||
|
|
||||||
const archiveUrl = matrixPublicArchiveURLCreator.archiveUrlForDate(roomId, new Date());
|
// Just render the page initially so that the archiver user is already
|
||||||
// Just render the page initially so that the archiver user is already joined to the page.
|
// joined to the page. We don't want their join event masking the one-off
|
||||||
// We don't want their join event masking the one-off problem where we're missing the latest message in the room.
|
// problem where we're missing the latest message in the room. We just use the date now
|
||||||
await fetchEndpointAsText(archiveUrl);
|
// because it will find whatever events backwards no matter when they were sent.
|
||||||
|
await fetchEndpointAsText(
|
||||||
|
matrixPublicArchiveURLCreator.archiveUrlForDate(roomId, new Date())
|
||||||
|
);
|
||||||
|
|
||||||
const messageTextList = [
|
const messageTextList = [
|
||||||
`Amontons' First Law: The force of friction is directly proportional to the applied load.`,
|
`Amontons' First Law: The force of friction is directly proportional to the applied load.`,
|
||||||
|
@ -164,9 +164,13 @@ describe('matrix-public-archive', () => {
|
||||||
|
|
||||||
const eventIds = [];
|
const eventIds = [];
|
||||||
for (const messageText of messageTextList) {
|
for (const messageText of messageTextList) {
|
||||||
const eventId = await client.sendMessage(roomId, {
|
const eventId = await sendMessageOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
msgtype: 'm.text',
|
msgtype: 'm.text',
|
||||||
body: messageText,
|
body: messageText,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
eventIds.push(eventId);
|
eventIds.push(eventId);
|
||||||
}
|
}
|
||||||
|
@ -174,10 +178,7 @@ describe('matrix-public-archive', () => {
|
||||||
// Sanity check that we actually sent some messages
|
// Sanity check that we actually sent some messages
|
||||||
assert.strictEqual(eventIds.length, 3);
|
assert.strictEqual(eventIds.length, 3);
|
||||||
|
|
||||||
if (interactive) {
|
archiveUrl = matrixPublicArchiveURLCreator.archiveUrlForDate(roomId, archiveDate);
|
||||||
console.log('Interactive URL for test', archiveUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
const archivePageHtml = await fetchEndpointAsText(archiveUrl);
|
const archivePageHtml = await fetchEndpointAsText(archiveUrl);
|
||||||
|
|
||||||
const dom = parseHTML(archivePageHtml);
|
const dom = parseHTML(archivePageHtml);
|
||||||
|
@ -191,25 +192,10 @@ describe('matrix-public-archive', () => {
|
||||||
new RegExp(`.*${escapeStringRegexp(eventText)}.*`)
|
new RegExp(`.*${escapeStringRegexp(eventText)}.*`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
if (err.body) {
|
|
||||||
// FIXME: Remove this try/catch once the matrix-bot-sdk no longer throws
|
|
||||||
// huge response objects as errors, see
|
|
||||||
// https://github.com/turt2live/matrix-bot-sdk/pull/158
|
|
||||||
throw new Error(
|
|
||||||
`Error occured in matrix-bot-sdk (this new error is to stop it from logging the huge response) statusCode=${
|
|
||||||
err.statusCode
|
|
||||||
} body=${JSON.stringify(err.body)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line max-statements
|
// eslint-disable-next-line max-statements
|
||||||
it('can render diverse messages', async () => {
|
it('can render diverse messages', async () => {
|
||||||
try {
|
|
||||||
const client = await getTestClientForHs(testMatrixServerUrl1);
|
const client = await getTestClientForHs(testMatrixServerUrl1);
|
||||||
const roomId = await createTestRoom(client);
|
const roomId = await createTestRoom(client);
|
||||||
|
|
||||||
|
@ -218,49 +204,64 @@ describe('matrix-public-archive', () => {
|
||||||
// TODO: Set avatar of room
|
// TODO: Set avatar of room
|
||||||
|
|
||||||
// Test image
|
// Test image
|
||||||
const mxcUri = await client.uploadContentFromUrl(
|
// via https://en.wikipedia.org/wiki/Friction#/media/File:Friction_between_surfaces.jpg (CaoHao)
|
||||||
'https://en.wikipedia.org/wiki/Friction#/media/File:Friction_between_surfaces.jpg'
|
const imageBuffer = await readFile(
|
||||||
|
path.resolve(__dirname, './fixtures/friction_between_surfaces.jpg')
|
||||||
);
|
);
|
||||||
const imageEventId = await client.sendMessage(roomId, {
|
const imageFileName = 'friction_between_surfaces.jpg';
|
||||||
body: 'Friction_between_surfaces.jpeg',
|
const mxcUri = await uploadContent({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
data: imageBuffer,
|
||||||
|
fileName: imageFileName,
|
||||||
|
});
|
||||||
|
const imageEventId = await sendMessageOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
|
body: imageFileName,
|
||||||
info: {
|
info: {
|
||||||
size: 396644,
|
size: 17471,
|
||||||
mimetype: 'image/jpeg',
|
mimetype: 'image/jpeg',
|
||||||
thumbnail_info: {
|
w: 640,
|
||||||
w: 800,
|
h: 312,
|
||||||
h: 390,
|
'xyz.amorgan.blurhash': 'LkR3G|IU?w%NbxbIemae_NxuD$M{',
|
||||||
mimetype: 'image/jpeg',
|
|
||||||
size: 126496,
|
|
||||||
},
|
|
||||||
w: 1894,
|
|
||||||
h: 925,
|
|
||||||
'xyz.amorgan.blurhash': 'LkR3G|IU?w%NbwbIemae_NxuD$M{',
|
|
||||||
// TODO: How to get a proper thumnail URL that will load?
|
|
||||||
thumbnail_url: mxcUri,
|
|
||||||
},
|
},
|
||||||
msgtype: 'm.image',
|
msgtype: 'm.image',
|
||||||
url: mxcUri,
|
url: mxcUri,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// A normal text message
|
// A normal text message
|
||||||
const normalMessageText1 =
|
const normalMessageText1 =
|
||||||
'^ Figure 1: Simulated blocks with fractal rough surfaces, exhibiting static frictional interactions';
|
'^ Figure 1: Simulated blocks with fractal rough surfaces, exhibiting static frictional interactions';
|
||||||
const normalMessageEventId1 = await client.sendMessage(roomId, {
|
const normalMessageEventId1 = await sendMessageOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
msgtype: 'm.text',
|
msgtype: 'm.text',
|
||||||
body: normalMessageText1,
|
body: normalMessageText1,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Another normal text message
|
// Another normal text message
|
||||||
const normalMessageText2 =
|
const normalMessageText2 =
|
||||||
'The topography of the Moon has been measured with laser altimetry and stereo image analysis.';
|
'The topography of the Moon has been measured with laser altimetry and stereo image analysis.';
|
||||||
const normalMessageEventId2 = await client.sendMessage(roomId, {
|
const normalMessageEventId2 = await sendMessageOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
msgtype: 'm.text',
|
msgtype: 'm.text',
|
||||||
body: normalMessageText2,
|
body: normalMessageText2,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test replies
|
// Test replies
|
||||||
const replyMessageText = `The concentration of maria on the near side likely reflects the substantially thicker crust of the highlands of the Far Side, which may have formed in a slow-velocity impact of a second moon of Earth a few tens of millions of years after the Moon's formation.`;
|
const replyMessageText = `The concentration of maria on the near side likely reflects the substantially thicker crust of the highlands of the Far Side, which may have formed in a slow-velocity impact of a second moon of Earth a few tens of millions of years after the Moon's formation.`;
|
||||||
const replyMessageEventId = await client.sendMessage(roomId, {
|
const replyMessageEventId = await sendMessageOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
content: {
|
||||||
'org.matrix.msc1767.message': [
|
'org.matrix.msc1767.message': [
|
||||||
{
|
{
|
||||||
body: '> <@ericgittertester:my.synapse.server> ${normalMessageText2}',
|
body: '> <@ericgittertester:my.synapse.server> ${normalMessageText2}',
|
||||||
|
@ -280,22 +281,25 @@ describe('matrix-public-archive', () => {
|
||||||
event_id: normalMessageEventId2,
|
event_id: normalMessageEventId2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test reactions
|
// Test reactions
|
||||||
const reactionText = '😅';
|
const reactionText = '😅';
|
||||||
await client.sendEvent(roomId, 'm.reaction', {
|
await sendEventOnArchiveDate({
|
||||||
|
client,
|
||||||
|
roomId,
|
||||||
|
eventType: 'm.reaction',
|
||||||
|
content: {
|
||||||
'm.relates_to': {
|
'm.relates_to': {
|
||||||
rel_type: 'm.annotation',
|
rel_type: 'm.annotation',
|
||||||
event_id: replyMessageEventId,
|
event_id: replyMessageEventId,
|
||||||
key: reactionText,
|
key: reactionText,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const archiveUrl = matrixPublicArchiveURLCreator.archiveUrlForDate(roomId, new Date());
|
archiveUrl = matrixPublicArchiveURLCreator.archiveUrlForDate(roomId, archiveDate);
|
||||||
if (interactive) {
|
|
||||||
console.log('Interactive URL for test', archiveUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
const archivePageHtml = await fetchEndpointAsText(archiveUrl);
|
const archivePageHtml = await fetchEndpointAsText(archiveUrl);
|
||||||
|
|
||||||
|
@ -305,7 +309,7 @@ describe('matrix-public-archive', () => {
|
||||||
const imageElement = dom.document.querySelector(`[data-event-id="${imageEventId}"] img`);
|
const imageElement = dom.document.querySelector(`[data-event-id="${imageEventId}"] img`);
|
||||||
assert(imageElement);
|
assert(imageElement);
|
||||||
assert.match(imageElement.getAttribute('src'), new RegExp(`^http://.*`));
|
assert.match(imageElement.getAttribute('src'), new RegExp(`^http://.*`));
|
||||||
assert.strictEqual(imageElement.getAttribute('alt'), 'Friction_between_surfaces.jpeg');
|
assert.strictEqual(imageElement.getAttribute('alt'), imageFileName);
|
||||||
|
|
||||||
// Make sure the normal message is visible
|
// Make sure the normal message is visible
|
||||||
assert.match(
|
assert.match(
|
||||||
|
@ -337,20 +341,6 @@ describe('matrix-public-archive', () => {
|
||||||
replyMessageElement.outerHTML,
|
replyMessageElement.outerHTML,
|
||||||
new RegExp(`.*${escapeStringRegexp(reactionText)}.*`)
|
new RegExp(`.*${escapeStringRegexp(reactionText)}.*`)
|
||||||
);
|
);
|
||||||
} catch (err) {
|
|
||||||
if (err.body) {
|
|
||||||
// FIXME: Remove this try/catch once the matrix-bot-sdk no longer throws
|
|
||||||
// huge response objects as errors, see
|
|
||||||
// https://github.com/turt2live/matrix-bot-sdk/pull/158
|
|
||||||
throw new Error(
|
|
||||||
`Error occured in matrix-bot-sdk (this new error is to stop it from logging the huge response) statusCode=${
|
|
||||||
err.statusCode
|
|
||||||
} body=${JSON.stringify(err.body)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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`);
|
||||||
|
@ -363,3 +353,4 @@ describe('matrix-public-archive', () => {
|
||||||
`will render a room with a sparse amount of messages (a few per day) with no contamination between days`
|
`will render a room with a sparse amount of messages (a few per day) with no contamination between days`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Loading…
Reference in New Issue