summaryrefslogtreecommitdiff
path: root/packages/backend/test/e2e/streaming.ts
diff options
context:
space:
mode:
authorKagami Sascha Rosylight <saschanaz@outlook.com>2023-03-03 03:13:12 +0100
committerGitHub <noreply@github.com>2023-03-03 11:13:12 +0900
commit61215e50ff9e4c84787c8d99c75fd36dafbd8815 (patch)
tree36419e8a3ec97afa0a3a0011d523d80addf8e724 /packages/backend/test/e2e/streaming.ts
parentfix(server): チャンネルでミュートが正しく機能していない... (diff)
downloadmisskey-61215e50ff9e4c84787c8d99c75fd36dafbd8815.tar.gz
misskey-61215e50ff9e4c84787c8d99c75fd36dafbd8815.tar.bz2
misskey-61215e50ff9e4c84787c8d99c75fd36dafbd8815.zip
test(backend): APIテストの復活 (#10163)
* Revert 1c5291f8185651c231903129ee7c1cee263f9f03 * APIテストの復活 * apiテストの移行 * moduleNameMapper修正 * simpleGetでthrowしないように status確認しているので要らない * longer timeout * ローカルでは問題ないのになんで * case sensitive * Run Nest instance within the current process * Skip some setIntervals * wait for 5 seconds * kill them all!! * logHeapUsage: true * detectOpenHandlesがじゃましているらしい * maxWorkers=1? * restore drive api tests * workerIdleMemoryLimit: 500MB * 1024MiB * Wait what
Diffstat (limited to 'packages/backend/test/e2e/streaming.ts')
-rw-r--r--packages/backend/test/e2e/streaming.ts547
1 files changed, 547 insertions, 0 deletions
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
new file mode 100644
index 0000000000..23c431f2e7
--- /dev/null
+++ b/packages/backend/test/e2e/streaming.ts
@@ -0,0 +1,547 @@
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import { Following } from '@/models/entities/Following.js';
+import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('Streaming', () => {
+ let p: INestApplicationContext;
+ let Followings: any;
+
+ const follow = async (follower: any, followee: any) => {
+ await Followings.save({
+ id: 'a',
+ createdAt: new Date(),
+ followerId: follower.id,
+ followeeId: followee.id,
+ followerHost: follower.host,
+ followerInbox: null,
+ followerSharedInbox: null,
+ followeeHost: followee.host,
+ followeeInbox: null,
+ followeeSharedInbox: null,
+ });
+ };
+
+ describe('Streaming', () => {
+ // Local users
+ let ayano: any;
+ let kyoko: any;
+ let chitose: any;
+
+ // Remote users
+ let akari: any;
+ let chinatsu: any;
+
+ let kyokoNote: any;
+ let list: any;
+
+ beforeAll(async () => {
+ p = await startServer();
+ const connection = await initTestDb(true);
+ Followings = connection.getRepository(Following);
+
+ ayano = await signup({ username: 'ayano' });
+ kyoko = await signup({ username: 'kyoko' });
+ chitose = await signup({ username: 'chitose' });
+
+ akari = await signup({ username: 'akari', host: 'example.com' });
+ chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
+
+ kyokoNote = await post(kyoko, { text: 'foo' });
+
+ // Follow: ayano => kyoko
+ await api('following/create', { userId: kyoko.id }, ayano);
+
+ // Follow: ayano => akari
+ await follow(ayano, akari);
+
+ // List: chitose => ayano, kyoko
+ list = await api('users/lists/create', {
+ name: 'my list',
+ }, chitose).then(x => x.body);
+
+ await api('users/lists/push', {
+ listId: list.id,
+ userId: ayano.id,
+ }, chitose);
+
+ await api('users/lists/push', {
+ listId: list.id,
+ userId: kyoko.id,
+ }, chitose);
+ }, 1000 * 60 * 2);
+
+ afterAll(async () => {
+ await p.close();
+ });
+
+ describe('Events', () => {
+ test('mention event', async () => {
+ const fired = await waitFire(
+ kyoko, 'main', // kyoko:main
+ () => post(ayano, { text: 'foo @kyoko bar' }), // ayano mention => kyoko
+ msg => msg.type === 'mention' && msg.body.userId === ayano.id, // wait ayano
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('renote event', async () => {
+ const fired = await waitFire(
+ kyoko, 'main', // kyoko:main
+ () => post(ayano, { renoteId: kyokoNote.id }), // ayano renote
+ msg => msg.type === 'renote' && msg.body.renoteId === kyokoNote.id, // wait renote
+ );
+
+ assert.strictEqual(fired, true);
+ });
+ });
+
+ describe('Home Timeline', () => {
+ test('自分の投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:Home
+ () => api('notes/create', { text: 'foo' }, ayano), // ayano posts
+ msg => msg.type === 'note' && msg.body.text === 'foo',
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしているユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:home
+ () => api('notes/create', { text: 'foo' }, kyoko), // kyoko posts
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないユーザーの投稿は流れない', async () => {
+ const fired = await waitFire(
+ kyoko, 'homeTimeline', // kyoko:home
+ () => api('notes/create', { text: 'foo' }, ayano), // ayano posts
+ msg => msg.type === 'note' && msg.body.userId === ayano.id, // wait ayano
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしているユーザーのダイレクト投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:home
+ () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko dm => ayano
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:home
+ () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), // kyoko dm => chitose
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, false);
+ });
+ }); // Home
+
+ describe('Local Timeline', () => {
+ test('自分の投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo' }, ayano), // ayano posts
+ msg => msg.type === 'note' && msg.body.text === 'foo',
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないローカルユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo' }, chitose), // chitose posts
+ msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('リモートユーザーの投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
+ msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo' }, akari), // akari posts
+ msg => msg.type === 'note' && msg.body.userId === akari.id, // wait akari
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('ホーム指定の投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko home posts
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko DM => ayano
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'localTimeline', // ayano:Local
+ () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose),
+ msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
+ );
+
+ assert.strictEqual(fired, false);
+ });
+ });
+
+ describe('Hybrid Timeline', () => {
+ test('自分の投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo' }, ayano), // ayano posts
+ msg => msg.type === 'note' && msg.body.text === 'foo',
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないローカルユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo' }, chitose), // chitose posts
+ msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしているリモートユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo' }, akari), // akari posts
+ msg => msg.type === 'note' && msg.body.userId === akari.id, // wait akari
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないリモートユーザーの投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
+ msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしているユーザーのダイレクト投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko),
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしているユーザーのホーム投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko),
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないローカルユーザーのホーム投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo', visibility: 'home' }, chitose),
+ msg => msg.type === 'note' && msg.body.userId === chitose.id,
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'hybridTimeline', // ayano:Hybrid
+ () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose),
+ msg => msg.type === 'note' && msg.body.userId === chitose.id,
+ );
+
+ assert.strictEqual(fired, false);
+ });
+ });
+
+ describe('Global Timeline', () => {
+ test('フォローしていないローカルユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'globalTimeline', // ayano:Global
+ () => api('notes/create', { text: 'foo' }, chitose), // chitose posts
+ msg => msg.type === 'note' && msg.body.userId === chitose.id, // wait chitose
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('フォローしていないリモートユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ ayano, 'globalTimeline', // ayano:Global
+ () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
+ msg => msg.type === 'note' && msg.body.userId === chinatsu.id, // wait chinatsu
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('ホーム投稿は流れない', async () => {
+ const fired = await waitFire(
+ ayano, 'globalTimeline', // ayano:Global
+ () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko posts
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
+ );
+
+ assert.strictEqual(fired, false);
+ });
+ });
+
+ describe('UserList Timeline', () => {
+ test('リストに入れているユーザーの投稿が流れる', async () => {
+ const fired = await waitFire(
+ chitose, 'userList',
+ () => api('notes/create', { text: 'foo' }, ayano),
+ msg => msg.type === 'note' && msg.body.userId === ayano.id,
+ { listId: list.id },
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('リストに入れていないユーザーの投稿は流れない', async () => {
+ const fired = await waitFire(
+ chitose, 'userList',
+ () => api('notes/create', { text: 'foo' }, chinatsu),
+ msg => msg.type === 'note' && msg.body.userId === chinatsu.id,
+ { listId: list.id },
+ );
+
+ assert.strictEqual(fired, false);
+ });
+
+ // #4471
+ test('リストに入れているユーザーのダイレクト投稿が流れる', async () => {
+ const fired = await waitFire(
+ chitose, 'userList',
+ () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, ayano),
+ msg => msg.type === 'note' && msg.body.userId === ayano.id,
+ { listId: list.id },
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ // #4335
+ test('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => {
+ const fired = await waitFire(
+ chitose, 'userList',
+ () => api('notes/create', { text: 'foo', visibility: 'followers' }, kyoko),
+ msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+ { listId: list.id },
+ );
+
+ assert.strictEqual(fired, false);
+ });
+ });
+
+ describe('Hashtag Timeline', () => {
+ test('指定したハッシュタグの投稿が流れる', () => new Promise<void>(async done => {
+ const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
+ if (type === 'note') {
+ assert.deepStrictEqual(body.text, '#foo');
+ ws.close();
+ done();
+ }
+ }, {
+ q: [
+ ['foo'],
+ ],
+ });
+
+ post(chitose, {
+ text: '#foo',
+ });
+ }));
+
+ // XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"
+
+ // test('指定したハッシュタグの投稿が流れる (AND)', () => new Promise<void>(async done => {
+ // let fooCount = 0;
+ // let barCount = 0;
+ // let fooBarCount = 0;
+
+ // const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
+ // if (type === 'note') {
+ // if (body.text === '#foo') fooCount++;
+ // if (body.text === '#bar') barCount++;
+ // if (body.text === '#foo #bar') fooBarCount++;
+ // }
+ // }, {
+ // q: [
+ // ['foo', 'bar'],
+ // ],
+ // });
+
+ // post(chitose, {
+ // text: '#foo',
+ // });
+
+ // post(chitose, {
+ // text: '#bar',
+ // });
+
+ // post(chitose, {
+ // text: '#foo #bar',
+ // });
+
+ // setTimeout(() => {
+ // assert.strictEqual(fooCount, 0);
+ // assert.strictEqual(barCount, 0);
+ // assert.strictEqual(fooBarCount, 1);
+ // ws.close();
+ // done();
+ // }, 3000);
+ // }));
+
+ test('指定したハッシュタグの投稿が流れる (OR)', () => new Promise<void>(async done => {
+ let fooCount = 0;
+ let barCount = 0;
+ let fooBarCount = 0;
+ let piyoCount = 0;
+
+ const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
+ if (type === 'note') {
+ if (body.text === '#foo') fooCount++;
+ if (body.text === '#bar') barCount++;
+ if (body.text === '#foo #bar') fooBarCount++;
+ if (body.text === '#piyo') piyoCount++;
+ }
+ }, {
+ q: [
+ ['foo'],
+ ['bar'],
+ ],
+ });
+
+ post(chitose, {
+ text: '#foo',
+ });
+
+ post(chitose, {
+ text: '#bar',
+ });
+
+ post(chitose, {
+ text: '#foo #bar',
+ });
+
+ post(chitose, {
+ text: '#piyo',
+ });
+
+ setTimeout(() => {
+ assert.strictEqual(fooCount, 1);
+ assert.strictEqual(barCount, 1);
+ assert.strictEqual(fooBarCount, 1);
+ assert.strictEqual(piyoCount, 0);
+ ws.close();
+ done();
+ }, 3000);
+ }));
+
+ test('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise<void>(async done => {
+ let fooCount = 0;
+ let barCount = 0;
+ let fooBarCount = 0;
+ let piyoCount = 0;
+ let waaaCount = 0;
+
+ const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => {
+ if (type === 'note') {
+ if (body.text === '#foo') fooCount++;
+ if (body.text === '#bar') barCount++;
+ if (body.text === '#foo #bar') fooBarCount++;
+ if (body.text === '#piyo') piyoCount++;
+ if (body.text === '#waaa') waaaCount++;
+ }
+ }, {
+ q: [
+ ['foo', 'bar'],
+ ['piyo'],
+ ],
+ });
+
+ post(chitose, {
+ text: '#foo',
+ });
+
+ post(chitose, {
+ text: '#bar',
+ });
+
+ post(chitose, {
+ text: '#foo #bar',
+ });
+
+ post(chitose, {
+ text: '#piyo',
+ });
+
+ post(chitose, {
+ text: '#waaa',
+ });
+
+ setTimeout(() => {
+ assert.strictEqual(fooCount, 0);
+ assert.strictEqual(barCount, 0);
+ assert.strictEqual(fooBarCount, 1);
+ assert.strictEqual(piyoCount, 1);
+ assert.strictEqual(waaaCount, 0);
+ ws.close();
+ done();
+ }, 3000);
+ }));
+ });
+ });
+});