From 5ac94cc268270108710fc689a4ada9973267eab0 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 26 Jun 2022 19:16:32 +0900 Subject: [PATCH] fix broken mocha tests v2 see also https://github.com/misskey-dev/misskey/pull/8892 --- packages/backend/.mocharc.json | 2 +- packages/backend/src/server/api/limiter.ts | 2 + packages/backend/test/mute.ts | 42 ++---- packages/backend/test/note.ts | 32 ++--- packages/backend/test/user-notes.ts | 11 +- packages/backend/test/utils.ts | 142 ++++++++++++++++++--- 6 files changed, 157 insertions(+), 74 deletions(-) diff --git a/packages/backend/.mocharc.json b/packages/backend/.mocharc.json index 87c571cfd..f836f9e90 100644 --- a/packages/backend/.mocharc.json +++ b/packages/backend/.mocharc.json @@ -5,6 +5,6 @@ "loader=./test/loader.js" ], "slow": 1000, - "timeout": 10000, + "timeout": 30000, "exit": true } diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts index a39e94f8e..4915c04be 100644 --- a/packages/backend/src/server/api/limiter.ts +++ b/packages/backend/src/server/api/limiter.ts @@ -6,6 +6,8 @@ import { IEndpointMeta } from './endpoints.js'; const logger = new Logger('limiter'); export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) => new Promise((ok, reject) => { + if (process.env.NODE_ENV === 'test') ok(); + const hasShortTermLimit = typeof limitation.minInterval === 'number'; const hasLongTermLimit = diff --git a/packages/backend/test/mute.ts b/packages/backend/test/mute.ts index 2be70f2b6..465633973 100644 --- a/packages/backend/test/mute.ts +++ b/packages/backend/test/mute.ts @@ -2,7 +2,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, react, connectStream, startServer, shutdownServer } from './utils.js'; +import { async, signup, request, post, react, startServer, shutdownServer, waitFire } from './utils.js'; describe('Mute', () => { let p: childProcess.ChildProcess; @@ -55,48 +55,24 @@ describe('Mute', () => { assert.strictEqual(res.body.hasUnreadMentions, false); })); - it('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => { + it('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); - let fired = false; + const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention'); - const ws = await connectStream(alice, 'main', ({ type }) => { - if (type == 'unreadMention') { - fired = true; - } - }); + assert.strictEqual(fired, false); + }); - post(carol, { text: '@alice hi' }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 5000); - })); - - it('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', () => new Promise(async done => { + it('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); await request('/notifications/mark-all-as-read', {}, alice); - let fired = false; + const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification'); - const ws = await connectStream(alice, 'main', ({ type }) => { - if (type == 'unreadNotification') { - fired = true; - } - }); - - post(carol, { text: '@alice hi' }); - - setTimeout(() => { - assert.strictEqual(fired, false); - ws.close(); - done(); - }, 5000); - })); + assert.strictEqual(fired, false); + }); describe('Timeline', () => { it('タイムラインにミュートしているユーザーの投稿が含まれない', async(async () => { diff --git a/packages/backend/test/note.ts b/packages/backend/test/note.ts index 1183e9e4f..b495d8b7b 100644 --- a/packages/backend/test/note.ts +++ b/packages/backend/test/note.ts @@ -3,7 +3,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; import { Note } from '../src/models/entities/note.js'; -import { async, signup, request, post, uploadFile, startServer, shutdownServer, initTestDb } from './utils.js'; +import { async, signup, request, post, uploadUrl, startServer, shutdownServer, initTestDb, api } from './utils.js'; describe('Note', () => { let p: childProcess.ChildProcess; @@ -37,7 +37,7 @@ describe('Note', () => { })); it('ファイルを添付できる', async(async () => { - const file = await uploadFile(alice); + const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { fileIds: [file.id], @@ -49,7 +49,7 @@ describe('Note', () => { })); it('他人のファイルは無視', async(async () => { - const file = await uploadFile(bob); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { text: 'test', @@ -72,11 +72,13 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); })); - it('不正なファイルIDで怒られる', async(async () => { + it('不正なファイルIDは無視', async(async () => { const res = await request('/notes/create', { fileIds: ['kyoppie'], }, alice); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.deepStrictEqual(res.body.createdNote.fileIds, []); })); it('返信できる', async(async () => { @@ -136,7 +138,7 @@ describe('Note', () => { it('文字数ぎりぎりで怒られない', async(async () => { const post = { - text: '!'.repeat(500), + text: '!'.repeat(3000), }; const res = await request('/notes/create', post, alice); assert.strictEqual(res.status, 200); @@ -144,7 +146,7 @@ describe('Note', () => { it('文字数オーバーで怒られる', async(async () => { const post = { - text: '!'.repeat(501), + text: '!'.repeat(3001), }; const res = await request('/notes/create', post, alice); assert.strictEqual(res.status, 400); @@ -207,7 +209,7 @@ describe('Note', () => { assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(res.body.createdNote.text, post.text); - const noteDoc = await Notes.findOne(res.body.createdNote.id); + const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id }); assert.deepStrictEqual(noteDoc.mentions, [bob.id]); })); @@ -336,32 +338,32 @@ describe('Note', () => { describe('notes/delete', () => { it('delete a reply', async(async () => { - const mainNoteRes = await request('/notes/create', { + const mainNoteRes = await api('notes/create', { text: 'main post', }, alice); - const replyOneRes = await request('/notes/create', { + const replyOneRes = await api('notes/create', { text: 'reply one', replyId: mainNoteRes.body.createdNote.id, }, alice); - const replyTwoRes = await request('/notes/create', { + const replyTwoRes = await api('notes/create', { text: 'reply two', replyId: mainNoteRes.body.createdNote.id, }, alice); - const deleteOneRes = await request('/notes/delete', { + const deleteOneRes = await api('notes/delete', { noteId: replyOneRes.body.createdNote.id, }, alice); assert.strictEqual(deleteOneRes.status, 204); - let mainNote = await Notes.findOne({ id: mainNoteRes.body.createdNote.id }); + let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id }); assert.strictEqual(mainNote.repliesCount, 1); - const deleteTwoRes = await request('/notes/delete', { + const deleteTwoRes = await api('notes/delete', { noteId: replyTwoRes.body.createdNote.id, }, alice); assert.strictEqual(deleteTwoRes.status, 204); - mainNote = await Notes.findOne({ id: mainNoteRes.body.createdNote.id }); + mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id }); assert.strictEqual(mainNote.repliesCount, 0); })); }); diff --git a/packages/backend/test/user-notes.ts b/packages/backend/test/user-notes.ts index 5b7933da6..4447754d6 100644 --- a/packages/backend/test/user-notes.ts +++ b/packages/backend/test/user-notes.ts @@ -2,12 +2,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { async, signup, request, post, uploadFile, startServer, shutdownServer } from './utils.js'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); +import { async, signup, request, post, uploadUrl, startServer, shutdownServer } from './utils.js'; describe('users/notes', () => { let p: childProcess.ChildProcess; @@ -20,8 +15,8 @@ describe('users/notes', () => { before(async () => { p = await startServer(); alice = await signup({ username: 'alice' }); - const jpg = await uploadFile(alice, _dirname + '/resources/Lenna.jpg'); - const png = await uploadFile(alice, _dirname + '/resources/Lenna.png'); + const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); + const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png'); jpgNote = await post(alice, { fileIds: [jpg.id], }); diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 5eb4ed3b0..0ee15067d 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -1,16 +1,18 @@ import * as fs from 'node:fs'; +import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import * as childProcess from 'child_process'; import * as http from 'node:http'; import { SIGKILL } from 'constants'; -import * as WebSocket from 'ws'; +import WebSocket from 'ws'; import * as misskey from 'misskey-js'; import fetch from 'node-fetch'; import FormData from 'form-data'; import { DataSource } from 'typeorm'; import loadConfig from '../src/config/load.js'; import { entities } from '../src/db/postgre.js'; +import got from 'got'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -26,6 +28,42 @@ export const async = (fn: Function) => (done: Function) => { }); }; +export const api = async (endpoint: string, params: any, me?: any) => { + endpoint = endpoint.replace(/^\//, ''); + + const auth = me ? { + i: me.token + } : {}; + + const res = await got(`http://localhost:${port}/api/${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(Object.assign(auth, params)), + retry: { + limit: 0, + }, + hooks: { + beforeError: [ + error => { + const { response } = error; + if (response && response.body) console.warn(response.body); + return error; + } + ] + }, + }); + + const status = res.statusCode; + const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null; + + return { + status, + body + }; +}; + export const request = async (endpoint: string, params: any, me?: any): Promise<{ body: any, status: number }> => { const auth = me ? { i: me.token, @@ -53,7 +91,7 @@ export const signup = async (params?: any): Promise => { password: 'test', }, params); - const res = await request('/signup', q); + const res = await api('signup', q); return res.body; }; @@ -63,34 +101,62 @@ export const post = async (user: any, params?: misskey.Endpoints['notes/create'] text: 'test', }, params); - const res = await request('/notes/create', q, user); + const res = await api('notes/create', q, user); return res.body ? res.body.createdNote : null; }; export const react = async (user: any, note: any, reaction: string): Promise => { - await request('/notes/reactions/create', { + await api('notes/reactions/create', { noteId: note.id, reaction: reaction, }, user); }; -export const uploadFile = (user: any, path?: string): Promise => { - const formData = new FormData(); - formData.append('i', user.token); - formData.append('file', fs.createReadStream(path || _dirname + '/resources/Lenna.png')); +/** + * Upload file + * @param user User + * @param _path Optional, absolute path or relative from ./resources/ + */ +export const uploadFile = async (user: any, _path?: string): Promise => { + const absPath = _path == null ? `${_dirname}/resources/Lenna.jpg` : path.isAbsolute(_path) ? _path : `${_dirname}/resources/${_path}`; - return fetch(`http://localhost:${port}/api/drive/files/create`, { - method: 'post', + const formData = new FormData() as any; + formData.append('i', user.token); + formData.append('file', fs.createReadStream(absPath)); + formData.append('force', 'true'); + + const res = await got(`http://localhost:${port}/api/drive/files/create`, { + method: 'POST', body: formData, - timeout: 30 * 1000, - }).then(res => { - if (!res.ok) { - throw `${res.status} ${res.statusText}`; - } else { - return res.json(); + retry: { + limit: 0, + }, + }); + + const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null; + + return body; +}; + +export const uploadUrl = async (user: any, url: string) => { + let file: any; + + const ws = await connectStream(user, 'main', (msg) => { + if (msg.type === 'driveFileCreated') { + file = msg.body; } }); + + await api('drive/files/upload-from-url', { + url, + force: true, + }, user); + + await sleep(5000); + ws.close(); + + return file; }; export function connectStream(user: any, channel: string, listener: (message: Record) => any, params?: any): Promise { @@ -120,6 +186,40 @@ export function connectStream(user: any, channel: string, listener: (message: Re }); } +export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record) => boolean) => { + return new Promise(async (res, rej) => { + let timer: NodeJS.Timeout; + + let ws: WebSocket; + try { + ws = await connectStream(user, channel, msg => { + if (cond(msg)) { + ws.close(); + if (timer) clearTimeout(timer); + res(true); + } + }); + } catch (e) { + rej(e); + } + + if (!ws!) return; + + timer = setTimeout(() => { + ws.close(); + res(false); + }, 5000); + + try { + await trgr(); + } catch (e) { + ws.close(); + if (timer) clearTimeout(timer); + rej(e); + } + }) +}; + export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?: number, type?: string, location?: string }> => { // node-fetchだと3xxを取れない return await new Promise((resolve, reject) => { @@ -176,7 +276,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) { return db; } -export function startServer(timeout = 30 * 1000): Promise { +export function startServer(timeout = 60 * 1000): Promise { return new Promise((res, rej) => { const t = setTimeout(() => { p.kill(SIGKILL); @@ -214,3 +314,11 @@ export function shutdownServer(p: childProcess.ChildProcess, timeout = 20 * 1000 p.kill(); }); } + +export function sleep(msec: number) { + return new Promise(res => { + setTimeout(() => { + res(); + }, msec); + }); +}