From b780208921f485ceabf48e162712a1476f3b3c1a Mon Sep 17 00:00:00 2001 From: Fairy-Phy Date: Sun, 8 Oct 2023 11:45:48 +0900 Subject: [PATCH 1/7] =?UTF-8?q?DM=E3=82=92redis=E3=81=ABpush=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/NoteCreateService.ts | 68 +++++++++++++------ .../src/server/api/endpoints/users/notes.ts | 1 + 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4a454e79e714..e68372120680 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -494,11 +494,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Increment notes count (user) this.incNotesCountOfUser(user); - if (data.visibility === 'specified') { - // TODO? - } else { - this.pushToTl(note, user); - } + this.pushToTl(note, user); this.antennaService.addNoteToAntennas(note, user); @@ -874,28 +870,52 @@ export class NoteCreateService implements OnApplicationShutdown { where: { userId: user.id, }, - select: ['userListId', 'withReplies'], + select: ['userList', 'userListId', 'withReplies'], + relations: ['userList'] }); - // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする - for (const following of followings) { - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { - if (!following.withReplies) continue; - } + if (note.visibility === 'specified') { + // 基本的にvisibleUserIdsには自身のidが含まれている前提であること + for (const userId of note.visibleUserIds) { + // 自分自身と返信元の人以外 + if (note.replyId && note.userId !== userId && note.replyUserId !== userId) continue; - redisPipeline.xadd( - `homeTimeline:${following.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); + redisPipeline.xadd( + `homeTimeline:${userId}`, + 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), + '*', + 'note', note.id); + + if (note.fileIds.length > 0) { + redisPipeline.xadd( + `homeTimelineWithFiles:${userId}`, + 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), + '*', + 'note', note.id); + } + } + } + else { + // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする + for (const following of followings) { + // 自分自身以外への返信 + if (note.replyId && note.replyUserId !== note.userId) { + if (!following.withReplies) continue; + } - if (note.fileIds.length > 0) { redisPipeline.xadd( - `homeTimelineWithFiles:${following.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), + `homeTimeline:${following.followerId}`, + 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), '*', 'note', note.id); + + if (note.fileIds.length > 0) { + redisPipeline.xadd( + `homeTimelineWithFiles:${following.followerId}`, + 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), + '*', + 'note', note.id); + } } } @@ -906,6 +926,12 @@ export class NoteCreateService implements OnApplicationShutdown { //} for (const userListMembership of userListMemberships) { + // ダイレクトのとき、そのリストが対象外のユーザーの場合 + if ( + note.visibility === 'specified' && + !note.visibleUserIds.some(v => v === userListMembership.userList!.userId) + ) continue; + // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { if (!userListMembership.withReplies) continue; @@ -926,7 +952,7 @@ export class NoteCreateService implements OnApplicationShutdown { } } - { // 自分自身のHTL + if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL redisPipeline.xadd( `homeTimeline:${user.id}`, 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 71aa6e661cf1..b724364bf08d 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -136,6 +136,7 @@ export default class extends Endpoint { // eslint- } } + if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; if (note.visibility === 'followers' && !isFollowing) return false; return true; From 515044aaf97e3d5d268fd54e1268c30e6f8a98ba Mon Sep 17 00:00:00 2001 From: Fairy-Phy Date: Sun, 8 Oct 2023 14:34:21 +0900 Subject: [PATCH 2/7] add test --- packages/backend/test/e2e/timelines.ts | 113 +++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 49256cba34bb..fe35aa4639a3 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -378,6 +378,63 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); + + test.concurrent('自分の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, 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'); + }); + + test.concurrent('相手が自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, 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'); + }); + + test.concurrent('相手が自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('visibility: specified なノートを返信したときのノートが返信元の人と自身に含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id }); + + await waitForPushToTl(); + + const aliceRes = await api('/notes/timeline', {}, alice); + + assert.strictEqual(aliceRes.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(aliceRes.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + + const bobRes = await api('/notes/timeline', {}, bob); + + assert.strictEqual(bobRes.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(bobRes.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + }); }); describe('Local TL', () => { @@ -778,6 +835,38 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }, 1000 * 10); + + test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + 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); + 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'); + }); + + test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + 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); + 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); + }); }); describe('User TL', () => { @@ -938,6 +1027,30 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true); }); + + test.concurrent('自身の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: alice.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + }); + + test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); // TODO: リノートミュート済みユーザーのテスト From a3d6525c479c7eac464875259d26a82028f53298 Mon Sep 17 00:00:00 2001 From: Fairy-Phy Date: Sun, 8 Oct 2023 22:11:20 +0900 Subject: [PATCH 3/7] add CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e655bef8b766..ac176790accf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - Enhance: 依存関係の更新 - Fix: ダイレクト投稿をリノートできてしまう問題を修正 - Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正 +- Fix: ダイレクト投稿がタイムライン上に正常に表示されない問題を修正 ### Client - Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に From 1788cbe255dc1b24851ed6d8ea37a26fdcb93c1b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 11:50:25 +0900 Subject: [PATCH 4/7] Update NoteCreateService.ts --- packages/backend/src/core/NoteCreateService.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index e68372120680..2b8113b5d200 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -870,8 +870,7 @@ export class NoteCreateService implements OnApplicationShutdown { where: { userId: user.id, }, - select: ['userList', 'userListId', 'withReplies'], - relations: ['userList'] + select: ['userListId', 'withReplies'], }); if (note.visibility === 'specified') { @@ -929,7 +928,7 @@ export class NoteCreateService implements OnApplicationShutdown { // ダイレクトのとき、そのリストが対象外のユーザーの場合 if ( note.visibility === 'specified' && - !note.visibleUserIds.some(v => v === userListMembership.userList!.userId) + !note.visibleUserIds.some(v => v === userListMembership.userListUserId) ) continue; // 自分自身以外への返信 From 8bdba1dd3b62404455ddb103ba65705306df4993 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 11:53:32 +0900 Subject: [PATCH 5/7] lint --- packages/backend/src/core/NoteCreateService.ts | 3 +-- packages/shared/.eslintrc.js | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 2b8113b5d200..94171f14eb5a 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -893,8 +893,7 @@ export class NoteCreateService implements OnApplicationShutdown { 'note', note.id); } } - } - else { + } else { // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする for (const following of followings) { // 自分自身以外への返信 diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 1ecad7ab759d..c578894f60f8 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -38,6 +38,9 @@ module.exports = { 'before': true, 'after': true, }], + 'brace-style': ['error', '1tbs', { + 'allowSingleLine': true, + }], 'padded-blocks': ['error', 'never'], /* TODO: path aliasを使わないとwarnする 'no-restricted-imports': ['warn', { From 400e8d71b7a981c28b29fe9bae9e53489200ea54 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 12:34:00 +0900 Subject: [PATCH 6/7] :v: --- .../backend/src/core/NoteCreateService.ts | 92 ++++++++----------- packages/backend/test/e2e/timelines.ts | 80 ++++++++++------ 2 files changed, 90 insertions(+), 82 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 94171f14eb5a..277875a19f18 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -857,72 +857,54 @@ export class NoteCreateService implements OnApplicationShutdown { } } else { // TODO: キャッシュ? - const followings = await this.followingsRepository.find({ - where: { - followeeId: user.id, - followerHost: IsNull(), - isFollowerHibernated: false, - }, - select: ['followerId', 'withReplies'], - }); - - const userListMemberships = await this.userListMembershipsRepository.find({ - where: { - userId: user.id, - }, - select: ['userListId', 'withReplies'], - }); + // eslint-disable-next-line prefer-const + let [followings, userListMemberships] = await Promise.all([ + this.followingsRepository.find({ + where: { + followeeId: user.id, + followerHost: IsNull(), + isFollowerHibernated: false, + }, + select: ['followerId', 'withReplies'], + }), + this.userListMembershipsRepository.find({ + where: { + userId: user.id, + }, + select: ['userListId', 'userListUserId', 'withReplies'], + }), + ]); + + if (note.visibility === 'followers') { + // TODO: 重そうだから何とかしたい Set 使う? + userListMemberships = userListMemberships.filter(x => followings.some(f => f.followerId === x.userListUserId)); + } - if (note.visibility === 'specified') { + // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする + for (const following of followings) { // 基本的にvisibleUserIdsには自身のidが含まれている前提であること - for (const userId of note.visibleUserIds) { - // 自分自身と返信元の人以外 - if (note.replyId && note.userId !== userId && note.replyUserId !== userId) continue; + if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; - redisPipeline.xadd( - `homeTimeline:${userId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - - if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${userId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); - } + // 自分自身以外への返信 + if (note.replyId && note.replyUserId !== note.userId) { + if (!following.withReplies) continue; } - } else { - // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする - for (const following of followings) { - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { - if (!following.withReplies) continue; - } + redisPipeline.xadd( + `homeTimeline:${following.followerId}`, + 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), + '*', + 'note', note.id); + + if (note.fileIds.length > 0) { redisPipeline.xadd( - `homeTimeline:${following.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), + `homeTimelineWithFiles:${following.followerId}`, + 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), '*', 'note', note.id); - - if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${following.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); - } } } - // TODO - //if (note.visibility === 'followers') { - // // TODO: 重そうだから何とかしたい Set 使う? - // userLists = userLists.filter(x => followings.some(f => f.followerId === x.userListUserId)); - //} - for (const userListMembership of userListMemberships) { // ダイレクトのとき、そのリストが対象外のユーザーの場合 if ( diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 35fe7f5da8e6..05209c902443 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +// How to run: +// pnpm jest -- e2e/timelines.ts + process.env.NODE_ENV = 'test'; process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true'; @@ -392,9 +395,11 @@ describe('Timelines', () => { assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); }); - test.concurrent('相手が自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { + 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); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); await waitForPushToTl(); @@ -405,9 +410,23 @@ describe('Timelines', () => { assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); }); - test.concurrent('相手が自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { + test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => 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); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); await waitForPushToTl(); @@ -417,7 +436,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); - test.concurrent('visibility: specified なノートを返信したときのノートが返信元の人と自身に含まれる', async () => { + test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); @@ -425,15 +444,40 @@ describe('Timelines', () => { await waitForPushToTl(); - const aliceRes = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', {}, 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'); + }); + + /* TODO + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); - assert.strictEqual(aliceRes.body.some((note: any) => note.id === aliceNote.id), true); - assert.strictEqual(aliceRes.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + 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'); + }); + */ - const bobRes = await api('/notes/timeline', {}, bob); + // ↑の挙動が理想だけど実装が面倒かも + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); - assert.strictEqual(bobRes.body.some((note: any) => note.id === aliceNote.id), true); - assert.strictEqual(bobRes.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); }); @@ -687,7 +731,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); - /* 未実装 test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -702,23 +745,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); - */ - - test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれるが隠される', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - 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); - 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, null); - }); test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); From beafb271e23aa6a4de57040b81b247ef3b92d8ef Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 12:36:07 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E5=89=8D=E3=81=AE=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=81=8B=E3=82=89=E7=99=BA=E7=94=9F?= =?UTF-8?q?=E3=81=97=E3=81=9F=E5=95=8F=E9=A1=8C=E3=81=A7=E3=81=AF=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=81=9F=E3=82=81=E4=B8=8D=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac176790accf..e655bef8b766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,6 @@ - Enhance: 依存関係の更新 - Fix: ダイレクト投稿をリノートできてしまう問題を修正 - Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正 -- Fix: ダイレクト投稿がタイムライン上に正常に表示されない問題を修正 ### Client - Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に