From eee0aba8afb588fd64f36064727aa5e78b59bb8d Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 22:18:11 +0900 Subject: [PATCH] cherry pick --- packages/backend/test/e2e/timelines.ts | 289 ++++++++++++++++++++----- 1 file changed, 229 insertions(+), 60 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 2c53dfff2e..68a0afe8ff 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -9,9 +9,9 @@ import * as assert from 'assert'; import { setTimeout } from 'node:timers/promises'; import { Redis } from 'ioredis'; -import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; +import { SignupResponse, Note, UserList } from 'misskey-js/entities.js'; +import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js'; import { loadConfig } from '@/config.js'; -import { SignupResponse, Role } from 'misskey-js/entities.js'; function genHost() { return randomString() + '.example.com'; @@ -653,26 +653,86 @@ describe('Timelines', () => { assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); - await setTimeout(250); + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); - const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); - 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('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); + }); + + describe('凍結(リモート)', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + }); }); }); @@ -911,25 +971,42 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); + }); - await setTimeout(250); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); - 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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + 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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + 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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); }); }); @@ -1151,25 +1228,97 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + /* + * bob = 未フォローのローカルユーザー (凍結対象でない) + * carol = 未フォローのローカルユーザー (凍結対象) + * dave = フォローしているローカルユーザー (凍結対象) + */ + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - await waitForPushToTl(); + await api('following/create', { userId: dave.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + daveNote = await post(dave, { text: 'hello' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); - await setTimeout(250); + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: dave.id }, root); + await setTimeout(250); + }); - const res = await api('notes/hybrid-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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/hybrid-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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: dave.id }, root); + await setTimeout(250); + + const res = await api('notes/hybrid-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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), true); + }); + }); + + describe('凍結 (リモート)', () => { + /* + * carol = 未フォローのリモートユーザー (凍結対象) + * elle = フォローしているリモートユーザー (凍結対象) + */ + let alice: SignupResponse, carol: SignupResponse, elle: SignupResponse; + let aliceNote: Note, carolNote: Note, elleNote: Note; + + beforeAll(async () => { + [alice, carol, elle] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: elle.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + elleNote = await post(elle, { text: 'hi there' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: elle.id }, root); + await setTimeout(250); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: elle.id }, root); + await setTimeout(250); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), true); + }); }); }); @@ -1417,26 +1566,46 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + let list: UserList; - 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); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('admin/suspend-user', { userId: carol.id }, root); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await setTimeout(250); + await waitForPushToTl(); - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - assert.strictEqual(res.body.length, 1); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); }); });