summaryrefslogtreecommitdiff
path: root/packages/backend/test/e2e
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2024-08-02 12:25:58 +0100
committerdakkar <dakkar@thenautilus.net>2024-08-02 12:25:58 +0100
commitcfa9b852df9e0293865b3acbd67d59265962e552 (patch)
treea408ad670956a45c4e162e4ecc97a3624e2b0f20 /packages/backend/test/e2e
parentmerge: rate limit all password checks - fixes #540 (!568) (diff)
parentMerge pull request #14233 from misskey-dev/develop (diff)
downloadsharkey-cfa9b852df9e0293865b3acbd67d59265962e552.tar.gz
sharkey-cfa9b852df9e0293865b3acbd67d59265962e552.tar.bz2
sharkey-cfa9b852df9e0293865b3acbd67d59265962e552.zip
Merge remote-tracking branch 'misskey/master' into feature/misskey-2024.07
Diffstat (limited to 'packages/backend/test/e2e')
-rw-r--r--packages/backend/test/e2e/2fa.ts30
-rw-r--r--packages/backend/test/e2e/antennas.ts3
-rw-r--r--packages/backend/test/e2e/api-visibility.ts16
-rw-r--r--packages/backend/test/e2e/block.ts12
-rw-r--r--packages/backend/test/e2e/clips.ts19
-rw-r--r--packages/backend/test/e2e/drive.ts6
-rw-r--r--packages/backend/test/e2e/endpoints.ts91
-rw-r--r--packages/backend/test/e2e/exports.ts70
-rw-r--r--packages/backend/test/e2e/fetch-resource.ts17
-rw-r--r--packages/backend/test/e2e/move.ts77
-rw-r--r--packages/backend/test/e2e/mute.ts72
-rw-r--r--packages/backend/test/e2e/note.ts79
-rw-r--r--packages/backend/test/e2e/renote-mute.ts25
-rw-r--r--packages/backend/test/e2e/reversi-game.ts33
-rw-r--r--packages/backend/test/e2e/streaming.ts62
-rw-r--r--packages/backend/test/e2e/synalio/abuse-report.ts360
-rw-r--r--packages/backend/test/e2e/synalio/user-create.ts130
-rw-r--r--packages/backend/test/e2e/thread-mute.ts10
-rw-r--r--packages/backend/test/e2e/timelines.ts533
-rw-r--r--packages/backend/test/e2e/user-notes.ts4
-rw-r--r--packages/backend/test/e2e/users.ts13
21 files changed, 1281 insertions, 381 deletions
diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index 13c56b88a6..06548fa7da 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -206,7 +206,7 @@ describe('2要素認証', () => {
username,
}, alice);
assert.strictEqual(usersShowResponse.status, 200);
- assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
+ assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
const signinResponse = await api('signin', {
...signinParam(),
@@ -248,7 +248,7 @@ describe('2要素認証', () => {
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
- }) as any, alice);
+ } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200);
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
assert.strictEqual(keyDoneResponse.body.name, keyName);
@@ -257,22 +257,22 @@ describe('2要素認証', () => {
username,
});
assert.strictEqual(usersShowResponse.status, 200);
- assert.strictEqual(usersShowResponse.body.securityKeys, true);
+ assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, true);
const signinResponse = await api('signin', {
...signinParam(),
});
assert.strictEqual(signinResponse.status, 200);
assert.strictEqual(signinResponse.body.i, undefined);
- assert.notEqual(signinResponse.body.challenge, undefined);
- assert.notEqual(signinResponse.body.allowCredentials, undefined);
- assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
+ assert.notEqual((signinResponse.body as unknown as { challenge: unknown | undefined }).challenge, undefined);
+ assert.notEqual((signinResponse.body as unknown as { allowCredentials: unknown | undefined }).allowCredentials, undefined);
+ assert.strictEqual((signinResponse.body as unknown as { allowCredentials: {id: string}[] }).allowCredentials[0].id, credentialId.toString('base64url'));
const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
keyName,
credentialId,
requestOptions: signinResponse.body,
- }));
+ } as any));
assert.strictEqual(signinResponse2.status, 200);
assert.notEqual(signinResponse2.body.i, undefined);
@@ -307,7 +307,7 @@ describe('2要素認証', () => {
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
- }) as any, alice);
+ } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200);
const passwordLessResponse = await api('i/2fa/password-less', {
@@ -319,7 +319,7 @@ describe('2要素認証', () => {
username,
});
assert.strictEqual(usersShowResponse.status, 200);
- assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true);
+ assert.strictEqual((usersShowResponse.body as unknown as { usePasswordLessLogin: boolean }).usePasswordLessLogin, true);
const signinResponse = await api('signin', {
...signinParam(),
@@ -333,7 +333,7 @@ describe('2要素認証', () => {
keyName,
credentialId,
requestOptions: signinResponse.body,
- }),
+ } as any),
password: '',
});
assert.strictEqual(signinResponse2.status, 200);
@@ -370,7 +370,7 @@ describe('2要素認証', () => {
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
- }) as any, alice);
+ } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200);
const renamedKey = 'other-key';
@@ -383,6 +383,7 @@ describe('2要素認証', () => {
const iResponse = await api('i', {
}, alice);
assert.strictEqual(iResponse.status, 200);
+ assert.ok(iResponse.body.securityKeysList);
const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
assert.strictEqual(securityKeys.length, 1);
assert.strictEqual(securityKeys[0].name, renamedKey);
@@ -419,13 +420,14 @@ describe('2要素認証', () => {
keyName,
credentialId,
creationOptions: registerKeyResponse.body,
- }) as any, alice);
+ } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200);
// テストの実行順によっては複数残ってるので全部消す
const iResponse = await api('i', {
}, alice);
assert.strictEqual(iResponse.status, 200);
+ assert.ok(iResponse.body.securityKeysList);
for (const key of iResponse.body.securityKeysList) {
const removeKeyResponse = await api('i/2fa/remove-key', {
token: otpToken(registerResponse.body.secret),
@@ -439,7 +441,7 @@ describe('2要素認証', () => {
username,
});
assert.strictEqual(usersShowResponse.status, 200);
- assert.strictEqual(usersShowResponse.body.securityKeys, false);
+ assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, false);
const signinResponse = await api('signin', {
...signinParam(),
@@ -470,7 +472,7 @@ describe('2要素認証', () => {
username,
});
assert.strictEqual(usersShowResponse.status, 200);
- assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
+ assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
const unregisterResponse = await api('i/2fa/unregister', {
token: otpToken(registerResponse.body.secret),
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 101238b601..6ac14cd8dc 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -163,8 +163,7 @@ describe('アンテナ', () => {
});
test('が上限いっぱいまで作成できること', async () => {
- // antennaLimit + 1まで作れるのがキモ
- const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit + 1)].map(() => successfulApiCall({
+ const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit)].map(() => successfulApiCall({
endpoint: 'antennas/create',
parameters: { ...defaultParam },
user: alice,
diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts
index c61b0c2a86..2dd645d97a 100644
--- a/packages/backend/test/e2e/api-visibility.ts
+++ b/packages/backend/test/e2e/api-visibility.ts
@@ -410,21 +410,21 @@ describe('API visibility', () => {
test('[HTL] public-post が 自分が見れる', async () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === pub.id);
+ const notes = res.body.filter(n => n.id === pub.id);
assert.strictEqual(notes[0].text, 'x');
});
test('[HTL] public-post が 非フォロワーから見れない', async () => {
const res = await api('notes/timeline', { limit: 100 }, other);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === pub.id);
+ const notes = res.body.filter(n => n.id === pub.id);
assert.strictEqual(notes.length, 0);
});
test('[HTL] followers-post が フォロワーから見れる', async () => {
const res = await api('notes/timeline', { limit: 100 }, follower);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === fol.id);
+ const notes = res.body.filter(n => n.id === fol.id);
assert.strictEqual(notes[0].text, 'x');
});
//#endregion
@@ -433,21 +433,21 @@ describe('API visibility', () => {
test('[replies] followers-reply が フォロワーから見れる', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === folR.id);
+ const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === folR.id);
+ const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes.length, 0);
});
test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === folR.id);
+ const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
//#endregion
@@ -456,14 +456,14 @@ describe('API visibility', () => {
test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await api('notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === folR.id);
+ const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x');
});
test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
const res = await api('notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200);
- const notes = res.body.filter((n: any) => n.id === folM.id);
+ const notes = res.body.filter(n => n.id === folM.id);
assert.strictEqual(notes[0].text, '@target x');
});
//#endregion
diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts
index e4f798498f..35b0e59383 100644
--- a/packages/backend/test/e2e/block.ts
+++ b/packages/backend/test/e2e/block.ts
@@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
-import { api, post, signup } from '../utils.js';
+import { api, castAsError, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Block', () => {
@@ -33,7 +33,7 @@ describe('Block', () => {
const res = await api('following/create', { userId: alice.id }, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
+ assert.strictEqual(castAsError(res.body).error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
});
test('ブロックされているユーザーにリアクションできない', async () => {
@@ -42,7 +42,8 @@ describe('Block', () => {
const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
});
test('ブロックされているユーザーに返信できない', async () => {
@@ -51,7 +52,8 @@ describe('Block', () => {
const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
});
test('ブロックされているユーザーのノートをRenoteできない', async () => {
@@ -60,7 +62,7 @@ describe('Block', () => {
const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
+ assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
});
// TODO: ユーザーリストに入れられないテスト
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index ba6f9d6a65..a130c3698d 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -79,14 +79,14 @@ describe('クリップ', () => {
};
const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => {
- return await successfulApiCall({
+ await successfulApiCall({
endpoint: 'clips/delete',
parameters,
user: alice,
...request,
}, {
status: 204,
- }) as any as void;
+ });
};
const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => {
@@ -153,8 +153,7 @@ describe('クリップ', () => {
});
test('の作成はポリシーで定められた数以上はできない。', async () => {
- // ポリシー + 1まで作れるという所がミソ
- const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
+ const clipLimit = DEFAULT_POLICIES.clipLimit;
for (let i = 0; i < clipLimit; i++) {
await create();
}
@@ -327,7 +326,7 @@ describe('クリップ', () => {
});
test('の一覧(clips/list)が取得できる(上限いっぱい)', async () => {
- const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
+ const clipLimit = DEFAULT_POLICIES.clipLimit;
const clips = await createMany({}, clipLimit);
const res = await list({
parameters: { limit: 1 }, // FIXME: 無視されて11全部返ってくる
@@ -455,25 +454,25 @@ describe('クリップ', () => {
let aliceClip: Misskey.entities.Clip;
const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => {
- return successfulApiCall({
+ await successfulApiCall({
endpoint: 'clips/favorite',
parameters,
user: alice,
...request,
}, {
status: 204,
- }) as any as void;
+ });
};
const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => {
- return successfulApiCall({
+ await successfulApiCall({
endpoint: 'clips/unfavorite',
parameters,
user: alice,
...request,
}, {
status: 204,
- }) as any as void;
+ });
};
const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
@@ -705,7 +704,7 @@ describe('クリップ', () => {
// TODO: 17000msくらいかかる...
test('をポリシーで定められた上限いっぱい(200)を超えて追加はできない。', async () => {
- const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
+ const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit;
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
text: `test ${i}`,
}) as unknown)) as Misskey.entities.Note[];
diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts
index 828c5200ef..43a73163eb 100644
--- a/packages/backend/test/e2e/drive.ts
+++ b/packages/backend/test/e2e/drive.ts
@@ -23,7 +23,7 @@ describe('Drive', () => {
const marker = Math.random().toString();
- const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg';
+ const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg';
const catcher = makeStreamCatcher(
alice,
@@ -41,14 +41,14 @@ describe('Drive', () => {
const file = await catcher;
assert.strictEqual(res.status, 204);
- assert.strictEqual(file.name, 'Lenna.jpg');
+ assert.strictEqual(file.name, '192.jpg');
assert.strictEqual(file.type, 'image/jpeg');
});
test('ローカルからアップロードできる', async () => {
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
- const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
+ const res = await uploadFile(alice, { path: '192.jpg', name: 'テスト画像' });
assert.strictEqual(res.body?.name, 'テスト画像.jpg');
assert.strictEqual(res.body.type, 'image/jpeg');
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index bc89dc37f4..5aaec7f6f9 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -10,7 +10,7 @@ import * as assert from 'assert';
// https://github.com/node-fetch/node-fetch/pull/1664
import { Blob } from 'node-fetch';
import { MiUser } from '@/models/_.js';
-import { api, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
+import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Endpoints', () => {
@@ -117,12 +117,21 @@ describe('Endpoints', () => {
assert.strictEqual(res.body.birthday, myBirthday);
});
- test('名前を空白にできる', async () => {
+ test('名前を空白のみにした場合nullになる', async () => {
const res = await api('i/update', {
name: ' ',
}, alice);
assert.strictEqual(res.status, 200);
- assert.strictEqual(res.body.name, ' ');
+ assert.strictEqual(res.body.name, null);
+ });
+
+ test('名前の前後に空白(ホワイトスペース)を入れてもトリムされる', async () => {
+ const res = await api('i/update', {
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#white_space
+ name: ' あ い う \u0009\u000b\u000c\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff',
+ }, alice);
+ assert.strictEqual(res.status, 200);
+ assert.strictEqual(res.body.name, 'あ い う');
});
test('誕生日の設定を削除できる', async () => {
@@ -155,7 +164,7 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
- assert.strictEqual(res.body.id, alice.id);
+ assert.strictEqual((res.body as unknown as { id: string }).id, alice.id);
});
test('ユーザーが存在しなかったら怒る', async () => {
@@ -266,6 +275,68 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 400);
});
+ test('リノートにリアクションできない', async () => {
+ const bobNote = await post(bob, { text: 'hi' });
+ const bobRenote = await post(bob, { renoteId: bobNote.id });
+
+ const res = await api('notes/reactions/create', {
+ noteId: bobRenote.id,
+ reaction: '🚀',
+ }, alice);
+
+ assert.strictEqual(res.status, 400);
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.code, 'CANNOT_REACT_TO_RENOTE');
+ });
+
+ test('引用にリアクションできる', async () => {
+ const bobNote = await post(bob, { text: 'hi' });
+ const bobRenote = await post(bob, { text: 'hi again', renoteId: bobNote.id });
+
+ const res = await api('notes/reactions/create', {
+ noteId: bobRenote.id,
+ reaction: '🚀',
+ }, alice);
+
+ assert.strictEqual(res.status, 204);
+ });
+
+ test('空文字列のリアクションは\u2764にフォールバックされる', async () => {
+ const bobNote = await post(bob, { text: 'hi' });
+
+ const res = await api('notes/reactions/create', {
+ noteId: bobNote.id,
+ reaction: '',
+ }, alice);
+
+ assert.strictEqual(res.status, 204);
+
+ const reaction = await api('notes/reactions', {
+ noteId: bobNote.id,
+ });
+
+ assert.strictEqual(reaction.body.length, 1);
+ assert.strictEqual(reaction.body[0].type, '\u2764');
+ });
+
+ test('絵文字ではない文字列のリアクションは\u2764にフォールバックされる', async () => {
+ const bobNote = await post(bob, { text: 'hi' });
+
+ const res = await api('notes/reactions/create', {
+ noteId: bobNote.id,
+ reaction: 'Hello!',
+ }, alice);
+
+ assert.strictEqual(res.status, 204);
+
+ const reaction = await api('notes/reactions', {
+ noteId: bobNote.id,
+ });
+
+ assert.strictEqual(reaction.body.length, 1);
+ assert.strictEqual(reaction.body[0].type, '\u2764');
+ });
+
test('空のパラメータで怒られる', async () => {
// @ts-expect-error param must not be empty
const res = await api('notes/reactions/create', {}, alice);
@@ -523,7 +594,7 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
- assert.strictEqual(res.body!.name, 'Lenna.jpg');
+ assert.strictEqual(res.body!.name, '192.jpg');
});
test('ファイルに名前を付けられる', async () => {
@@ -993,7 +1064,7 @@ describe('Endpoints', () => {
userId: bob.id,
}, alice);
assert.strictEqual(res1.status, 204);
- assert.strictEqual(res2.body?.memo, memo);
+ assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
});
test('自分に関するメモを更新できる', async () => {
@@ -1008,7 +1079,7 @@ describe('Endpoints', () => {
userId: alice.id,
}, alice);
assert.strictEqual(res1.status, 204);
- assert.strictEqual(res2.body?.memo, memo);
+ assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
});
test('メモを削除できる', async () => {
@@ -1029,7 +1100,7 @@ describe('Endpoints', () => {
}, alice);
// memoには常に文字列かnullが入っている(5cac151)
- assert.strictEqual(res.body.memo, null);
+ assert.strictEqual((res.body as unknown as { memo: string | null }).memo, null);
});
test('メモは個人ごとに独立して保存される', async () => {
@@ -1056,8 +1127,8 @@ describe('Endpoints', () => {
}, carol),
]);
- assert.strictEqual(resAlice.body.memo, memoAliceToBob);
- assert.strictEqual(resCarol.body.memo, memoCarolToBob);
+ assert.strictEqual((resAlice.body as unknown as { memo: string }).memo, memoAliceToBob);
+ assert.strictEqual((resCarol.body as unknown as { memo: string }).memo, memoCarolToBob);
});
});
});
diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts
index 80a5331a6d..4bcecc9716 100644
--- a/packages/backend/test/e2e/exports.ts
+++ b/packages/backend/test/e2e/exports.ts
@@ -61,14 +61,14 @@ describe('export-clips', () => {
});
test('basic export', async () => {
- let res = await api('clips/create', {
+ const res1 = await api('clips/create', {
name: 'foo',
description: 'bar',
}, alice);
- assert.strictEqual(res.status, 200);
+ assert.strictEqual(res1.status, 200);
- res = await api('i/export-clips', {}, alice);
- assert.strictEqual(res.status, 204);
+ const res2 = await api('i/export-clips', {}, alice);
+ assert.strictEqual(res2.status, 204);
const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'foo');
@@ -77,7 +77,7 @@ describe('export-clips', () => {
});
test('export with notes', async () => {
- let res = await api('clips/create', {
+ const res = await api('clips/create', {
name: 'foo',
description: 'bar',
}, alice);
@@ -96,15 +96,15 @@ describe('export-clips', () => {
});
for (const note of [note1, note2]) {
- res = await api('clips/add-note', {
+ const res2 = await api('clips/add-note', {
clipId: clip.id,
noteId: note.id,
}, alice);
- assert.strictEqual(res.status, 204);
+ assert.strictEqual(res2.status, 204);
}
- res = await api('i/export-clips', {}, alice);
- assert.strictEqual(res.status, 204);
+ const res3 = await api('i/export-clips', {}, alice);
+ assert.strictEqual(res3.status, 204);
const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'foo');
@@ -116,19 +116,19 @@ describe('export-clips', () => {
});
test('multiple clips', async () => {
- let res = await api('clips/create', {
+ const res1 = await api('clips/create', {
name: 'kawaii',
description: 'kawaii',
}, alice);
- assert.strictEqual(res.status, 200);
- const clip1 = res.body;
+ assert.strictEqual(res1.status, 200);
+ const clip1 = res1.body;
- res = await api('clips/create', {
+ const res2 = await api('clips/create', {
name: 'yuri',
description: 'yuri',
}, alice);
- assert.strictEqual(res.status, 200);
- const clip2 = res.body;
+ assert.strictEqual(res2.status, 200);
+ const clip2 = res2.body;
const note1 = await post(alice, {
text: 'baz1',
@@ -138,20 +138,26 @@ describe('export-clips', () => {
text: 'baz2',
});
- res = await api('clips/add-note', {
- clipId: clip1.id,
- noteId: note1.id,
- }, alice);
- assert.strictEqual(res.status, 204);
+ {
+ const res = await api('clips/add-note', {
+ clipId: clip1.id,
+ noteId: note1.id,
+ }, alice);
+ assert.strictEqual(res.status, 204);
+ }
- res = await api('clips/add-note', {
- clipId: clip2.id,
- noteId: note2.id,
- }, alice);
- assert.strictEqual(res.status, 204);
+ {
+ const res = await api('clips/add-note', {
+ clipId: clip2.id,
+ noteId: note2.id,
+ }, alice);
+ assert.strictEqual(res.status, 204);
+ }
- res = await api('i/export-clips', {}, alice);
- assert.strictEqual(res.status, 204);
+ {
+ const res = await api('i/export-clips', {}, alice);
+ assert.strictEqual(res.status, 204);
+ }
const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'kawaii');
@@ -163,7 +169,7 @@ describe('export-clips', () => {
});
test('Clipping other user\'s note', async () => {
- let res = await api('clips/create', {
+ const res = await api('clips/create', {
name: 'kawaii',
description: 'kawaii',
}, alice);
@@ -175,14 +181,14 @@ describe('export-clips', () => {
visibility: 'followers',
});
- res = await api('clips/add-note', {
+ const res2 = await api('clips/add-note', {
clipId: clip.id,
noteId: note.id,
}, alice);
- assert.strictEqual(res.status, 204);
+ assert.strictEqual(res2.status, 204);
- res = await api('i/export-clips', {}, alice);
- assert.strictEqual(res.status, 204);
+ const res3 = await api('i/export-clips', {}, alice);
+ assert.strictEqual(res3.status, 204);
const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'kawaii');
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 4851ed14be..7efd688ec2 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -153,6 +153,23 @@ describe('Webリソース', () => {
path: path('nonexisting'),
status: 404,
}));
+
+ describe(' has entry such ', () => {
+ beforeEach(() => {
+ post(alice, { text: "**a**" })
+ });
+
+ test('MFMを含まない。', async () => {
+ const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
+ const _body: unknown = content.body;
+ // JSONフィードのときは改めて文字列化する
+ const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
+
+ if (body.includes("**a**")) {
+ throw new Error("MFM shouldn't be included");
+ }
+ });
+ })
});
describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 35050130dc..fd798bdb25 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -7,19 +7,20 @@ import { INestApplicationContext } from '@nestjs/common';
process.env.NODE_ENV = 'test';
+import { setTimeout } from 'node:timers/promises';
import * as assert from 'assert';
import { loadConfig } from '@/config.js';
-import { MiUser, UsersRepository } from '@/models/_.js';
+import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import { jobQueue } from '@/boot/common.js';
-import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
+import { api, castAsError, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Account Move', () => {
let jq: INestApplicationContext;
let url: URL;
- let root: any;
+ let root: misskey.entities.SignupResponse;
let alice: misskey.entities.SignupResponse;
let bob: misskey.entities.SignupResponse;
let carol: misskey.entities.SignupResponse;
@@ -42,7 +43,7 @@ describe('Account Move', () => {
dave = await signup({ username: 'dave' });
eve = await signup({ username: 'eve' });
frank = await signup({ username: 'frank' });
- Users = connection.getRepository(MiUser);
+ Users = connection.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>);
}, 1000 * 60 * 2);
afterAll(async () => {
@@ -92,8 +93,8 @@ describe('Account Move', () => {
}, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
- assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
+ assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
});
test('Unable to add duplicated aliases to alsoKnownAs', async () => {
@@ -102,8 +103,8 @@ describe('Account Move', () => {
}, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'INVALID_PARAM');
- assert.strictEqual(res.body.error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
+ assert.strictEqual(castAsError(res.body).error.code, 'INVALID_PARAM');
+ assert.strictEqual(castAsError(res.body).error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
});
test('Unable to add itself', async () => {
@@ -112,8 +113,8 @@ describe('Account Move', () => {
}, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'FORBIDDEN_TO_SET_YOURSELF');
- assert.strictEqual(res.body.error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
+ assert.strictEqual(castAsError(res.body).error.code, 'FORBIDDEN_TO_SET_YOURSELF');
+ assert.strictEqual(castAsError(res.body).error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
});
test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
@@ -122,16 +123,16 @@ describe('Account Move', () => {
}, bob);
assert.strictEqual(res1.status, 400);
- assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
- assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ assert.strictEqual(castAsError(res1.body).error.code, 'NO_SUCH_USER');
+ assert.strictEqual(castAsError(res1.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
const res2 = await api('i/update', {
alsoKnownAs: ['@alice', 'nonexist'],
}, bob);
assert.strictEqual(res2.status, 400);
- assert.strictEqual(res2.body.error.code, 'NO_SUCH_USER');
- assert.strictEqual(res2.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ assert.strictEqual(castAsError(res2.body).error.code, 'NO_SUCH_USER');
+ assert.strictEqual(castAsError(res2.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
});
test('Able to add two existing local account to alsoKnownAs', async () => {
@@ -240,8 +241,8 @@ describe('Account Move', () => {
}, root);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NOT_ROOT_FORBIDDEN');
- assert.strictEqual(res.body.error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
+ assert.strictEqual(castAsError(res.body).error.code, 'NOT_ROOT_FORBIDDEN');
+ assert.strictEqual(castAsError(res.body).error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
});
test('Unable to move to a nonexisting local account', async () => {
@@ -250,8 +251,8 @@ describe('Account Move', () => {
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
- assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
+ assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
});
test('Unable to move if alsoKnownAs is invalid', async () => {
@@ -260,8 +261,8 @@ describe('Account Move', () => {
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
- assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+ assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+ assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
});
test('Relationships have been properly migrated', async () => {
@@ -271,43 +272,51 @@ describe('Account Move', () => {
assert.strictEqual(move.status, 200);
- await sleep(1000 * 3); // wait for jobs to finish
+ await setTimeout(1000 * 3); // wait for jobs to finish
// Unfollow delayed?
const aliceFollowings = await api('users/following', {
userId: alice.id,
}, alice);
assert.strictEqual(aliceFollowings.status, 200);
+ assert.ok(aliceFollowings);
assert.strictEqual(aliceFollowings.body.length, 3);
const carolFollowings = await api('users/following', {
userId: carol.id,
}, carol);
assert.strictEqual(carolFollowings.status, 200);
+ assert.ok(carolFollowings);
assert.strictEqual(carolFollowings.body.length, 2);
assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
const blockings = await api('blocking/list', {}, dave);
assert.strictEqual(blockings.status, 200);
+ assert.ok(blockings);
assert.strictEqual(blockings.body.length, 2);
assert.strictEqual(blockings.body[0].blockeeId, bob.id);
assert.strictEqual(blockings.body[1].blockeeId, alice.id);
const mutings = await api('mute/list', {}, dave);
assert.strictEqual(mutings.status, 200);
+ assert.ok(mutings);
assert.strictEqual(mutings.body.length, 2);
assert.strictEqual(mutings.body[0].muteeId, bob.id);
assert.strictEqual(mutings.body[1].muteeId, alice.id);
const rootLists = await api('users/lists/list', {}, root);
assert.strictEqual(rootLists.status, 200);
+ assert.ok(rootLists);
+ assert.ok(rootLists.body[0].userIds);
assert.strictEqual(rootLists.body[0].userIds.length, 2);
assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
const eveLists = await api('users/lists/list', {}, eve);
assert.strictEqual(eveLists.status, 200);
+ assert.ok(eveLists);
+ assert.ok(eveLists.body[0].userIds);
assert.strictEqual(eveLists.body[0].userIds.length, 1);
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
});
@@ -330,7 +339,7 @@ describe('Account Move', () => {
});
test('Unfollowed after 10 sec (24 hours in production).', async () => {
- await sleep(1000 * 8);
+ await setTimeout(1000 * 8);
const following = await api('users/following', {
userId: alice.id,
@@ -346,8 +355,8 @@ describe('Account Move', () => {
}, bob);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
- assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+ assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+ assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
});
test('Follow and follower counts are properly adjusted', async () => {
@@ -418,8 +427,9 @@ describe('Account Move', () => {
] as const)('Prohibit access after moving: %s', async (endpoint) => {
const res = await api(endpoint, {}, alice);
assert.strictEqual(res.status, 403);
- assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
- assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
});
test('Prohibit access after moving: /antennas/update', async () => {
@@ -437,16 +447,19 @@ describe('Account Move', () => {
}, alice);
assert.strictEqual(res.status, 403);
- assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
- assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
});
test('Prohibit access after moving: /drive/files/create', async () => {
+ // FIXME: 一旦逃げておく
const res = await uploadFile(alice);
assert.strictEqual(res.status, 403);
- assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED');
- assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ assert.ok(res.body);
+ assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
});
test('Prohibit updating alsoKnownAs after moving', async () => {
@@ -455,8 +468,8 @@ describe('Account Move', () => {
}, alice);
assert.strictEqual(res.status, 403);
- assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
- assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+ assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+ assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
});
});
});
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index 0e52c5decc..f37da288b7 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -47,8 +47,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
@@ -92,9 +92,9 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => {
@@ -108,9 +108,9 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
});
@@ -124,8 +124,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -138,8 +138,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -152,8 +152,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@@ -166,8 +166,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@@ -180,8 +180,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@@ -193,8 +193,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol);
@@ -210,8 +210,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol);
@@ -228,8 +228,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
const aliceNote = await post(alice, { text: 'hi' });
@@ -241,8 +241,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -255,8 +255,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@@ -269,8 +269,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@@ -283,8 +283,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@@ -296,8 +296,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol);
@@ -313,8 +313,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
- assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+ assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
});
});
});
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index bda31d9640..5937eb9b49 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -3,16 +3,18 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import type { Repository } from "typeorm";
+
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { MiNote } from '@/models/Note.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
-import { api, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
+import { api, castAsError, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Note', () => {
- let Notes: any;
+ let Notes: Repository<MiNote>;
let root: misskey.entities.SignupResponse;
let alice: misskey.entities.SignupResponse;
@@ -41,7 +43,7 @@ describe('Note', () => {
});
test('ファイルを添付できる', async () => {
- const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
+ const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
const res = await api('notes/create', {
fileIds: [file.id],
@@ -53,7 +55,7 @@ describe('Note', () => {
}, 1000 * 10);
test('他人のファイルで怒られる', async () => {
- const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
+ const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
const res = await api('notes/create', {
text: 'test',
@@ -61,8 +63,8 @@ describe('Note', () => {
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
- assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+ assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
}, 1000 * 10);
test('存在しないファイルで怒られる', async () => {
@@ -72,8 +74,8 @@ describe('Note', () => {
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
- assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+ assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
});
test('不正なファイルIDで怒られる', async () => {
@@ -81,8 +83,8 @@ describe('Note', () => {
fileIds: ['kyoppie'],
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
- assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+ assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
});
test('返信できる', async () => {
@@ -101,6 +103,7 @@ describe('Note', () => {
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId);
+ assert.ok(res.body.createdNote.reply);
assert.strictEqual(res.body.createdNote.reply.text, bobPost.text);
});
@@ -118,6 +121,7 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
+ assert.ok(res.body.createdNote.renote);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
});
@@ -137,6 +141,7 @@ describe('Note', () => {
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
+ assert.ok(res.body.createdNote.renote);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
});
@@ -218,7 +223,7 @@ describe('Note', () => {
}, bob);
assert.strictEqual(bobReply.status, 400);
- assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
+ assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
});
test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
@@ -256,7 +261,7 @@ describe('Note', () => {
}, bob);
assert.strictEqual(bobReply.status, 400);
- assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
+ assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
});
test('文字数ぎりぎりで怒られない', async () => {
@@ -333,6 +338,7 @@ describe('Note', () => {
assert.strictEqual(res.body.createdNote.text, post.text);
const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id });
+ assert.ok(noteDoc);
assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
});
@@ -345,6 +351,7 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
+ assert.ok(res.body.createdNote.files);
assert.strictEqual(res.body.createdNote.files.length, 1);
assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id);
});
@@ -363,8 +370,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
- assert.notEqual(myNote, null);
+ const myNote = res.body.find(note => note.id === createdNote.body.createdNote.id);
+ assert.ok(myNote);
+ assert.ok(myNote.files);
assert.strictEqual(myNote.files.length, 1);
assert.strictEqual(myNote.files[0].id, file.body!.id);
});
@@ -389,7 +397,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
- assert.notEqual(myNote, null);
+ assert.ok(myNote);
+ assert.ok(myNote.renote);
+ assert.ok(myNote.renote.files);
assert.strictEqual(myNote.renote.files.length, 1);
assert.strictEqual(myNote.renote.files[0].id, file.body!.id);
});
@@ -415,7 +425,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
- assert.notEqual(myNote, null);
+ assert.ok(myNote);
+ assert.ok(myNote.reply);
+ assert.ok(myNote.reply.files);
assert.strictEqual(myNote.reply.files.length, 1);
assert.strictEqual(myNote.reply.files[0].id, file.body!.id);
});
@@ -446,7 +458,10 @@ describe('Note', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
- assert.notEqual(myNote, null);
+ assert.ok(myNote);
+ assert.ok(myNote.renote);
+ assert.ok(myNote.renote.reply);
+ assert.ok(myNote.renote.reply.files);
assert.strictEqual(myNote.renote.reply.files.length, 1);
assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id);
});
@@ -474,7 +489,7 @@ describe('Note', () => {
priority: 0,
value: true,
},
- } as any,
+ },
}, root);
assert.strictEqual(res.status, 200);
@@ -498,7 +513,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(liftnsfw.status, 400);
- assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
+ assert.strictEqual(castAsError(liftnsfw.body).error.code, 'RESTRICTED_BY_ROLE');
const oldaddnsfw = await api('drive/files/update', {
fileId: file.body!.id,
@@ -710,7 +725,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(note1.status, 400);
- assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ assert.strictEqual(castAsError(note1.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
});
test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => {
@@ -727,7 +742,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(note2.status, 400);
- assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
});
test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => {
@@ -744,7 +759,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(note2.status, 400);
- assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
});
test('禁止ワードを含んでるリモートノートもエラーになる', async () => {
@@ -786,7 +801,7 @@ describe('Note', () => {
priority: 1,
value: 0,
},
- } as any,
+ },
}, root);
assert.strictEqual(res.status, 200);
@@ -807,7 +822,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(note.status, 400);
- assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+ assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', {
userId: alice.id,
@@ -840,7 +855,7 @@ describe('Note', () => {
priority: 1,
value: 0,
},
- } as any,
+ },
}, root);
assert.strictEqual(res.status, 200);
@@ -863,7 +878,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(note.status, 400);
- assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+ assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', {
userId: alice.id,
@@ -896,7 +911,7 @@ describe('Note', () => {
priority: 1,
value: 1,
},
- } as any,
+ },
}, root);
assert.strictEqual(res.status, 200);
@@ -951,6 +966,7 @@ describe('Note', () => {
assert.strictEqual(deleteOneRes.status, 204);
let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
+ assert.ok(mainNote);
assert.strictEqual(mainNote.repliesCount, 1);
const deleteTwoRes = await api('notes/delete', {
@@ -959,6 +975,7 @@ describe('Note', () => {
assert.strictEqual(deleteTwoRes.status, 204);
mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
+ assert.ok(mainNote);
assert.strictEqual(mainNote.repliesCount, 0);
});
});
@@ -980,7 +997,7 @@ describe('Note', () => {
}, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+ assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
});
afterAll(async () => {
@@ -992,7 +1009,7 @@ describe('Note', () => {
const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice);
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'NO_SUCH_NOTE');
+ assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_NOTE');
});
test('不可視なノートは翻訳できない', async () => {
@@ -1000,7 +1017,7 @@ describe('Note', () => {
const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob);
assert.strictEqual(bobTranslateAttempt.status, 400);
- assert.strictEqual(bobTranslateAttempt.body.error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
+ assert.strictEqual(castAsError(bobTranslateAttempt.body).error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
});
test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => {
@@ -1016,7 +1033,7 @@ describe('Note', () => {
// NOTE: デフォルトでは登録されていないので落ちる
assert.strictEqual(res.status, 400);
- assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+ assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
});
});
});
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index 1abbb4f044..0f636b9ae2 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -6,7 +6,8 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
-import { api, post, signup, sleep, waitFire } from '../utils.js';
+import { setTimeout } from 'node:timers/promises';
+import { api, post, signup, waitFire } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Renote Mute', () => {
@@ -35,15 +36,15 @@ describe('Renote Mute', () => {
const carolNote = await post(carol, { text: 'hi' });
// redisに追加されるのを待つ
- await sleep(100);
+ await setTimeout(100);
const res = await api('notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolRenote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
});
test('タイムラインにリノートミュートしているユーザーの引用が含まれる', async () => {
@@ -52,15 +53,15 @@ describe('Renote Mute', () => {
const carolNote = await post(carol, { text: 'hi' });
// redisに追加されるのを待つ
- await sleep(100);
+ await setTimeout(100);
const res = await api('notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolRenote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
});
// #12956
@@ -69,14 +70,14 @@ describe('Renote Mute', () => {
const bobRenote = await post(bob, { renoteId: carolNote.id });
// redisに追加されるのを待つ
- await sleep(100);
+ await setTimeout(100);
const res = await api('notes/local-timeline', {}, alice);
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobRenote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobRenote.id), true);
});
test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {
diff --git a/packages/backend/test/e2e/reversi-game.ts b/packages/backend/test/e2e/reversi-game.ts
new file mode 100644
index 0000000000..788255beac
--- /dev/null
+++ b/packages/backend/test/e2e/reversi-game.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import { ReversiMatchResponse } from 'misskey-js/entities.js';
+import { api, signup } from '../utils.js';
+import type * as misskey from 'misskey-js';
+
+describe('ReversiGame', () => {
+ let alice: misskey.entities.SignupResponse;
+ let bob: misskey.entities.SignupResponse;
+
+ beforeAll(async () => {
+ alice = await signup({ username: 'alice' });
+ bob = await signup({ username: 'bob' });
+ }, 1000 * 60 * 2);
+
+ test('matches when alice invites bob and bob accepts', async () => {
+ const response1 = await api('reversi/match', { userId: bob.id }, alice);
+ assert.strictEqual(response1.status, 204);
+ assert.strictEqual(response1.body, null);
+ const response2 = await api('reversi/match', { userId: alice.id }, bob);
+ assert.strictEqual(response2.status, 200);
+ assert.notStrictEqual(response2.body, null);
+ const body = response2.body as ReversiMatchResponse;
+ assert.strictEqual(body.user1.id, alice.id);
+ assert.strictEqual(body.user2.id, bob.id);
+ });
+});
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index b0a70074c6..72f26a38e0 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -34,6 +34,7 @@ describe('Streaming', () => {
let kyoko: misskey.entities.SignupResponse;
let chitose: misskey.entities.SignupResponse;
let kanako: misskey.entities.SignupResponse;
+ let erin: misskey.entities.SignupResponse;
// Remote users
let akari: misskey.entities.SignupResponse;
@@ -53,6 +54,7 @@ describe('Streaming', () => {
kyoko = await signup({ username: 'kyoko' });
chitose = await signup({ username: 'chitose' });
kanako = await signup({ username: 'kanako' });
+ erin = await signup({ username: 'erin' }); // erin: A generic fifth participant
akari = await signup({ username: 'akari', host: 'example.com' });
chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
@@ -71,6 +73,12 @@ describe('Streaming', () => {
// Follow: kyoko => chitose
await api('following/create', { userId: chitose.id }, kyoko);
+ // Follow: erin <=> ayano each other.
+ // erin => ayano: withReplies: true
+ await api('following/create', { userId: ayano.id, withReplies: true }, erin);
+ // ayano => erin: withReplies: false
+ await api('following/create', { userId: erin.id, withReplies: false }, ayano);
+
// Mute: chitose => kanako
await api('mute/create', { userId: kanako.id }, chitose);
@@ -297,6 +305,28 @@ describe('Streaming', () => {
assert.strictEqual(fired, true);
});
+
+ test('withReplies: true のとき自分のfollowers投稿に対するリプライが流れる', async () => {
+ const erinNote = await post(erin, { text: 'hi', visibility: 'followers' });
+ const fired = await waitFire(
+ erin, 'homeTimeline', // erin:home
+ () => api('notes/create', { text: 'hello', replyId: erinNote.id }, ayano), // ayano reply to erin's followers post
+ msg => msg.type === 'note' && msg.body.userId === ayano.id, // wait ayano
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('withReplies: false でも自分の投稿に対するリプライが流れる', async () => {
+ const ayanoNote = await post(ayano, { text: 'hi', visibility: 'followers' });
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:home
+ () => api('notes/create', { text: 'hello', replyId: ayanoNote.id }, erin), // erin reply to ayano's followers post
+ msg => msg.type === 'note' && msg.body.userId === erin.id, // wait erin
+ );
+
+ assert.strictEqual(fired, true);
+ });
}); // Home
describe('Local Timeline', () => {
@@ -475,6 +505,38 @@ describe('Streaming', () => {
assert.strictEqual(fired, false);
});
+
+ test('withReplies: true のとき自分のfollowers投稿に対するリプライが流れる', async () => {
+ const erinNote = await post(erin, { text: 'hi', visibility: 'followers' });
+ const fired = await waitFire(
+ erin, 'homeTimeline', // erin:home
+ () => api('notes/create', { text: 'hello', replyId: erinNote.id }, ayano), // ayano reply to erin's followers post
+ msg => msg.type === 'note' && msg.body.userId === ayano.id, // wait ayano
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('withReplies: false でも自分の投稿に対するリプライが流れる', async () => {
+ const ayanoNote = await post(ayano, { text: 'hi', visibility: 'followers' });
+ const fired = await waitFire(
+ ayano, 'homeTimeline', // ayano:home
+ () => api('notes/create', { text: 'hello', replyId: ayanoNote.id }, erin), // erin reply to ayano's followers post
+ msg => msg.type === 'note' && msg.body.userId === erin.id, // wait erin
+ );
+
+ assert.strictEqual(fired, true);
+ });
+
+ test('withReplies: true のフォローしていない人のfollowersノートに対するリプライが流れない', async () => {
+ const fired = await waitFire(
+ erin, 'homeTimeline', // erin:home
+ () => api('notes/create', { text: 'hello', replyId: chitose.id }, ayano), // ayano reply to chitose's post
+ msg => msg.type === 'note' && msg.body.userId === ayano.id, // wait ayano
+ );
+
+ assert.strictEqual(fired, false);
+ });
});
describe('Global Timeline', () => {
diff --git a/packages/backend/test/e2e/synalio/abuse-report.ts b/packages/backend/test/e2e/synalio/abuse-report.ts
new file mode 100644
index 0000000000..6ce6e47781
--- /dev/null
+++ b/packages/backend/test/e2e/synalio/abuse-report.ts
@@ -0,0 +1,360 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { entities } from 'misskey-js';
+import { beforeEach, describe, test } from '@jest/globals';
+import {
+ api,
+ captureWebhook,
+ randomString,
+ role,
+ signup,
+ startJobQueue,
+ UserToken,
+ WEBHOOK_HOST,
+} from '../../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('[シナリオ] ユーザ通報', () => {
+ let queue: INestApplicationContext;
+ let admin: entities.SignupResponse;
+ let alice: entities.SignupResponse;
+ let bob: entities.SignupResponse;
+
+ async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
+ const res = await api(
+ 'admin/system-webhook/create',
+ {
+ isActive: true,
+ name: randomString(),
+ on: ['abuseReport'],
+ url: WEBHOOK_HOST,
+ secret: randomString(),
+ ...args,
+ },
+ credential ?? admin,
+ );
+ return res.body;
+ }
+
+ async function createAbuseReportNotificationRecipient(args?: Partial<entities.AdminAbuseReportNotificationRecipientCreateRequest>, credential?: UserToken): Promise<entities.AdminAbuseReportNotificationRecipientCreateResponse> {
+ const res = await api(
+ 'admin/abuse-report/notification-recipient/create',
+ {
+ isActive: true,
+ name: randomString(),
+ method: 'webhook',
+ ...args,
+ },
+ credential ?? admin,
+ );
+ return res.body;
+ }
+
+ async function createAbuseReport(args?: Partial<entities.UsersReportAbuseRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
+ const res = await api(
+ 'users/report-abuse',
+ {
+ userId: alice.id,
+ comment: randomString(),
+ ...args,
+ },
+ credential ?? admin,
+ );
+ return res.body;
+ }
+
+ async function resolveAbuseReport(args?: Partial<entities.AdminResolveAbuseUserReportRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
+ const res = await api(
+ 'admin/resolve-abuse-user-report',
+ {
+ reportId: admin.id,
+ ...args,
+ },
+ credential ?? admin,
+ );
+ return res.body;
+ }
+
+ // -------------------------------------------------------------------------------------------
+
+ beforeAll(async () => {
+ queue = await startJobQueue();
+ admin = await signup({ username: 'admin' });
+ alice = await signup({ username: 'alice' });
+ bob = await signup({ username: 'bob' });
+
+ await role(admin, { isAdministrator: true });
+ }, 1000 * 60 * 2);
+
+ afterAll(async () => {
+ await queue.close();
+ });
+
+ // -------------------------------------------------------------------------------------------
+
+ describe('SystemWebhook', () => {
+ beforeEach(async () => {
+ const webhooks = await api('admin/system-webhook/list', {}, admin);
+ for (const webhook of webhooks.body) {
+ await api('admin/system-webhook/delete', { id: webhook.id }, admin);
+ }
+ });
+
+ test('通報を受けた -> abuseReportが送出される', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReport'],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ });
+
+ console.log(JSON.stringify(webhookBody, null, 2));
+
+ expect(webhookBody.hookId).toBe(webhook.id);
+ expect(webhookBody.type).toBe('abuseReport');
+ expect(webhookBody.body.targetUserId).toBe(alice.id);
+ expect(webhookBody.body.reporterId).toBe(bob.id);
+ expect(webhookBody.body.comment).toBe(abuse.comment);
+ });
+
+ test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが送出される', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReport', 'abuseReportResolved'],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ });
+
+ console.log(JSON.stringify(webhookBody1, null, 2));
+ expect(webhookBody1.hookId).toBe(webhook.id);
+ expect(webhookBody1.type).toBe('abuseReport');
+ expect(webhookBody1.body.targetUserId).toBe(alice.id);
+ expect(webhookBody1.body.reporterId).toBe(bob.id);
+ expect(webhookBody1.body.assigneeId).toBeNull();
+ expect(webhookBody1.body.resolved).toBe(false);
+ expect(webhookBody1.body.comment).toBe(abuse.comment);
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: webhookBody1.body.id,
+ forward: false,
+ }, admin);
+ });
+
+ console.log(JSON.stringify(webhookBody2, null, 2));
+ expect(webhookBody2.hookId).toBe(webhook.id);
+ expect(webhookBody2.type).toBe('abuseReportResolved');
+ expect(webhookBody2.body.targetUserId).toBe(alice.id);
+ expect(webhookBody2.body.reporterId).toBe(bob.id);
+ expect(webhookBody2.body.assigneeId).toBe(admin.id);
+ expect(webhookBody2.body.resolved).toBe(true);
+ expect(webhookBody2.body.comment).toBe(abuse.comment);
+ });
+
+ test('通報を受けた -> abuseReportが未許可の場合は送出されない', async () => {
+ const webhook = await createSystemWebhook({
+ on: [],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ }).catch(e => e.message);
+
+ expect(webhookBody).toBe('timeout');
+ });
+
+ test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが送出される', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReportResolved'],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ }).catch(e => e.message);
+
+ expect(webhookBody1).toBe('timeout');
+
+ const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: abuseReportId,
+ forward: false,
+ }, admin);
+ });
+
+ console.log(JSON.stringify(webhookBody2, null, 2));
+ expect(webhookBody2.hookId).toBe(webhook.id);
+ expect(webhookBody2.type).toBe('abuseReportResolved');
+ expect(webhookBody2.body.targetUserId).toBe(alice.id);
+ expect(webhookBody2.body.reporterId).toBe(bob.id);
+ expect(webhookBody2.body.assigneeId).toBe(admin.id);
+ expect(webhookBody2.body.resolved).toBe(true);
+ expect(webhookBody2.body.comment).toBe(abuse.comment);
+ });
+
+ test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReport'],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ });
+
+ console.log(JSON.stringify(webhookBody1, null, 2));
+ expect(webhookBody1.hookId).toBe(webhook.id);
+ expect(webhookBody1.type).toBe('abuseReport');
+ expect(webhookBody1.body.targetUserId).toBe(alice.id);
+ expect(webhookBody1.body.reporterId).toBe(bob.id);
+ expect(webhookBody1.body.assigneeId).toBeNull();
+ expect(webhookBody1.body.resolved).toBe(false);
+ expect(webhookBody1.body.comment).toBe(abuse.comment);
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: webhookBody1.body.id,
+ forward: false,
+ }, admin);
+ }).catch(e => e.message);
+
+ expect(webhookBody2).toBe('timeout');
+ });
+
+ test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
+ const webhook = await createSystemWebhook({
+ on: [],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ }).catch(e => e.message);
+
+ expect(webhookBody1).toBe('timeout');
+
+ const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: abuseReportId,
+ forward: false,
+ }, admin);
+ }).catch(e => e.message);
+
+ expect(webhookBody2).toBe('timeout');
+ });
+
+ test('通報を受けた -> Webhookが無効の場合は送出されない', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReport', 'abuseReportResolved'],
+ isActive: false,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ }).catch(e => e.message);
+
+ expect(webhookBody1).toBe('timeout');
+
+ const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: abuseReportId,
+ forward: false,
+ }, admin);
+ }).catch(e => e.message);
+
+ expect(webhookBody2).toBe('timeout');
+ });
+
+ test('通報を受けた -> 通知設定が無効の場合は送出されない', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['abuseReport', 'abuseReportResolved'],
+ isActive: true,
+ });
+ await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id, isActive: false });
+
+ // 通報(bob -> alice)
+ const abuse = {
+ userId: alice.id,
+ comment: randomString(),
+ };
+ const webhookBody1 = await captureWebhook(async () => {
+ await createAbuseReport(abuse, bob);
+ }).catch(e => e.message);
+
+ expect(webhookBody1).toBe('timeout');
+
+ const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+ // 解決
+ const webhookBody2 = await captureWebhook(async () => {
+ await resolveAbuseReport({
+ reportId: abuseReportId,
+ forward: false,
+ }, admin);
+ }).catch(e => e.message);
+
+ expect(webhookBody2).toBe('timeout');
+ });
+ });
+});
diff --git a/packages/backend/test/e2e/synalio/user-create.ts b/packages/backend/test/e2e/synalio/user-create.ts
new file mode 100644
index 0000000000..cb0f68dfea
--- /dev/null
+++ b/packages/backend/test/e2e/synalio/user-create.ts
@@ -0,0 +1,130 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { setTimeout } from 'node:timers/promises';
+import { entities } from 'misskey-js';
+import { beforeEach, describe, test } from '@jest/globals';
+import {
+ api,
+ captureWebhook,
+ randomString,
+ role,
+ signup,
+ startJobQueue,
+ UserToken,
+ WEBHOOK_HOST,
+} from '../../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('[シナリオ] ユーザ作成', () => {
+ let queue: INestApplicationContext;
+ let admin: entities.SignupResponse;
+
+ async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
+ const res = await api(
+ 'admin/system-webhook/create',
+ {
+ isActive: true,
+ name: randomString(),
+ on: ['userCreated'],
+ url: WEBHOOK_HOST,
+ secret: randomString(),
+ ...args,
+ },
+ credential ?? admin,
+ );
+ return res.body;
+ }
+
+ // -------------------------------------------------------------------------------------------
+
+ beforeAll(async () => {
+ queue = await startJobQueue();
+ admin = await signup({ username: 'admin' });
+
+ await role(admin, { isAdministrator: true });
+ }, 1000 * 60 * 2);
+
+ afterAll(async () => {
+ await queue.close();
+ });
+
+ // -------------------------------------------------------------------------------------------
+
+ describe('SystemWebhook', () => {
+ beforeEach(async () => {
+ const webhooks = await api('admin/system-webhook/list', {}, admin);
+ for (const webhook of webhooks.body) {
+ await api('admin/system-webhook/delete', { id: webhook.id }, admin);
+ }
+ });
+
+ test('ユーザが作成された -> userCreatedが送出される', async () => {
+ const webhook = await createSystemWebhook({
+ on: ['userCreated'],
+ isActive: true,
+ });
+
+ let alice: any = null;
+ const webhookBody = await captureWebhook(async () => {
+ alice = await signup({ username: 'alice' });
+ });
+
+ // webhookの送出後にいろいろやってるのでちょっと待つ必要がある
+ await setTimeout(2000);
+
+ console.log(alice);
+ console.log(JSON.stringify(webhookBody, null, 2));
+
+ expect(webhookBody.hookId).toBe(webhook.id);
+ expect(webhookBody.type).toBe('userCreated');
+
+ const body = webhookBody.body as entities.UserLite;
+ expect(alice.id).toBe(body.id);
+ expect(alice.name).toBe(body.name);
+ expect(alice.username).toBe(body.username);
+ expect(alice.host).toBe(body.host);
+ expect(alice.avatarUrl).toBe(body.avatarUrl);
+ expect(alice.avatarBlurhash).toBe(body.avatarBlurhash);
+ expect(alice.avatarDecorations).toEqual(body.avatarDecorations);
+ expect(alice.isBot).toBe(body.isBot);
+ expect(alice.isCat).toBe(body.isCat);
+ expect(alice.instance).toEqual(body.instance);
+ expect(alice.emojis).toEqual(body.emojis);
+ expect(alice.onlineStatus).toBe(body.onlineStatus);
+ expect(alice.badgeRoles).toEqual(body.badgeRoles);
+ });
+
+ test('ユーザ作成 -> userCreatedが未許可の場合は送出されない', async () => {
+ await createSystemWebhook({
+ on: [],
+ isActive: true,
+ });
+
+ let alice: any = null;
+ const webhookBody = await captureWebhook(async () => {
+ alice = await signup({ username: 'alice' });
+ }).catch(e => e.message);
+
+ expect(webhookBody).toBe('timeout');
+ expect(alice.id).not.toBeNull();
+ });
+
+ test('ユーザ作成 -> Webhookが無効の場合は送出されない', async () => {
+ await createSystemWebhook({
+ on: ['userCreated'],
+ isActive: false,
+ });
+
+ let alice: any = null;
+ const webhookBody = await captureWebhook(async () => {
+ alice = await signup({ username: 'alice' });
+ }).catch(e => e.message);
+
+ expect(webhookBody).toBe('timeout');
+ expect(alice.id).not.toBeNull();
+ });
+ });
+});
diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts
index 53bb6eb765..1ac99df884 100644
--- a/packages/backend/test/e2e/thread-mute.ts
+++ b/packages/backend/test/e2e/thread-mute.ts
@@ -33,9 +33,9 @@ describe('Note thread mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolReply.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolReply.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolReplyWithoutMention.id), false);
});
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
@@ -93,8 +93,8 @@ describe('Note thread mute', () => {
assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true);
- assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReply.id), false);
- assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReplyWithoutMention.id), false);
+ assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReply.id), false);
+ assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReplyWithoutMention.id), false);
// NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい
});
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 5487292afc..d12be2a9ac 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -7,17 +7,26 @@
// pnpm jest -- e2e/timelines.ts
import * as assert from 'assert';
-import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js';
+import { setTimeout } from 'node:timers/promises';
+import { Redis } from 'ioredis';
+import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js';
+import { loadConfig } from '@/config.js';
function genHost() {
return randomString() + '.example.com';
}
function waitForPushToTl() {
- return sleep(500);
+ return setTimeout(500);
}
+let redisForTimelines: Redis;
+
describe('Timelines', () => {
+ beforeAll(() => {
+ redisForTimelines = new Redis(loadConfig().redisForTimelines);
+ });
+
describe('Home TL', () => {
test.concurrent('自分の visibility: followers なノートが含まれる', async () => {
const [alice] = await Promise.all([signup()]);
@@ -28,15 +37,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
});
test.concurrent('フォローしているユーザーのノートが含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi' });
const carolNote = await post(carol, { text: 'hi' });
@@ -44,15 +53,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
const carolNote = await post(carol, { text: 'hi' });
@@ -60,16 +69,16 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -77,8 +86,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
@@ -86,7 +95,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -94,8 +103,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => {
@@ -103,7 +112,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
@@ -111,16 +120,17 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+ await api('following/create', { userId: carol.id }, bob);
await api('following/create', { userId: bob.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -128,8 +138,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
@@ -139,7 +149,7 @@ describe('Timelines', () => {
await api('following/create', { userId: carol.id }, alice);
await api('following/create', { userId: carol.id }, bob);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -147,9 +157,27 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
+ });
+
+ test.concurrent('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: alice.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(1000);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/timeline', { limit: 100 }, alice);
+
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id).text, 'hi');
+ assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
});
test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => {
@@ -158,7 +186,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('following/create', { userId: carol.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
@@ -166,15 +194,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
});
test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote1 = await post(bob, { text: 'hi' });
const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
@@ -182,15 +210,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
@@ -198,8 +226,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('自分の他人への返信が含まれる', async () => {
@@ -212,15 +240,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
});
test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { renoteId: carolNote.id });
@@ -228,15 +256,15 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { renoteId: carolNote.id });
@@ -246,15 +274,15 @@ describe('Timelines', () => {
withRenotes: false,
}, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
@@ -264,22 +292,22 @@ describe('Timelines', () => {
withRenotes: false,
}, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
await waitForPushToTl();
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@@ -287,7 +315,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
@@ -295,8 +323,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@@ -305,7 +333,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -313,8 +341,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@@ -329,7 +357,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@@ -344,14 +372,14 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const [bobFile, carolFile] = await Promise.all([
uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
@@ -365,10 +393,10 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote2.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
}, 1000 * 10);
test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
@@ -376,14 +404,14 @@ describe('Timelines', () => {
const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
await waitForPushToTl();
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('自分の visibility: specified なノートが含まれる', async () => {
@@ -395,23 +423,23 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
});
test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
await waitForPushToTl();
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
});
test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => {
@@ -423,21 +451,21 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
await waitForPushToTl();
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => {
@@ -450,8 +478,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok');
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok');
});
/* TODO
@@ -465,8 +493,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok');
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
});
*/
@@ -481,7 +509,45 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ test.concurrent('FTT: ローカルユーザーの HTL にはプッシュされる', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ await api('following/create', {
+ userId: alice.id,
+ }, bob);
+
+ const aliceNote = await post(alice, { text: 'I\'m Alice.' });
+ const bobNote = await post(bob, { text: 'I\'m Bob.' });
+ const carolNote = await post(carol, { text: 'I\'m Carol.' });
+
+ await waitForPushToTl();
+
+ // NOTE: notes/timeline だと DB へのフォールバックが効くので Redis を直接見て確かめる
+ assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 1);
+
+ const bobHTL = await redisForTimelines.lrange(`list:homeTimeline:${bob.id}`, 0, -1);
+ assert.strictEqual(bobHTL.includes(aliceNote.id), true);
+ assert.strictEqual(bobHTL.includes(bobNote.id), true);
+ assert.strictEqual(bobHTL.includes(carolNote.id), false);
+ });
+
+ test.concurrent('FTT: リモートユーザーの HTL にはプッシュされない', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+
+ await api('following/create', {
+ userId: alice.id,
+ }, bob);
+
+ await post(alice, { text: 'I\'m Alice.' });
+ await post(bob, { text: 'I\'m Bob.' });
+
+ await waitForPushToTl();
+
+ // NOTE: notes/timeline だと DB へのフォールバックが効くので Redis を直接見て確かめる
+ assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0);
});
});
@@ -496,8 +562,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('他人の他人への返信が含まれない', async () => {
@@ -510,8 +576,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
});
test.concurrent('他人のその人自身への返信が含まれる', async () => {
@@ -524,8 +590,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
});
test.concurrent('チャンネル投稿が含まれない', async () => {
@@ -538,7 +604,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('リモートユーザーのノートが含まれない', async () => {
@@ -550,7 +616,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
// 含まれても良いと思うけど実装が面倒なので含まれない
@@ -558,7 +624,7 @@ describe('Timelines', () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('following/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
const bobNote = await post(bob, { text: 'hi' });
@@ -566,15 +632,15 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('ミュートしているユーザーのノートが含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi' });
@@ -582,8 +648,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@@ -591,7 +657,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
@@ -599,8 +665,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@@ -609,7 +675,7 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
await api('following/update', { userId: bob.id, withReplies: true }, alice);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -617,15 +683,15 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
@@ -633,8 +699,23 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
+
+ test.concurrent('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await setTimeout(1000);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@@ -647,7 +728,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -661,8 +742,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10);
});
@@ -676,7 +757,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('ローカルユーザーの visibility: home なノートが含まれない', async () => {
@@ -688,28 +769,28 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
await waitForPushToTl();
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
@@ -717,8 +798,64 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
+
+ test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(1000);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+ });
+
+ test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
+ const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: carol.id }, alice);
+ await api('following/create', { userId: carol.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(1000);
+ const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id)?.text, 'hi');
+ });
+
+ test.concurrent('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await api('following/create', { userId: bob.id }, alice);
+ await api('following/create', { userId: alice.id }, bob);
+ await api('following/update', { userId: bob.id, withReplies: true }, alice);
+ await setTimeout(1000);
+ const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
});
test.concurrent('他人の他人への返信が含まれない', async () => {
@@ -731,8 +868,8 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
});
test.concurrent('リモートユーザーのノートが含まれない', async () => {
@@ -744,7 +881,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@@ -759,7 +896,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@@ -774,7 +911,22 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ });
+
+ test.concurrent('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
+ const [alice, bob] = await Promise.all([signup(), signup()]);
+
+ await setTimeout(1000);
+ const aliceNote = await post(alice, { text: 'hi' });
+ const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+ await waitForPushToTl();
+
+ const res = await api('notes/local-timeline', { limit: 100 }, alice);
+
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@@ -787,7 +939,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -801,8 +953,8 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10);
});
@@ -812,14 +964,14 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => {
@@ -827,14 +979,14 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@@ -842,14 +994,14 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@@ -857,7 +1009,7 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -865,7 +1017,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => {
@@ -873,7 +1025,7 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote1 = await post(bob, { text: 'hi' });
const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
@@ -881,8 +1033,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
});
test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => {
@@ -891,7 +1043,7 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const aliceNote = await post(alice, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
@@ -899,7 +1051,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@@ -908,7 +1060,7 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -916,7 +1068,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
@@ -925,7 +1077,7 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
@@ -933,7 +1085,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => {
@@ -942,14 +1094,14 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@@ -958,15 +1110,15 @@ describe('Timelines', () => {
await api('following/create', { userId: bob.id }, alice);
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
});
test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => {
@@ -974,15 +1126,15 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: alice.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
});
test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => {
@@ -991,14 +1143,14 @@ describe('Timelines', () => {
const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
@@ -1014,8 +1166,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10);
test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
@@ -1023,15 +1175,15 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
});
test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => {
@@ -1040,14 +1192,14 @@ describe('Timelines', () => {
const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
await waitForPushToTl();
const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
});
@@ -1061,7 +1213,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@@ -1073,22 +1225,22 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('following/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
await waitForPushToTl();
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
});
test.concurrent('自身の visibility: followers なノートが含まれる', async () => {
@@ -1100,8 +1252,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: alice.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
- assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
});
test.concurrent('チャンネル投稿が含まれない', async () => {
@@ -1114,7 +1266,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('[withReplies: false] 他人への返信が含まれない', async () => {
@@ -1128,8 +1280,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
});
test.concurrent('[withReplies: true] 他人への返信が含まれる', async () => {
@@ -1143,8 +1295,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
});
test.concurrent('[withReplies: true] 他人への visibility: specified な返信が含まれない', async () => {
@@ -1158,8 +1310,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
});
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -1173,8 +1325,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10);
test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
@@ -1187,7 +1339,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => {
@@ -1200,7 +1352,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => {
@@ -1213,14 +1365,14 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
});
test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => {
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
await api('mute/create', { userId: carol.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const carolNote = await post(carol, { text: 'hi' });
const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
@@ -1228,14 +1380,14 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
});
test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => {
const [alice, bob] = await Promise.all([signup(), signup()]);
await api('mute/create', { userId: bob.id }, alice);
- await sleep(1000);
+ await setTimeout(1000);
const bobNote1 = await post(bob, { text: 'hi' });
const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
const bobNote3 = await post(bob, { text: 'hi', renoteId: bobNote1.id });
@@ -1244,9 +1396,9 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+ assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
});
test.concurrent('自身の visibility: specified なノートが含まれる', async () => {
@@ -1258,7 +1410,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+ assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
});
test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => {
@@ -1270,7 +1422,34 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
- assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+ assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+ });
+
+ /** @see https://github.com/misskey-dev/misskey/issues/14000 */
+ test.concurrent('FTT: sinceId にキャッシュより古いノートを指定しても、sinceId による絞り込みが正しく動作する', async () => {
+ const alice = await signup();
+ const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+ const note1 = await post(alice, { text: '1' });
+ const note2 = await post(alice, { text: '2' });
+ await redisForTimelines.del('list:userTimeline:' + alice.id);
+ const note3 = await post(alice, { text: '3' });
+
+ const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id });
+ assert.deepStrictEqual(res.body, [note1, note2, note3]);
+ });
+
+ test.concurrent('FTT: sinceId にキャッシュより古いノートを指定しても、sinceId と untilId による絞り込みが正しく動作する', async () => {
+ const alice = await signup();
+ const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+ const note1 = await post(alice, { text: '1' });
+ const note2 = await post(alice, { text: '2' });
+ await redisForTimelines.del('list:userTimeline:' + alice.id);
+ const note3 = await post(alice, { text: '3' });
+ const noteUntil = await post(alice, { text: 'Note where id will be `untilId`.' });
+ await post(alice, { text: '4' });
+
+ const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id, untilId: noteUntil.id });
+ assert.deepStrictEqual(res.body, [note3, note2, note1]);
});
});
diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts
index 331e053935..cc07c5ae71 100644
--- a/packages/backend/test/e2e/user-notes.ts
+++ b/packages/backend/test/e2e/user-notes.ts
@@ -17,8 +17,8 @@ describe('users/notes', () => {
beforeAll(async () => {
alice = await signup({ username: 'alice' });
- 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');
+ const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
+ const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.png');
jpgNote = await post(alice, {
fileIds: [jpg.id],
});
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 89d48158ea..eb56f7bebf 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -234,7 +234,7 @@ describe('ユーザー', () => {
rolePublic = await role(root, { isPublic: true, name: 'Public Role' });
await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root);
userRoleBadge = await signup({ username: 'userRoleBadge' });
- roleBadge = await role(root, { asBadge: true, name: 'Badge Role' });
+ roleBadge = await role(root, { asBadge: true, name: 'Badge Role', isPublic: true });
await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root);
userSilenced = await signup({ username: 'userSilenced' });
await post(userSilenced, { text: 'test' });
@@ -684,7 +684,16 @@ describe('ユーザー', () => {
iconUrl: roleBadge.iconUrl,
displayOrder: roleBadge.displayOrder,
}]);
- assert.deepStrictEqual(response.roles, []); // バッヂだからといってrolesが取れるとは限らない
+ assert.deepStrictEqual(response.roles, [{
+ id: roleBadge.id,
+ name: roleBadge.name,
+ color: roleBadge.color,
+ iconUrl: roleBadge.iconUrl,
+ description: roleBadge.description,
+ isModerator: roleBadge.isModerator,
+ isAdministrator: roleBadge.isAdministrator,
+ displayOrder: roleBadge.displayOrder,
+ }]);
});
test('をID指定のリスト形式で取得することができる(空)', async () => {
const parameters = { userIds: [] };