diff options
Diffstat (limited to 'test/streaming.ts')
| -rw-r--r-- | test/streaming.ts | 913 |
1 files changed, 807 insertions, 106 deletions
diff --git a/test/streaming.ts b/test/streaming.ts index 500324d520..74a5aaa0b4 100644 --- a/test/streaming.ts +++ b/test/streaming.ts @@ -6,143 +6,844 @@ * * To specify test: * > mocha test/streaming.ts --require ts-node/register -g 'test name' + * + * If the tests not start, try set following enviroment variables: + * TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true + * for more details, please see: https://github.com/TypeStrong/ts-node/issues/754 */ -import * as http from 'http'; -import * as WebSocket from 'ws'; +process.env.NODE_ENV = 'test'; + import * as assert from 'assert'; -import { _signup, _request, _uploadFile, _post, _react, resetDb } from './utils'; +import * as childProcess from 'child_process'; +import { connectStream, signup, request, post } from './utils'; +import { Following } from '../built/models/entities/following'; +const initDb = require('../built/db/postgre.js').initDb; -//#region process -Error.stackTraceLimit = Infinity; +describe('Streaming', () => { + let p: childProcess.ChildProcess; + let Followings: any; -// During the test the env variable is set to test -process.env.NODE_ENV = 'test'; + beforeEach(done => { + p = childProcess.spawn('node', [__dirname + '/../index.js'], { + stdio: ['inherit', 'inherit', 'ipc'], + env: { NODE_ENV: 'test' } + }); + p.on('message', message => { + if (message === 'ok') { + (p.channel as any).onread = () => {}; + initDb(true).then(async connection => { + Followings = connection.getRepository(Following); + done(); + }); + } + }); + }); -// Display detail of unhandled promise rejection -process.on('unhandledRejection', console.dir); -//#endregion + afterEach(() => { + p.kill(); + }); -const app = require('../built/server/api').default; -const server = require('../built/server').startServer(); -const db = require('../built/db/mongodb').default; + const follow = async (follower, followee) => { + 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 + }); + }; -const apiServer = http.createServer(app.callback()); + it('mention event', () => new Promise(async done => { + const alice = await signup({ username: 'alice' }); + const bob = await signup({ username: 'bob' }); -//#region Utilities -const request = _request(apiServer); -const signup = _signup(request); -const post = _post(request); -//#endregion + const ws = await connectStream(bob, 'main', ({ type, body }) => { + if (type == 'mention') { + assert.deepStrictEqual(body.userId, alice.id); + ws.close(); + done(); + } + }); -describe('Streaming', () => { - // Reset database each test - beforeEach(resetDb(db)); + post(alice, { + text: 'foo @bob bar' + }); + })); + + 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' + }); + + const ws = await connectStream(bob, 'main', ({ type, body }) => { + if (type == 'renote') { + assert.deepStrictEqual(body.renoteId, bobNote.id); + ws.close(); + done(); + } + }); + + post(alice, { + renoteId: bobNote.id + }); + })); + + describe('Home Timeline', () => { + it('自分の投稿が流れる', () => new Promise(async done => { + const post = { + text: 'foo' + }; + + const me = await signup(); + + const ws = await connectStream(me, 'homeTimeline', ({ type, body }) => { + if (type == 'note') { + assert.deepStrictEqual(body.text, post.text); + ws.close(); + done(); + } + }); + + 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; + } + }); - after(() => { - server.close(); + // Bob が Carol 宛てのダイレクト投稿 + post(bob, { + text: 'foo', + visibility: 'specified', + visibleUserIds: [carol.id] + }); + + setTimeout(() => { + assert.strictEqual(fired, false); + ws.close(); + done(); + }, 3000); + })); }); - it('投稿がタイムラインに流れる', () => new Promise(async done => { - const post = { - text: 'foo' - }; + 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' + }); + })); - const me = await signup(); - const ws = new WebSocket(`ws://localhost/streaming?i=${me.token}`); + it('フォローしていないローカルユーザーの投稿が流れる', () => new Promise(async done => { + const alice = await signup({ username: 'alice' }); + const bob = await signup({ username: 'bob' }); - ws.on('open', () => { - ws.on('message', data => { - const msg = JSON.parse(data.toString()); - if (msg.type == 'channel' && msg.body.id == 'a') { - if (msg.body.type == 'note') { - assert.deepStrictEqual(msg.body.body.text, post.text); - ws.close(); - done(); - } - } else if (msg.type == 'connected' && msg.body.id == 'a') { - request('/notes/create', post, me); + const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { + if (type == 'note') { + assert.deepStrictEqual(body.userId, bob.id); + ws.close(); + done(); } }); - ws.send(JSON.stringify({ - type: 'connect', - body: { - channel: 'homeTimeline', - id: 'a', - pong: true + 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; } - })); - }); - })); + }); - it('mention event', () => new Promise(async done => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - const aliceNote = { - text: 'foo @bob bar' - }; + post(bob, { + text: 'foo' + }); - const ws = new WebSocket(`ws://localhost/streaming?i=${bob.token}`); + setTimeout(() => { + assert.strictEqual(fired, false); + ws.close(); + done(); + }, 3000); + })); - ws.on('open', () => { - ws.on('message', data => { - const msg = JSON.parse(data.toString()); - if (msg.type == 'channel' && msg.body.id == 'a') { - if (msg.body.type == 'mention') { - assert.deepStrictEqual(msg.body.body.text, aliceNote.text); - ws.close(); - done(); - } - } else if (msg.type == 'connected' && msg.body.id == 'a') { - request('/notes/create', aliceNote, alice); + 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; } }); - ws.send(JSON.stringify({ - type: 'connect', - body: { - channel: 'main', - id: 'a', - pong: 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; } - })); - }); - })); + }); - 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' - }); + // ホーム指定 + 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' }); - const ws = new WebSocket(`ws://localhost/streaming?i=${bob.token}`); + // Alice が Bob をフォロー + await request('/following/create', { + userId: bob.id + }, alice); - ws.on('open', () => { - ws.on('message', data => { - const msg = JSON.parse(data.toString()); - if (msg.type == 'channel' && msg.body.id == 'a') { - if (msg.body.type == 'renote') { - assert.deepStrictEqual(msg.body.body.renoteId, bobNote.id); - ws.close(); - done(); - } - } else if (msg.type == 'connected' && msg.body.id == 'a') { - request('/notes/create', { - renoteId: bobNote.id - }, alice); + const ws = await connectStream(alice, 'localTimeline', ({ type, body }) => { + if (type == 'note') { + assert.deepStrictEqual(body.userId, bob.id); + assert.deepStrictEqual(body.text, 'foo'); + ws.close(); + done(); } }); - ws.send(JSON.stringify({ - type: 'connect', - body: { - channel: 'main', - id: 'a', - pong: true + // 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' }); + + 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('Social Timeline', () => { + it('自分の投稿が流れる', () => new Promise(async done => { + const me = await signup(); + + const ws = await connectStream(me, 'socialTimeline', ({ 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, 'socialTimeline', ({ 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, 'socialTimeline', ({ 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, 'socialTimeline', ({ 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, 'socialTimeline', ({ 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' }); + + let fired = false; + + const ws = await connectStream(alice, 'socialTimeline', ({ 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' + }); + })); + }); + + 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', { + title: '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); + ws.close(); + done(); + } + }, { + listId: list.id + }); + + post(bob, { + text: 'foo' + }); + })); + + it('リストに入れていないユーザーの投稿は流れない', () => new Promise(async done => { + const alice = await signup({ username: 'alice' }); + const bob = await signup({ username: 'bob' }); + + // リスト作成 + const list = await request('/users/lists/create', { + title: '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 + }); + + post(bob, { + text: 'foo' + }); + + setTimeout(() => { + 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', { + title: '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] + }); + })); + + // #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', { + title: '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 + }); + + // フォロワー宛て投稿 + post(bob, { + text: 'foo', + visibility: 'followers' + }); + + setTimeout(() => { + assert.strictEqual(fired, false); + ws.close(); + done(); + }, 3000); + })); + }); + + describe('Hashtag Timeline', () => { + it('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { + const me = await signup(); + + const ws = await connectStream(me, 'hashtag', ({ type, body }) => { + if (type == 'note') { + assert.deepStrictEqual(body.text, '#foo'); + ws.close(); + done(); + } + }, { + q: [ + ['foo'] + ] + }); + + post(me, { + text: '#foo' + }); + })); + + it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { + const me = await signup(); + + let fooCount = 0; + let barCount = 0; + let fooBarCount = 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++; + } + }, { + q: [ + ['foo', 'bar'] + ] + }); + + post(me, { + text: '#foo' + }); + + post(me, { + text: '#bar' + }); + + post(me, { + text: '#foo #bar' + }); + + setTimeout(() => { + assert.strictEqual(fooCount, 0); + assert.strictEqual(barCount, 0); + assert.strictEqual(fooBarCount, 1); + ws.close(); + done(); + }, 3000); + })); + + it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { + const me = await signup(); + + let fooCount = 0; + let barCount = 0; + let fooBarCount = 0; + let piyoCount = 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++; + } + }, { + q: [ + ['foo'], + ['bar'] + ] + }); + + post(me, { + text: '#foo' + }); + + post(me, { + text: '#bar' + }); + + post(me, { + text: '#foo #bar' + }); + + 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); + })); + }); }); |