From 034c5d792b47fbc4dda498c73adbdcac6b50c096 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 3 Jul 2022 20:54:54 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20streaming=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=8A=E3=81=9D=E3=81=84=20(#8912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/test/streaming.ts | 1295 ++++++++++------------------ packages/backend/test/utils.ts | 6 +- 2 files changed, 471 insertions(+), 830 deletions(-) diff --git a/packages/backend/test/streaming.ts b/packages/backend/test/streaming.ts index f080b71dd4..621d07f9c2 100644 --- a/packages/backend/test/streaming.ts +++ b/packages/backend/test/streaming.ts @@ -3,22 +3,12 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; import { Following } from '../src/models/entities/following.js'; -import { connectStream, signup, request, post, startServer, shutdownServer, initTestDb } from './utils.js'; +import { connectStream, signup, api, post, startServer, shutdownServer, initTestDb, waitFire } from './utils.js'; describe('Streaming', () => { let p: childProcess.ChildProcess; let Followings: any; - beforeEach(async () => { - p = await startServer(); - const connection = await initTestDb(true); - Followings = connection.getRepository(Following); - }); - - afterEach(async () => { - await shutdownServer(p); - }); - const follow = async (follower: any, followee: any) => { await Followings.save({ id: 'a', @@ -34,871 +24,522 @@ describe('Streaming', () => { }); }; - it('mention event', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); + describe('Streaming', () => { + // Local users + let ayano: any; + let kyoko: any; + let chitose: any; - const ws = await connectStream(bob, 'main', ({ type, body }) => { - if (type == 'mention') { - assert.deepStrictEqual(body.userId, alice.id); - ws.close(); - done(); - } - }); + // Remote users + let akari: any; + let chinatsu: any; - post(alice, { - text: 'foo @bob bar', - }); - })); + let kyokoNote: any; + let list: any; - it('renote event', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - const bobNote = await post(bob, { - text: 'foo', - }); + before(async () => { + p = await startServer(); + const connection = await initTestDb(true); + Followings = connection.getRepository(Following); - const ws = await connectStream(bob, 'main', ({ type, body }) => { - if (type == 'renote') { - assert.deepStrictEqual(body.renoteId, bobNote.id); - ws.close(); - done(); - } - }); + ayano = await signup({ username: 'ayano' }); + kyoko = await signup({ username: 'kyoko' }); + chitose = await signup({ username: 'chitose' }); - post(alice, { - renoteId: bobNote.id, - }); - })); + akari = await signup({ username: 'akari', host: 'example.com' }); + chinatsu = await signup({ username: 'chinatsu', host: 'example.com' }); - describe('Home Timeline', () => { - it('自分の投稿が流れる', () => new Promise(async done => { - const post = { - text: 'foo', - }; + kyokoNote = await post(kyoko, { text: 'foo' }); - const me = await signup(); + // Follow: ayano => kyoko + await api('following/create', { userId: kyoko.id }, ayano); - const ws = await connectStream(me, 'homeTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.text, post.text); - ws.close(); - done(); - } - }); + // Follow: ayano => akari + await follow(ayano, akari); - request('/notes/create', post, me); - })); - - it('フォローしているユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('フォローしていないユーザーの投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - post(bob, { - text: 'foo', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしているユーザーのダイレクト投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - assert.deepStrictEqual(body.text, 'foo'); - ws.close(); - done(); - } - }); - - // Bob が Alice 宛てのダイレクト投稿 - post(bob, { - text: 'foo', - visibility: 'specified', - visibleUserIds: [alice.id], - }); - })); - - it('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - const carol = await signup({ username: 'carol' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - let fired = false; - - const ws = await connectStream(alice, 'homeTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // Bob が Carol 宛てのダイレクト投稿 - post(bob, { - text: 'foo', - visibility: 'specified', - visibleUserIds: [carol.id], - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - }); - - describe('Local Timeline', () => { - it('自分の投稿が流れる', () => new Promise(async done => { - const me = await signup(); - - const ws = await connectStream(me, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, me.id); - ws.close(); - done(); - } - }); - - post(me, { - text: 'foo', - }); - })); - - it('フォローしていないローカルユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('リモートユーザーの投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob', host: 'example.com' }); - - let fired = false; - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - post(bob, { - text: 'foo', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしてたとしてもリモートユーザーの投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob', host: 'example.com' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - let fired = false; - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - post(bob, { - text: 'foo', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('ホーム指定の投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // ホーム指定 - post(bob, { - text: 'foo', - visibility: 'home', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしているローカルユーザーのダイレクト投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - let fired = false; - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // Bob が Alice 宛てのダイレクト投稿 - post(bob, { - text: 'foo', - visibility: 'specified', - visibleUserIds: [alice.id], - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // フォロワー宛て投稿 - post(bob, { - text: 'foo', - visibility: 'followers', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - }); - - describe('Hybrid Timeline', () => { - it('自分の投稿が流れる', () => new Promise(async done => { - const me = await signup(); - - const ws = await connectStream(me, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, me.id); - ws.close(); - done(); - } - }); - - post(me, { - text: 'foo', - }); - })); - - it('フォローしていないローカルユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('フォローしているリモートユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob', host: 'example.com' }); - - // Alice が Bob をフォロー - await follow(alice, bob); - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('フォローしていないリモートユーザーの投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob', host: 'example.com' }); - - let fired = false; - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - post(bob, { - text: 'foo', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしているユーザーのダイレクト投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - assert.deepStrictEqual(body.text, 'foo'); - ws.close(); - done(); - } - }); - - // Bob が Alice 宛てのダイレクト投稿 - post(bob, { - text: 'foo', - visibility: 'specified', - visibleUserIds: [alice.id], - }); - })); - - it('フォローしているユーザーのホーム投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // Alice が Bob をフォロー - await request('/following/create', { - userId: bob.id, - }, alice); - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - assert.deepStrictEqual(body.text, 'foo'); - ws.close(); - done(); - } - }); - - // ホーム投稿 - post(bob, { - text: 'foo', - visibility: 'home', - }); - })); - - it('フォローしていないローカルユーザーのホーム投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // ホーム投稿 - post(bob, { - text: 'foo', - visibility: 'home', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'hybridTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // フォロワー宛て投稿 - post(bob, { - text: 'foo', - visibility: 'followers', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - }); - - describe('Global Timeline', () => { - it('フォローしていないローカルユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const ws = await connectStream(alice, 'globalTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('フォローしていないリモートユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob', host: 'example.com' }); - - const ws = await connectStream(alice, 'globalTimeline', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }); - - post(bob, { - text: 'foo', - }); - })); - - it('ホーム投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - let fired = false; - - const ws = await connectStream(alice, 'globalTimeline', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }); - - // ホーム投稿 - post(bob, { - text: 'foo', - visibility: 'home', - }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - }); - - describe('UserList Timeline', () => { - it('リストに入れているユーザーの投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // リスト作成 - const list = await request('/users/lists/create', { + // List: chitose => ayano, kyoko + list = await api('users/lists/create', { name: 'my list', - }, alice).then(x => x.body); + }, chitose).then(x => x.body); - // Alice が Bob をリスイン - await request('/users/lists/push', { + await api('users/lists/push', { listId: list.id, - userId: bob.id, - }, alice); + userId: ayano.id, + }, chitose); - const ws = await connectStream(alice, 'userList', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - ws.close(); - done(); - } - }, { + await api('users/lists/push', { listId: list.id, + userId: kyoko.id, + }, chitose); + }); + + after(async () => { + await shutdownServer(p); + }); + + describe('Events', () => { + it('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); }); - post(bob, { - text: 'foo', + it('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); }); - })); + }); - it('リストに入れていないユーザーの投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); + describe('Home Timeline', () => { + it('自分の投稿が流れる', 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' + ); - // リスト作成 - const list = await request('/users/lists/create', { - name: 'my list', - }, alice).then(x => x.body); - - let fired = false; - - const ws = await connectStream(alice, 'userList', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }, { - listId: list.id, + assert.strictEqual(fired, true); }); - post(bob, { - text: 'foo', + it('フォローしているユーザーの投稿が流れる', 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); }); - setTimeout(() => { + it('フォローしていないユーザーの投稿は流れない', 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); - ws.close(); - done(); - }, 3000); - })); - - // #4471 - it('リストに入れているユーザーのダイレクト投稿が流れる', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // リスト作成 - const list = await request('/users/lists/create', { - name: 'my list', - }, alice).then(x => x.body); - - // Alice が Bob をリスイン - await request('/users/lists/push', { - listId: list.id, - userId: bob.id, - }, alice); - - const ws = await connectStream(alice, 'userList', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.userId, bob.id); - assert.deepStrictEqual(body.text, 'foo'); - ws.close(); - done(); - } - }, { - listId: list.id, }); - // Bob が Alice 宛てのダイレクト投稿 - post(bob, { - text: 'foo', - visibility: 'specified', - visibleUserIds: [alice.id], - }); - })); + it('フォローしているユーザーのダイレクト投稿が流れる', 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 + ); - // #4335 - it('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - // リスト作成 - const list = await request('/users/lists/create', { - name: 'my list', - }, alice).then(x => x.body); - - // Alice が Bob をリスイン - await request('/users/lists/push', { - listId: list.id, - userId: bob.id, - }, alice); - - let fired = false; - - const ws = await connectStream(alice, 'userList', ({ type, body }) => { - if (type == 'note') { - fired = true; - } - }, { - listId: list.id, + assert.strictEqual(fired, true); }); - // フォロワー宛て投稿 - post(bob, { - text: 'foo', - visibility: 'followers', - }); + it('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', 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 + ); - setTimeout(() => { assert.strictEqual(fired, false); - ws.close(); - done(); - }, 3000); - })); - }); + }); + }); // Home - describe('Hashtag Timeline', () => { - it('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { - const me = await signup(); + describe('Local Timeline', () => { + it('自分の投稿が流れる', 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' + ); - const ws = await connectStream(me, 'hashtag', ({ type, body }) => { - if (type == 'note') { - assert.deepStrictEqual(body.text, '#foo'); + assert.strictEqual(fired, true); + }); + + it('フォローしていないローカルユーザーの投稿が流れる', 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); + }); + + it('リモートユーザーの投稿は流れない', 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); + }); + + it('フォローしてたとしてもリモートユーザーの投稿は流れない', 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); + }); + + it('ホーム指定の投稿は流れない', 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); + }); + + it('フォローしているローカルユーザーのダイレクト投稿は流れない', 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); + }); + + it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', 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', () => { + it('自分の投稿が流れる', 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); + }); + + it('フォローしていないローカルユーザーの投稿が流れる', 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); + }); + + it('フォローしているリモートユーザーの投稿が流れる', 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); + }); + + it('フォローしていないリモートユーザーの投稿は流れない', 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); + }); + + it('フォローしているユーザーのダイレクト投稿が流れる', 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); + }); + + it('フォローしているユーザーのホーム投稿が流れる', 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); + }); + + it('フォローしていないローカルユーザーのホーム投稿は流れない', 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); + }); + + it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', () => 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', () => { + it('フォローしていないローカルユーザーの投稿が流れる', () => 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); + }); + + it('フォローしていないリモートユーザーの投稿が流れる', () => 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); + }); + + it('ホーム投稿は流れない', () => 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', () => { + it('リストに入れているユーザーの投稿が流れる', () => 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); + }); + + it('リストに入れていないユーザーの投稿は流れない', () => 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 + it('リストに入れているユーザーのダイレクト投稿が流れる', () => 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 + it('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', () => 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', () => { + it('指定したハッシュタグの投稿が流れる', () => new Promise(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', + }); + })); + + it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(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(); - } - }, { - q: [ - ['foo'], - ], - }); + }, 3000); + })); - post(me, { - text: '#foo', - }); - })); + it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { + let fooCount = 0; + let barCount = 0; + let fooBarCount = 0; + let piyoCount = 0; - it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { - const me = await signup(); + 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'], + ], + }); - let fooCount = 0; - let barCount = 0; - let fooBarCount = 0; + post(chitose, { + text: '#foo', + }); - const ws = await connectStream(me, '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: '#bar', + }); - post(me, { - text: '#foo', - }); + post(chitose, { + text: '#foo #bar', + }); - post(me, { - text: '#bar', - }); + post(chitose, { + text: '#piyo', + }); - post(me, { - text: '#foo #bar', - }); + setTimeout(() => { + assert.strictEqual(fooCount, 1); + assert.strictEqual(barCount, 1); + assert.strictEqual(fooBarCount, 1); + assert.strictEqual(piyoCount, 0); + ws.close(); + done(); + }, 3000); + })); - setTimeout(() => { - assert.strictEqual(fooCount, 0); - assert.strictEqual(barCount, 0); - assert.strictEqual(fooBarCount, 1); - ws.close(); - done(); - }, 3000); - })); + it('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { + let fooCount = 0; + let barCount = 0; + let fooBarCount = 0; + let piyoCount = 0; + let waaaCount = 0; - it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { - const me = await signup(); + 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'], + ], + }); - let fooCount = 0; - let barCount = 0; - let fooBarCount = 0; - let piyoCount = 0; + post(chitose, { + text: '#foo', + }); - const ws = await connectStream(me, '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: '#bar', + }); - post(me, { - text: '#foo', - }); + post(chitose, { + text: '#foo #bar', + }); - post(me, { - text: '#bar', - }); + post(chitose, { + text: '#piyo', + }); - post(me, { - text: '#foo #bar', - }); + post(chitose, { + text: '#waaa', + }); - post(me, { - text: '#piyo', - }); - - setTimeout(() => { - assert.strictEqual(fooCount, 1); - assert.strictEqual(barCount, 1); - assert.strictEqual(fooBarCount, 1); - assert.strictEqual(piyoCount, 0); - ws.close(); - done(); - }, 3000); - })); - - it('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { - const me = await signup(); - - let fooCount = 0; - let barCount = 0; - let fooBarCount = 0; - let piyoCount = 0; - let waaaCount = 0; - - const ws = await connectStream(me, '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(me, { - text: '#foo', - }); - - post(me, { - text: '#bar', - }); - - post(me, { - text: '#foo #bar', - }); - - post(me, { - text: '#piyo', - }); - - post(me, { - 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); - })); + 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); + })); + }); }); }); diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 0ee15067d1..245cf858d8 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -186,7 +186,7 @@ export function connectStream(user: any, channel: string, listener: (message: Re }); } -export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record) => boolean) => { +export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record) => boolean, params?: any) => { return new Promise(async (res, rej) => { let timer: NodeJS.Timeout; @@ -198,7 +198,7 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond if (timer) clearTimeout(timer); res(true); } - }); + }, params); } catch (e) { rej(e); } @@ -208,7 +208,7 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond timer = setTimeout(() => { ws.close(); res(false); - }, 5000); + }, 3000); try { await trgr();