wip
This commit is contained in:
parent
0267197339
commit
ab61baa3ea
|
@ -225,7 +225,7 @@ type UndefinedAsNullAll<T> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface InternalEventTypes {
|
export interface InternalEventTypes {
|
||||||
userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; };
|
userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; } | { id: MiUser['id']; isRemoteSuspended: MiUser['isRemoteSuspended']; };
|
||||||
userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
|
userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
|
||||||
userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
|
userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
|
||||||
remoteUserUpdated: { id: MiUser['id']; };
|
remoteUserUpdated: { id: MiUser['id']; };
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js';
|
import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiRemoteUser, MiUser } from '@/models/User.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
@ -45,11 +45,20 @@ export class UserSuspendService {
|
||||||
});
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await this.postSuspend(user).catch((e: any) => {});
|
await this.postSuspend(user, false).catch((e: any) => { });
|
||||||
await this.suspendFollowings(user).catch((e: any) => {});
|
await this.suspendFollowings(user).catch((e: any) => { });
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async suspendFromRemote(user: { id: MiRemoteUser['id']; host: MiRemoteUser['host'] }): Promise<void> {
|
||||||
|
await this.usersRepository.update(user.id, {
|
||||||
|
isRemoteSuspended: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.postSuspend(user, true);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async unsuspend(user: MiUser, moderator: MiUser): Promise<void> {
|
public async unsuspend(user: MiUser, moderator: MiUser): Promise<void> {
|
||||||
await this.usersRepository.update(user.id, {
|
await this.usersRepository.update(user.id, {
|
||||||
|
@ -63,14 +72,26 @@ export class UserSuspendService {
|
||||||
});
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await this.postUnsuspend(user).catch((e: any) => {});
|
await this.postUnsuspend(user, false).catch((e: any) => { });
|
||||||
await this.restoreFollowings(user).catch((e: any) => {});
|
await this.restoreFollowings(user).catch((e: any) => { });
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async postSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise<void> {
|
public async unsuspendFromRemote(user: { id: MiRemoteUser['id']; host: MiRemoteUser['host'] }): Promise<void> {
|
||||||
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
|
await this.usersRepository.update(user.id, {
|
||||||
|
isRemoteSuspended: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.postUnsuspend(user, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async postSuspend(user: { id: MiUser['id']; host: MiUser['host'] }, isFromRemote: boolean): Promise<void> {
|
||||||
|
this.globalEventService.publishInternalEvent(
|
||||||
|
'userChangeSuspendedState',
|
||||||
|
isFromRemote ? { id: user.id, isRemoteSuspended: true } : { id: user.id, isSuspended: true }
|
||||||
|
);
|
||||||
|
|
||||||
this.followRequestsRepository.delete({
|
this.followRequestsRepository.delete({
|
||||||
followeeId: user.id,
|
followeeId: user.id,
|
||||||
|
@ -85,8 +106,11 @@ export class UserSuspendService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async postUnsuspend(user: MiUser): Promise<void> {
|
private async postUnsuspend(user: { id: MiUser['id']; host: MiUser['host'] }, isFromRemote: boolean): Promise<void> {
|
||||||
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
|
this.globalEventService.publishInternalEvent(
|
||||||
|
'userChangeSuspendedState',
|
||||||
|
isFromRemote ? { id: user.id, isRemoteSuspended: false } : { id: user.id, isSuspended: false }
|
||||||
|
);
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(user)) {
|
if (this.userEntityService.isLocalUser(user)) {
|
||||||
this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id);
|
this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id);
|
||||||
|
@ -94,7 +118,7 @@ export class UserSuspendService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async suspendFollowings(follower: MiUser) {
|
private async suspendFollowings(follower: { id: MiUser['id'] }) {
|
||||||
await this.followingsRepository.update(
|
await this.followingsRepository.update(
|
||||||
{
|
{
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
|
@ -106,7 +130,18 @@ export class UserSuspendService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async restoreFollowings(follower: MiUser) {
|
private async restoreFollowings(_follower: { id: MiUser['id'] }) {
|
||||||
|
// 最新の情報を取得
|
||||||
|
const follower = await this.usersRepository.findOneBy({ id: _follower.id });
|
||||||
|
if (follower == null) {
|
||||||
|
// ユーザーが削除されている場合は何もしないでおく
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (follower.isSuspended || follower.isRemoteSuspended) {
|
||||||
|
// フォロー関係を復元しない
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// フォロー関係を復元(isFollowerSuspended: false)に変更
|
// フォロー関係を復元(isFollowerSuspended: false)に変更
|
||||||
await this.followingsRepository.update(
|
await this.followingsRepository.update(
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,6 +48,7 @@ import type { ApLoggerService } from '../ApLoggerService.js';
|
||||||
|
|
||||||
import type { ApImageService } from './ApImageService.js';
|
import type { ApImageService } from './ApImageService.js';
|
||||||
import type { IActor, ICollection, IObject, IOrderedCollection } from '../type.js';
|
import type { IActor, ICollection, IObject, IOrderedCollection } from '../type.js';
|
||||||
|
import { UserSuspendService } from '@/core/UserSuspendService.js';
|
||||||
|
|
||||||
const nameLength = 128;
|
const nameLength = 128;
|
||||||
const summaryLength = 2048;
|
const summaryLength = 2048;
|
||||||
|
@ -74,6 +75,7 @@ export class ApPersonService implements OnModuleInit {
|
||||||
private instanceChart: InstanceChart;
|
private instanceChart: InstanceChart;
|
||||||
private apLoggerService: ApLoggerService;
|
private apLoggerService: ApLoggerService;
|
||||||
private accountMoveService: AccountMoveService;
|
private accountMoveService: AccountMoveService;
|
||||||
|
private userSuspendService: UserSuspendService;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -126,6 +128,7 @@ export class ApPersonService implements OnModuleInit {
|
||||||
this.instanceChart = this.moduleRef.get('InstanceChart');
|
this.instanceChart = this.moduleRef.get('InstanceChart');
|
||||||
this.apLoggerService = this.moduleRef.get('ApLoggerService');
|
this.apLoggerService = this.moduleRef.get('ApLoggerService');
|
||||||
this.accountMoveService = this.moduleRef.get('AccountMoveService');
|
this.accountMoveService = this.moduleRef.get('AccountMoveService');
|
||||||
|
this.userSuspendService = this.moduleRef.get('UserSuspendService');
|
||||||
this.logger = this.apLoggerService.logger;
|
this.logger = this.apLoggerService.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +603,17 @@ export class ApPersonService implements OnModuleInit {
|
||||||
return 'skip';
|
return 'skip';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region suspend
|
||||||
|
if (exist.isRemoteSuspended === false && person.suspended === true) {
|
||||||
|
// リモートサーバーでアカウントが凍結された
|
||||||
|
this.userSuspendService.suspendFromRemote({ id: exist.id, host: exist.host });
|
||||||
|
}
|
||||||
|
if (exist.isRemoteSuspended === true && person.suspended === false) {
|
||||||
|
// リモートサーバーでアカウントが解凍された
|
||||||
|
this.userSuspendService.unsuspendFromRemote({ id: exist.id, host: exist.host });
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
if (person.publicKey) {
|
if (person.publicKey) {
|
||||||
await this.userPublickeysRepository.update({ userId: exist.id }, {
|
await this.userPublickeysRepository.update({ userId: exist.id }, {
|
||||||
keyId: person.publicKey.id,
|
keyId: person.publicKey.id,
|
||||||
|
|
|
@ -113,449 +113,4 @@ describe('User Suspension', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Profile', () => {
|
|
||||||
describe('Consistency of profile', () => {
|
|
||||||
let alice: LoginUser;
|
|
||||||
let aliceWatcher: LoginUser;
|
|
||||||
let aliceWatcherInB: LoginUser;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
alice = await createAccount('a.test');
|
|
||||||
[
|
|
||||||
aliceWatcher,
|
|
||||||
aliceWatcherInB,
|
|
||||||
] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Check consistency', async () => {
|
|
||||||
const aliceInA = await aliceWatcher.client.request('users/show', { userId: alice.id });
|
|
||||||
const resolved = await resolveRemoteUser('a.test', aliceInA.id, aliceWatcherInB);
|
|
||||||
const aliceInB = await aliceWatcherInB.client.request('users/show', { userId: resolved.id });
|
|
||||||
|
|
||||||
// console.log(`a.test: ${JSON.stringify(aliceInA, null, '\t')}`);
|
|
||||||
// console.log(`b.test: ${JSON.stringify(aliceInB, null, '\t')}`);
|
|
||||||
|
|
||||||
deepStrictEqualWithExcludedFields(aliceInA, aliceInB, [
|
|
||||||
'id',
|
|
||||||
'host',
|
|
||||||
'avatarUrl',
|
|
||||||
'avatarBlurhash',
|
|
||||||
'instance',
|
|
||||||
'badgeRoles',
|
|
||||||
'url',
|
|
||||||
'uri',
|
|
||||||
'createdAt',
|
|
||||||
'lastFetchedAt',
|
|
||||||
'publicReactions',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ffVisibility is federated', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// NOTE: follow each other
|
|
||||||
await Promise.all([
|
|
||||||
alice.client.request('following/create', { userId: bobInA.id }),
|
|
||||||
bob.client.request('following/create', { userId: aliceInB.id }),
|
|
||||||
]);
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Visibility set public by default', async () => {
|
|
||||||
for (const user of await Promise.all([
|
|
||||||
alice.client.request('users/show', { userId: bobInA.id }),
|
|
||||||
bob.client.request('users/show', { userId: aliceInB.id }),
|
|
||||||
])) {
|
|
||||||
strictEqual(user.followersVisibility, 'public');
|
|
||||||
strictEqual(user.followingVisibility, 'public');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/** FIXME: not working */
|
|
||||||
test.skip('Setting private for followersVisibility is federated', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
alice.client.request('i/update', { followersVisibility: 'private' }),
|
|
||||||
bob.client.request('i/update', { followersVisibility: 'private' }),
|
|
||||||
]);
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
for (const user of await Promise.all([
|
|
||||||
alice.client.request('users/show', { userId: bobInA.id }),
|
|
||||||
bob.client.request('users/show', { userId: aliceInB.id }),
|
|
||||||
])) {
|
|
||||||
strictEqual(user.followersVisibility, 'private');
|
|
||||||
strictEqual(user.followingVisibility, 'public');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.skip('Setting private for followingVisibility is federated', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
alice.client.request('i/update', { followingVisibility: 'private' }),
|
|
||||||
bob.client.request('i/update', { followingVisibility: 'private' }),
|
|
||||||
]);
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
for (const user of await Promise.all([
|
|
||||||
alice.client.request('users/show', { userId: bobInA.id }),
|
|
||||||
bob.client.request('users/show', { userId: aliceInB.id }),
|
|
||||||
])) {
|
|
||||||
strictEqual(user.followersVisibility, 'private');
|
|
||||||
strictEqual(user.followingVisibility, 'private');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isCat is federated', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Not isCat for default', () => {
|
|
||||||
strictEqual(aliceInB.isCat, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Becoming a cat is sent to their followers', async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
await alice.client.request('i/update', { isCat: true });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const res = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
strictEqual(res.isCat, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Pinning Notes', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
aliceInB = await resolveRemoteUser('a.test', alice.id, bob);
|
|
||||||
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Pinning localOnly Note is not delivered', async () => {
|
|
||||||
const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote;
|
|
||||||
await alice.client.request('i/pin', { noteId: note.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
strictEqual(_aliceInB.pinnedNoteIds.length, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Pinning followers-only Note is not delivered', async () => {
|
|
||||||
const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote;
|
|
||||||
await alice.client.request('i/pin', { noteId: note.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
strictEqual(_aliceInB.pinnedNoteIds.length, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
let pinnedNote: Misskey.entities.Note;
|
|
||||||
|
|
||||||
test('Pinning normal Note is delivered', async () => {
|
|
||||||
pinnedNote = (await alice.client.request('notes/create', { text: 'a' })).createdNote;
|
|
||||||
await alice.client.request('i/pin', { noteId: pinnedNote.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
strictEqual(_aliceInB.pinnedNoteIds.length, 1);
|
|
||||||
const pinnedNoteInB = await resolveRemoteNote('a.test', pinnedNote.id, bob);
|
|
||||||
strictEqual(_aliceInB.pinnedNotes[0].id, pinnedNoteInB.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Unpinning normal Note is delivered', async () => {
|
|
||||||
await alice.client.request('i/unpin', { noteId: pinnedNote.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
strictEqual(_aliceInB.pinnedNoteIds.length, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Follow / Unfollow', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Follow a.test ==> b.test', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await alice.client.request('following/create', { userId: bobInA.id });
|
|
||||||
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Check consistency with `users/following` and `users/followers` endpoints', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
strictEqual(
|
|
||||||
(await alice.client.request('users/following', { userId: alice.id }))
|
|
||||||
.some(v => v.followeeId === bobInA.id),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
strictEqual(
|
|
||||||
(await bob.client.request('users/followers', { userId: bob.id }))
|
|
||||||
.some(v => v.followerId === aliceInB.id),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Unfollow a.test ==> b.test', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await alice.client.request('following/delete', { userId: bobInA.id });
|
|
||||||
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Check consistency with `users/following` and `users/followers` endpoints', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
strictEqual(
|
|
||||||
(await alice.client.request('users/following', { userId: alice.id }))
|
|
||||||
.some(v => v.followeeId === bobInA.id),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
strictEqual(
|
|
||||||
(await bob.client.request('users/followers', { userId: bob.id }))
|
|
||||||
.some(v => v.followerId === aliceInB.id),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Follow requests', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await alice.client.request('i/update', { isLocked: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Send follow request from Bob to Alice and cancel', () => {
|
|
||||||
describe('Bob sends follow request to Alice', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Alice should have a request', async () => {
|
|
||||||
const requests = await alice.client.request('following/requests/list', {});
|
|
||||||
strictEqual(requests.length, 1);
|
|
||||||
strictEqual(requests[0].followee.id, alice.id);
|
|
||||||
strictEqual(requests[0].follower.id, bobInA.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Alice cancels it', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await bob.client.request('following/requests/cancel', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Alice should have no requests', async () => {
|
|
||||||
const requests = await alice.client.request('following/requests/list', {});
|
|
||||||
strictEqual(requests.length, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Send follow request from Bob to Alice and reject', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
await alice.client.request('following/requests/reject', { userId: bobInA.id });
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Bob should have no requests', async () => {
|
|
||||||
await rejects(
|
|
||||||
async () => await bob.client.request('following/requests/cancel', { userId: aliceInB.id }),
|
|
||||||
(err: any) => {
|
|
||||||
strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND');
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Bob doesn\'t follow Alice', async () => {
|
|
||||||
const following = await bob.client.request('users/following', { userId: bob.id });
|
|
||||||
strictEqual(following.length, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Send follow request from Bob to Alice and accept', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
await alice.client.request('following/requests/accept', { userId: bobInA.id });
|
|
||||||
await sleep();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Bob follows Alice', async () => {
|
|
||||||
const following = await bob.client.request('users/following', { userId: bob.id });
|
|
||||||
strictEqual(following.length, 1);
|
|
||||||
strictEqual(following[0].followeeId, aliceInB.id);
|
|
||||||
strictEqual(following[0].followerId, bob.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Deletion', () => {
|
|
||||||
describe('Check Delete consistency', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Bob follows Alice, and Alice deleted themself', async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const followers = await alice.client.request('users/followers', { userId: alice.id });
|
|
||||||
strictEqual(followers.length, 1); // followed by Bob
|
|
||||||
|
|
||||||
await alice.client.request('i/delete-account', { password: alice.password });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const following = await bob.client.request('users/following', { userId: bob.id });
|
|
||||||
strictEqual(following.length, 0); // no following relation
|
|
||||||
|
|
||||||
await rejects(
|
|
||||||
async () => await bob.client.request('following/create', { userId: aliceInB.id }),
|
|
||||||
(err: any) => {
|
|
||||||
strictEqual(err.code, 'NO_SUCH_USER');
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Deletion of remote user for moderation', () => {
|
|
||||||
let alice: LoginUser, bob: LoginUser;
|
|
||||||
let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
[alice, bob] = await Promise.all([
|
|
||||||
createAccount('a.test'),
|
|
||||||
createAccount('b.test'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[bobInA, aliceInB] = await Promise.all([
|
|
||||||
resolveRemoteUser('b.test', bob.id, alice),
|
|
||||||
resolveRemoteUser('a.test', alice.id, bob),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Bob follows Alice, then Alice gets deleted in B server', async () => {
|
|
||||||
await bob.client.request('following/create', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const followers = await alice.client.request('users/followers', { userId: alice.id });
|
|
||||||
strictEqual(followers.length, 1); // followed by Bob
|
|
||||||
|
|
||||||
await bAdmin.client.request('admin/delete-account', { userId: aliceInB.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME: remote account is not deleted!
|
|
||||||
* @see https://github.com/misskey-dev/misskey/issues/14728
|
|
||||||
*/
|
|
||||||
const deletedAlice = await bob.client.request('users/show', { userId: aliceInB.id });
|
|
||||||
assert(deletedAlice.id, aliceInB.id);
|
|
||||||
|
|
||||||
// TODO: why still following relation?
|
|
||||||
const following = await bob.client.request('users/following', { userId: bob.id });
|
|
||||||
strictEqual(following.length, 1);
|
|
||||||
await rejects(
|
|
||||||
async () => await bob.client.request('following/create', { userId: aliceInB.id }),
|
|
||||||
(err: any) => {
|
|
||||||
strictEqual(err.code, 'ALREADY_FOLLOWING');
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Alice tries to follow Bob, but it is not processed', async () => {
|
|
||||||
await alice.client.request('following/create', { userId: bobInA.id });
|
|
||||||
await sleep();
|
|
||||||
|
|
||||||
const following = await alice.client.request('users/following', { userId: alice.id });
|
|
||||||
strictEqual(following.length, 0); // Not following Bob because B server doesn't return Accept
|
|
||||||
|
|
||||||
const followers = await bob.client.request('users/followers', { userId: bob.id });
|
|
||||||
strictEqual(followers.length, 0); // Alice's Follow is not processed
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -400,7 +400,7 @@ describe('UserSuspendService', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('remote user suspension', () => {
|
describe('suspension for remote user', () => {
|
||||||
test('should suspend remote user without AP delivery', async () => {
|
test('should suspend remote user without AP delivery', async () => {
|
||||||
const remoteUser = await createUser({ host: genHost() });
|
const remoteUser = await createUser({ host: genHost() });
|
||||||
const moderator = await createUser();
|
const moderator = await createUser();
|
||||||
|
@ -422,9 +422,7 @@ describe('UserSuspendService', () => {
|
||||||
// ActivityPub配信が呼ばれていないことを確認
|
// ActivityPub配信が呼ばれていないことを確認
|
||||||
expect(queueService.deliver).not.toHaveBeenCalled();
|
expect(queueService.deliver).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('remote user unsuspension', () => {
|
|
||||||
test('should unsuspend remote user without AP delivery', async () => {
|
test('should unsuspend remote user without AP delivery', async () => {
|
||||||
const remoteUser = await createUser({ host: genHost(), isSuspended: true });
|
const remoteUser = await createUser({ host: genHost(), isSuspended: true });
|
||||||
const moderator = await createUser();
|
const moderator = await createUser();
|
||||||
|
@ -448,4 +446,36 @@ describe('UserSuspendService', () => {
|
||||||
expect(queueService.deliver).not.toHaveBeenCalled();
|
expect(queueService.deliver).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('suspension from remote', () => {
|
||||||
|
test('should suspend remote user and post suspend event', async () => {
|
||||||
|
const remoteUser = { id: secureRndstr(16), host: genHost() };
|
||||||
|
await userSuspendService.suspendFromRemote(remoteUser);
|
||||||
|
|
||||||
|
// ユーザーがリモート凍結されているかチェック
|
||||||
|
const suspendedUser = await usersRepository.findOneBy({ id: remoteUser.id });
|
||||||
|
expect(suspendedUser?.isRemoteSuspended).toBe(true);
|
||||||
|
|
||||||
|
// イベントが発行されているかチェック
|
||||||
|
expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith(
|
||||||
|
'userChangeSuspendedState',
|
||||||
|
{ id: remoteUser.id, isRemoteSuspended: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should unsuspend remote user and post unsuspend event', async () => {
|
||||||
|
const remoteUser = { id: secureRndstr(16), host: genHost() };
|
||||||
|
await userSuspendService.unsuspendFromRemote(remoteUser);
|
||||||
|
|
||||||
|
// ユーザーのリモート凍結が解除されているかチェック
|
||||||
|
const unsuspendedUser = await usersRepository.findOneBy({ id: remoteUser.id });
|
||||||
|
expect(unsuspendedUser?.isRemoteSuspended).toBe(false);
|
||||||
|
|
||||||
|
// イベントが発行されているかチェック
|
||||||
|
expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith(
|
||||||
|
'userChangeSuspendedState',
|
||||||
|
{ id: remoteUser.id, isRemoteSuspended: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue