Merge remote-tracking branch 'misskey/master' into feature/misskey-2024.07
This commit is contained in:
commit
cfa9b852df
585 changed files with 23423 additions and 9623 deletions
343
packages/backend/test/unit/AbuseReportNotificationService.ts
Normal file
343
packages/backend/test/unit/AbuseReportNotificationService.ts
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
|
||||
import {
|
||||
AbuseReportNotificationRecipientRepository,
|
||||
MiAbuseReportNotificationRecipient,
|
||||
MiSystemWebhook,
|
||||
MiUser,
|
||||
SystemWebhooksRepository,
|
||||
UserProfilesRepository,
|
||||
UsersRepository,
|
||||
} from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { EmailService } from '@/core/EmailService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { randomString } from '../utils.js';
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
describe('AbuseReportNotificationService', () => {
|
||||
let app: TestingModule;
|
||||
let service: AbuseReportNotificationService;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let usersRepository: UsersRepository;
|
||||
let userProfilesRepository: UserProfilesRepository;
|
||||
let systemWebhooksRepository: SystemWebhooksRepository;
|
||||
let abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository;
|
||||
let idService: IdService;
|
||||
let roleService: jest.Mocked<RoleService>;
|
||||
let emailService: jest.Mocked<EmailService>;
|
||||
let webhookService: jest.Mocked<SystemWebhookService>;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let root: MiUser;
|
||||
let alice: MiUser;
|
||||
let bob: MiUser;
|
||||
let systemWebhook1: MiSystemWebhook;
|
||||
let systemWebhook2: MiSystemWebhook;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
const user = await usersRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
await userProfilesRepository.insert({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function createWebhook(data: Partial<MiSystemWebhook> = {}) {
|
||||
return systemWebhooksRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
...data,
|
||||
})
|
||||
.then(x => systemWebhooksRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
async function createRecipient(data: Partial<MiAbuseReportNotificationRecipient> = {}) {
|
||||
return abuseReportNotificationRecipientRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
...data,
|
||||
})
|
||||
.then(x => abuseReportNotificationRecipientRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
AbuseReportNotificationService,
|
||||
IdService,
|
||||
{
|
||||
provide: RoleService, useFactory: () => ({ getModeratorIds: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: MetaService, useFactory: () => ({ fetch: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
|
||||
},
|
||||
{
|
||||
provide: GlobalEventService, useFactory: () => ({ publishAdminStream: jest.fn() }),
|
||||
},
|
||||
],
|
||||
})
|
||||
.compile();
|
||||
|
||||
usersRepository = app.get(DI.usersRepository);
|
||||
userProfilesRepository = app.get(DI.userProfilesRepository);
|
||||
systemWebhooksRepository = app.get(DI.systemWebhooksRepository);
|
||||
abuseReportNotificationRecipientRepository = app.get(DI.abuseReportNotificationRecipientRepository);
|
||||
|
||||
service = app.get(AbuseReportNotificationService);
|
||||
idService = app.get(IdService);
|
||||
roleService = app.get(RoleService) as jest.Mocked<RoleService>;
|
||||
emailService = app.get<EmailService>(EmailService) as jest.Mocked<EmailService>;
|
||||
webhookService = app.get<SystemWebhookService>(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
|
||||
|
||||
app.enableShutdownHooks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
|
||||
alice = await createUser({ username: 'alice', usernameLower: 'alice', isRoot: false });
|
||||
bob = await createUser({ username: 'bob', usernameLower: 'bob', isRoot: false });
|
||||
systemWebhook1 = await createWebhook();
|
||||
systemWebhook2 = await createWebhook();
|
||||
|
||||
roleService.getModeratorIds.mockResolvedValue([root.id, alice.id, bob.id]);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
emailService.sendEmail.mockClear();
|
||||
webhookService.enqueueSystemWebhook.mockClear();
|
||||
|
||||
await usersRepository.delete({});
|
||||
await userProfilesRepository.delete({});
|
||||
await systemWebhooksRepository.delete({});
|
||||
await abuseReportNotificationRecipientRepository.delete({});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
describe('createRecipient', () => {
|
||||
test('作成成功1', async () => {
|
||||
const params = {
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
method: 'email' as RecipientMethod,
|
||||
userId: alice.id,
|
||||
systemWebhookId: null,
|
||||
};
|
||||
|
||||
const recipient1 = await service.createRecipient(params, root);
|
||||
expect(recipient1).toMatchObject(params);
|
||||
});
|
||||
|
||||
test('作成成功2', async () => {
|
||||
const params = {
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
method: 'webhook' as RecipientMethod,
|
||||
userId: null,
|
||||
systemWebhookId: systemWebhook1.id,
|
||||
};
|
||||
|
||||
const recipient1 = await service.createRecipient(params, root);
|
||||
expect(recipient1).toMatchObject(params);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateRecipient', () => {
|
||||
test('更新成功1', async () => {
|
||||
const recipient1 = await createRecipient({
|
||||
method: 'email',
|
||||
userId: alice.id,
|
||||
});
|
||||
|
||||
const params = {
|
||||
id: recipient1.id,
|
||||
isActive: false,
|
||||
name: randomString(),
|
||||
method: 'email' as RecipientMethod,
|
||||
userId: bob.id,
|
||||
systemWebhookId: null,
|
||||
};
|
||||
|
||||
const recipient2 = await service.updateRecipient(params, root);
|
||||
expect(recipient2).toMatchObject(params);
|
||||
});
|
||||
|
||||
test('更新成功2', async () => {
|
||||
const recipient1 = await createRecipient({
|
||||
method: 'webhook',
|
||||
systemWebhookId: systemWebhook1.id,
|
||||
});
|
||||
|
||||
const params = {
|
||||
id: recipient1.id,
|
||||
isActive: false,
|
||||
name: randomString(),
|
||||
method: 'webhook' as RecipientMethod,
|
||||
userId: null,
|
||||
systemWebhookId: systemWebhook2.id,
|
||||
};
|
||||
|
||||
const recipient2 = await service.updateRecipient(params, root);
|
||||
expect(recipient2).toMatchObject(params);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteRecipient', () => {
|
||||
test('削除成功1', async () => {
|
||||
const recipient1 = await createRecipient({
|
||||
method: 'email',
|
||||
userId: alice.id,
|
||||
});
|
||||
|
||||
await service.deleteRecipient(recipient1.id, root);
|
||||
|
||||
await expect(abuseReportNotificationRecipientRepository.findOneBy({ id: recipient1.id })).resolves.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchRecipients', () => {
|
||||
async function create() {
|
||||
const recipient1 = await createRecipient({
|
||||
method: 'email',
|
||||
userId: alice.id,
|
||||
});
|
||||
const recipient2 = await createRecipient({
|
||||
method: 'email',
|
||||
userId: bob.id,
|
||||
});
|
||||
|
||||
const recipient3 = await createRecipient({
|
||||
method: 'webhook',
|
||||
systemWebhookId: systemWebhook1.id,
|
||||
});
|
||||
const recipient4 = await createRecipient({
|
||||
method: 'webhook',
|
||||
systemWebhookId: systemWebhook2.id,
|
||||
});
|
||||
|
||||
return [recipient1, recipient2, recipient3, recipient4];
|
||||
}
|
||||
|
||||
test('フィルタなし', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({});
|
||||
expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
|
||||
});
|
||||
|
||||
test('フィルタなし(非モデレータは除外される)', async () => {
|
||||
roleService.getModeratorIds.mockClear();
|
||||
roleService.getModeratorIds.mockResolvedValue([root.id, bob.id]);
|
||||
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({});
|
||||
// aliceはモデレータではないので除外される
|
||||
expect(recipients).toEqual([recipient2, recipient3, recipient4]);
|
||||
});
|
||||
|
||||
test('フィルタなし(非モデレータでも除外されないオプション設定)', async () => {
|
||||
roleService.getModeratorIds.mockClear();
|
||||
roleService.getModeratorIds.mockResolvedValue([root.id, bob.id]);
|
||||
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({}, { removeUnauthorized: false });
|
||||
expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
|
||||
});
|
||||
|
||||
test('emailのみ', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ method: ['email'] });
|
||||
expect(recipients).toEqual([recipient1, recipient2]);
|
||||
});
|
||||
|
||||
test('webhookのみ', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ method: ['webhook'] });
|
||||
expect(recipients).toEqual([recipient3, recipient4]);
|
||||
});
|
||||
|
||||
test('すべて', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ method: ['email', 'webhook'] });
|
||||
expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
|
||||
});
|
||||
|
||||
test('ID指定', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id] });
|
||||
expect(recipients).toEqual([recipient1, recipient3]);
|
||||
});
|
||||
|
||||
test('ID指定(method=emailではないIDが混ざりこまない)', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id], method: ['email'] });
|
||||
expect(recipients).toEqual([recipient1]);
|
||||
});
|
||||
|
||||
test('ID指定(method=webhookではないIDが混ざりこまない)', async () => {
|
||||
const [recipient1, recipient2, recipient3, recipient4] = await create();
|
||||
|
||||
const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id], method: ['webhook'] });
|
||||
expect(recipients).toEqual([recipient3]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -23,10 +23,10 @@ describe('ApMfmService', () => {
|
|||
|
||||
describe('getNoteHtml', () => {
|
||||
test('Do not provide _misskey_content for simple text', () => {
|
||||
const note: MiNote = {
|
||||
const note = {
|
||||
text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
|
||||
mentionedRemoteUsers: '[]',
|
||||
} as any;
|
||||
};
|
||||
|
||||
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||
|
||||
|
@ -35,10 +35,10 @@ describe('ApMfmService', () => {
|
|||
});
|
||||
|
||||
test('Provide _misskey_content for MFM', () => {
|
||||
const note: MiNote = {
|
||||
const note = {
|
||||
text: '$[tada foo]',
|
||||
mentionedRemoteUsers: '[]',
|
||||
} as any;
|
||||
};
|
||||
|
||||
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ModuleMocker } from 'jest-mock';
|
|||
import { Test } from '@nestjs/testing';
|
||||
import { afterAll, beforeAll, describe, test } from '@jest/globals';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { FileInfoService } from '@/core/FileInfoService.js';
|
||||
import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
|
||||
//import { DI } from '@/di-symbols.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
|
@ -27,6 +27,15 @@ const moduleMocker = new ModuleMocker(global);
|
|||
describe('FileInfoService', () => {
|
||||
let app: TestingModule;
|
||||
let fileInfoService: FileInfoService;
|
||||
const strip = (fileInfo: FileInfo): Omit<Partial<FileInfo>, 'warnings' | 'blurhash' | 'sensitive' | 'porn'> => {
|
||||
const fi: Partial<FileInfo> = fileInfo;
|
||||
delete fi.warnings;
|
||||
delete fi.sensitive;
|
||||
delete fi.blurhash;
|
||||
delete fi.porn;
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
|
@ -58,11 +67,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('Empty file', async () => {
|
||||
const path = `${resources}/emptyfile`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 0,
|
||||
md5: 'd41d8cd98f00b204e9800998ecf8427e',
|
||||
|
@ -78,32 +83,24 @@ describe('FileInfoService', () => {
|
|||
|
||||
describe('IMAGE', () => {
|
||||
test('Generic JPEG', async () => {
|
||||
const path = `${resources}/Lenna.jpg`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const path = `${resources}/192.jpg`;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 25360,
|
||||
md5: '091b3f259662aa31e2ffef4519951168',
|
||||
size: 5131,
|
||||
md5: '8c9ed0677dd2b8f9f7472c3af247e5e3',
|
||||
type: {
|
||||
mime: 'image/jpeg',
|
||||
ext: 'jpg',
|
||||
},
|
||||
width: 512,
|
||||
height: 512,
|
||||
width: 192,
|
||||
height: 192,
|
||||
orientation: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
test('Generic APNG', async () => {
|
||||
const path = `${resources}/anime.png`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 1868,
|
||||
md5: '08189c607bea3b952704676bb3c979e0',
|
||||
|
@ -119,11 +116,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('Generic AGIF', async () => {
|
||||
const path = `${resources}/anime.gif`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 2248,
|
||||
md5: '32c47a11555675d9267aee1a86571e7e',
|
||||
|
@ -139,11 +132,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('PNG with alpha', async () => {
|
||||
const path = `${resources}/with-alpha.png`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 3772,
|
||||
md5: 'f73535c3e1e27508885b69b10cf6e991',
|
||||
|
@ -159,11 +148,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('Generic SVG', async () => {
|
||||
const path = `${resources}/image.svg`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 505,
|
||||
md5: 'b6f52b4b021e7b92cdd04509c7267965',
|
||||
|
@ -180,11 +165,7 @@ describe('FileInfoService', () => {
|
|||
test('SVG with XML definition', async () => {
|
||||
// https://github.com/misskey-dev/misskey/issues/4413
|
||||
const path = `${resources}/with-xml-def.svg`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 544,
|
||||
md5: '4b7a346cde9ccbeb267e812567e33397',
|
||||
|
@ -200,11 +181,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('Dimension limit', async () => {
|
||||
const path = `${resources}/25000x25000.png`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 75933,
|
||||
md5: '268c5dde99e17cf8fe09f1ab3f97df56',
|
||||
|
@ -220,11 +197,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('Rotate JPEG', async () => {
|
||||
const path = `${resources}/rotate.jpg`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 12624,
|
||||
md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
|
||||
|
@ -242,11 +215,7 @@ describe('FileInfoService', () => {
|
|||
describe('AUDIO', () => {
|
||||
test('MP3', async () => {
|
||||
const path = `${resources}/kick_gaba7.mp3`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
@ -262,11 +231,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('WAV', async () => {
|
||||
const path = `${resources}/kick_gaba7.wav`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
@ -282,11 +247,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('AAC', async () => {
|
||||
const path = `${resources}/kick_gaba7.aac`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
@ -302,11 +263,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('FLAC', async () => {
|
||||
const path = `${resources}/kick_gaba7.flac`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
@ -322,11 +279,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('MPEG-4 AUDIO (M4A)', async () => {
|
||||
const path = `${resources}/kick_gaba7.m4a`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
@ -342,11 +295,7 @@ describe('FileInfoService', () => {
|
|||
|
||||
test('WEBM AUDIO', async () => {
|
||||
const path = `${resources}/kick_gaba7.webm`;
|
||||
const info = await fileInfoService.getFileInfo(path) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
const info = strip(await fileInfoService.getFileInfo(path));
|
||||
delete info.width;
|
||||
delete info.height;
|
||||
delete info.orientation;
|
||||
|
|
|
@ -3,17 +3,23 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { jest } from '@jest/globals';
|
||||
import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as lolex from '@sinonjs/fake-timers';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import type { MiRole, MiUser, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js';
|
||||
import {
|
||||
MiRole,
|
||||
MiRoleAssignment,
|
||||
MiUser,
|
||||
RoleAssignmentsRepository,
|
||||
RolesRepository,
|
||||
UsersRepository,
|
||||
} from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { genAidx } from '@/misc/id/aidx.js';
|
||||
|
@ -23,7 +29,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
|
|||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import { RoleCondFormulaValue } from '@/models/Role.js';
|
||||
import { sleep } from '../utils.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import type { TestingModule } from '@nestjs/testing';
|
||||
import type { MockFunctionMetadata } from 'jest-mock';
|
||||
|
||||
|
@ -39,27 +45,27 @@ describe('RoleService', () => {
|
|||
let notificationService: jest.Mocked<NotificationService>;
|
||||
let clock: lolex.InstalledClock;
|
||||
|
||||
function createUser(data: Partial<MiUser> = {}) {
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
const un = secureRndstr(16);
|
||||
return usersRepository.insert({
|
||||
const x = await usersRepository.insert({
|
||||
id: genAidx(Date.now()),
|
||||
username: un,
|
||||
usernameLower: un,
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
return await usersRepository.findOneByOrFail(x.identifiers[0]);
|
||||
}
|
||||
|
||||
function createRole(data: Partial<MiRole> = {}) {
|
||||
return rolesRepository.insert({
|
||||
async function createRole(data: Partial<MiRole> = {}) {
|
||||
const x = await rolesRepository.insert({
|
||||
id: genAidx(Date.now()),
|
||||
updatedAt: new Date(),
|
||||
lastUsedAt: new Date(),
|
||||
name: '',
|
||||
description: '',
|
||||
...data,
|
||||
})
|
||||
.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
|
||||
});
|
||||
return await rolesRepository.findOneByOrFail(x.identifiers[0]);
|
||||
}
|
||||
|
||||
function createConditionalRole(condFormula: RoleCondFormulaValue, data: Partial<MiRole> = {}) {
|
||||
|
@ -71,6 +77,20 @@ describe('RoleService', () => {
|
|||
});
|
||||
}
|
||||
|
||||
async function assignRole(args: Partial<MiRoleAssignment>) {
|
||||
const id = genAidx(Date.now());
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setDate(expiresAt.getDate() + 1);
|
||||
|
||||
await roleAssignmentsRepository.insert({
|
||||
id,
|
||||
expiresAt,
|
||||
...args,
|
||||
});
|
||||
|
||||
return await roleAssignmentsRepository.findOneByOrFail({ id });
|
||||
}
|
||||
|
||||
function aidx() {
|
||||
return genAidx(Date.now());
|
||||
}
|
||||
|
@ -258,13 +278,103 @@ describe('RoleService', () => {
|
|||
|
||||
// ストリーミング経由で反映されるまでちょっと待つ
|
||||
clock.uninstall();
|
||||
await sleep(100);
|
||||
await setTimeout(100);
|
||||
|
||||
const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
|
||||
expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModeratorIds', () => {
|
||||
test('includeAdmins = false, excludeExpire = false', async () => {
|
||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
||||
]);
|
||||
|
||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||
const role3 = await createRole({ name: 'normal' });
|
||||
|
||||
await Promise.all([
|
||||
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
]);
|
||||
|
||||
const result = await roleService.getModeratorIds(false, false);
|
||||
expect(result).toEqual([modeUser1.id, modeUser2.id]);
|
||||
});
|
||||
|
||||
test('includeAdmins = false, excludeExpire = true', async () => {
|
||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
||||
]);
|
||||
|
||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||
const role3 = await createRole({ name: 'normal' });
|
||||
|
||||
await Promise.all([
|
||||
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
]);
|
||||
|
||||
const result = await roleService.getModeratorIds(false, true);
|
||||
expect(result).toEqual([modeUser1.id]);
|
||||
});
|
||||
|
||||
test('includeAdmins = true, excludeExpire = false', async () => {
|
||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
||||
]);
|
||||
|
||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||
const role3 = await createRole({ name: 'normal' });
|
||||
|
||||
await Promise.all([
|
||||
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
]);
|
||||
|
||||
const result = await roleService.getModeratorIds(true, false);
|
||||
expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id]);
|
||||
});
|
||||
|
||||
test('includeAdmins = true, excludeExpire = true', async () => {
|
||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
||||
]);
|
||||
|
||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||
const role3 = await createRole({ name: 'normal' });
|
||||
|
||||
await Promise.all([
|
||||
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||
]);
|
||||
|
||||
const result = await roleService.getModeratorIds(true, true);
|
||||
expect(result).toEqual([adminUser1.id, modeUser1.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('conditional role', () => {
|
||||
test('~かつ~', async () => {
|
||||
const [user1, user2, user3, user4] = await Promise.all([
|
||||
|
@ -697,7 +807,7 @@ describe('RoleService', () => {
|
|||
await roleService.assign(user.id, role.id);
|
||||
|
||||
clock.uninstall();
|
||||
await sleep(100);
|
||||
await setTimeout(100);
|
||||
|
||||
const assignments = await roleAssignmentsRepository.find({
|
||||
where: {
|
||||
|
@ -725,7 +835,7 @@ describe('RoleService', () => {
|
|||
await roleService.assign(user.id, role.id);
|
||||
|
||||
clock.uninstall();
|
||||
await sleep(100);
|
||||
await setTimeout(100);
|
||||
|
||||
const assignments = await roleAssignmentsRepository.find({
|
||||
where: {
|
||||
|
|
516
packages/backend/test/unit/SystemWebhookService.ts
Normal file
516
packages/backend/test/unit/SystemWebhookService.ts
Normal file
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MiUser } from '@/models/User.js';
|
||||
import { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js';
|
||||
import { SystemWebhooksRepository, UsersRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { randomString } from '../utils.js';
|
||||
|
||||
describe('SystemWebhookService', () => {
|
||||
let app: TestingModule;
|
||||
let service: SystemWebhookService;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let usersRepository: UsersRepository;
|
||||
let systemWebhooksRepository: SystemWebhooksRepository;
|
||||
let idService: IdService;
|
||||
let queueService: jest.Mocked<QueueService>;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
let root: MiUser;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
return await usersRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
async function createWebhook(data: Partial<MiSystemWebhook> = {}) {
|
||||
return systemWebhooksRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
...data,
|
||||
})
|
||||
.then(x => systemWebhooksRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
async function beforeAllImpl() {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
SystemWebhookService,
|
||||
IdService,
|
||||
LoggerService,
|
||||
GlobalEventService,
|
||||
{
|
||||
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
|
||||
},
|
||||
],
|
||||
})
|
||||
.compile();
|
||||
|
||||
usersRepository = app.get(DI.usersRepository);
|
||||
systemWebhooksRepository = app.get(DI.systemWebhooksRepository);
|
||||
|
||||
service = app.get(SystemWebhookService);
|
||||
idService = app.get(IdService);
|
||||
queueService = app.get(QueueService) as jest.Mocked<QueueService>;
|
||||
|
||||
app.enableShutdownHooks();
|
||||
}
|
||||
|
||||
async function afterAllImpl() {
|
||||
await app.close();
|
||||
}
|
||||
|
||||
async function beforeEachImpl() {
|
||||
root = await createUser({ isRoot: true, username: 'root', usernameLower: 'root' });
|
||||
}
|
||||
|
||||
async function afterEachImpl() {
|
||||
await usersRepository.delete({});
|
||||
await systemWebhooksRepository.delete({});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
describe('アプリを毎回作り直す必要のないグループ', () => {
|
||||
beforeAll(beforeAllImpl);
|
||||
afterAll(afterAllImpl);
|
||||
beforeEach(beforeEachImpl);
|
||||
afterEach(afterEachImpl);
|
||||
|
||||
describe('fetchSystemWebhooks', () => {
|
||||
test('フィルタなし', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks();
|
||||
expect(fetchedWebhooks).toEqual([webhook1, webhook2, webhook3, webhook4]);
|
||||
});
|
||||
|
||||
test('activeのみ', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks({ isActive: true });
|
||||
expect(fetchedWebhooks).toEqual([webhook1, webhook3]);
|
||||
});
|
||||
|
||||
test('特定のイベントのみ', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks({ on: ['abuseReport'] });
|
||||
expect(fetchedWebhooks).toEqual([webhook1, webhook2]);
|
||||
});
|
||||
|
||||
test('activeな特定のイベントのみ', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks({ on: ['abuseReport'], isActive: true });
|
||||
expect(fetchedWebhooks).toEqual([webhook1]);
|
||||
});
|
||||
|
||||
test('ID指定', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks({ ids: [webhook1.id, webhook4.id] });
|
||||
expect(fetchedWebhooks).toEqual([webhook1, webhook4]);
|
||||
});
|
||||
|
||||
test('ID指定(他条件とANDになるか見たい)', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
const webhook3 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
const webhook4 = await createWebhook({
|
||||
isActive: false,
|
||||
on: [],
|
||||
});
|
||||
|
||||
const fetchedWebhooks = await service.fetchSystemWebhooks({ ids: [webhook1.id, webhook4.id], isActive: false });
|
||||
expect(fetchedWebhooks).toEqual([webhook4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSystemWebhook', () => {
|
||||
test('作成成功 ', async () => {
|
||||
const params = {
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'] as SystemWebhookEventType[],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
};
|
||||
|
||||
const webhook = await service.createSystemWebhook(params, root);
|
||||
expect(webhook).toMatchObject(params);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSystemWebhook', () => {
|
||||
test('更新成功', async () => {
|
||||
const webhook = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
|
||||
const params = {
|
||||
id: webhook.id,
|
||||
isActive: false,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'] as SystemWebhookEventType[],
|
||||
url: randomString(),
|
||||
secret: randomString(),
|
||||
};
|
||||
|
||||
const updatedWebhook = await service.updateSystemWebhook(params, root);
|
||||
expect(updatedWebhook).toMatchObject(params);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteSystemWebhook', () => {
|
||||
test('削除成功', async () => {
|
||||
const webhook = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
|
||||
await service.deleteSystemWebhook(webhook.id, root);
|
||||
|
||||
await expect(systemWebhooksRepository.findOneBy({ id: webhook.id })).resolves.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('アプリを毎回作り直す必要があるグループ', () => {
|
||||
beforeEach(async () => {
|
||||
await beforeAllImpl();
|
||||
await beforeEachImpl();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await afterEachImpl();
|
||||
await afterAllImpl();
|
||||
});
|
||||
|
||||
describe('enqueueSystemWebhook', () => {
|
||||
test('キューに追加成功', async () => {
|
||||
const webhook = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' });
|
||||
|
||||
expect(queueService.systemWebhookDeliver).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('非アクティブなWebhookはキューに追加されない', async () => {
|
||||
const webhook = await createWebhook({
|
||||
isActive: false,
|
||||
on: ['abuseReport'],
|
||||
});
|
||||
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' });
|
||||
|
||||
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('未許可のイベント種別が渡された場合はWebhookはキューに追加されない', async () => {
|
||||
const webhook1 = await createWebhook({
|
||||
isActive: true,
|
||||
on: [],
|
||||
});
|
||||
const webhook2 = await createWebhook({
|
||||
isActive: true,
|
||||
on: ['abuseReportResolved'],
|
||||
});
|
||||
await service.enqueueSystemWebhook(webhook1.id, 'abuseReport', { foo: 'bar' });
|
||||
await service.enqueueSystemWebhook(webhook2.id, 'abuseReport', { foo: 'bar' });
|
||||
|
||||
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchActiveSystemWebhooks', () => {
|
||||
describe('systemWebhookCreated', () => {
|
||||
test('ActiveなWebhookが追加された時、キャッシュに追加されている', async () => {
|
||||
const webhook = await service.createSystemWebhook(
|
||||
{
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks).toEqual([webhook]);
|
||||
});
|
||||
|
||||
test('NotActiveなWebhookが追加された時、キャッシュに追加されていない', async () => {
|
||||
const webhook = await service.createSystemWebhook(
|
||||
{
|
||||
isActive: false,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('systemWebhookUpdated', () => {
|
||||
test('ActiveなWebhookが編集された時、キャッシュに反映されている', async () => {
|
||||
const id = idService.gen();
|
||||
await createWebhook({ id });
|
||||
// キャッシュ作成
|
||||
const webhook1 = await service.fetchActiveSystemWebhooks();
|
||||
// 読み込まれていることをチェック
|
||||
expect(webhook1.length).toEqual(1);
|
||||
expect(webhook1[0].id).toEqual(id);
|
||||
|
||||
const webhook2 = await service.updateSystemWebhook(
|
||||
{
|
||||
id,
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks).toEqual([webhook2]);
|
||||
});
|
||||
|
||||
test('NotActiveなWebhookが編集された時、キャッシュに追加されない', async () => {
|
||||
const id = idService.gen();
|
||||
await createWebhook({ id, isActive: false });
|
||||
// キャッシュ作成
|
||||
const webhook1 = await service.fetchActiveSystemWebhooks();
|
||||
// 読み込まれていないことをチェック
|
||||
expect(webhook1.length).toEqual(0);
|
||||
|
||||
const webhook2 = await service.updateSystemWebhook(
|
||||
{
|
||||
id,
|
||||
isActive: false,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: 'https://example.com',
|
||||
secret: randomString(),
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('NotActiveなWebhookがActiveにされた時、キャッシュに追加されている', async () => {
|
||||
const id = idService.gen();
|
||||
const baseWebhook = await createWebhook({ id, isActive: false });
|
||||
// キャッシュ作成
|
||||
const webhook1 = await service.fetchActiveSystemWebhooks();
|
||||
// 読み込まれていないことをチェック
|
||||
expect(webhook1.length).toEqual(0);
|
||||
|
||||
const webhook2 = await service.updateSystemWebhook(
|
||||
{
|
||||
...baseWebhook,
|
||||
isActive: true,
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks).toEqual([webhook2]);
|
||||
});
|
||||
|
||||
test('ActiveなWebhookがNotActiveにされた時、キャッシュから削除されている', async () => {
|
||||
const id = idService.gen();
|
||||
const baseWebhook = await createWebhook({ id, isActive: true });
|
||||
// キャッシュ作成
|
||||
const webhook1 = await service.fetchActiveSystemWebhooks();
|
||||
// 読み込まれていることをチェック
|
||||
expect(webhook1.length).toEqual(1);
|
||||
expect(webhook1[0].id).toEqual(id);
|
||||
|
||||
const webhook2 = await service.updateSystemWebhook(
|
||||
{
|
||||
...baseWebhook,
|
||||
isActive: false,
|
||||
},
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('systemWebhookDeleted', () => {
|
||||
test('キャッシュから削除されている', async () => {
|
||||
const id = idService.gen();
|
||||
const baseWebhook = await createWebhook({ id, isActive: true });
|
||||
// キャッシュ作成
|
||||
const webhook1 = await service.fetchActiveSystemWebhooks();
|
||||
// 読み込まれていることをチェック
|
||||
expect(webhook1.length).toEqual(1);
|
||||
expect(webhook1[0].id).toEqual(id);
|
||||
|
||||
const webhook2 = await service.deleteSystemWebhook(
|
||||
id,
|
||||
root,
|
||||
);
|
||||
|
||||
// redisでの配信経由で更新されるのでちょっと待つ
|
||||
await setTimeout(500);
|
||||
|
||||
const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
|
||||
expect(fetchedWebhooks.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
265
packages/backend/test/unit/UserSearchService.ts
Normal file
265
packages/backend/test/unit/UserSearchService.ts
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { describe, jest, test } from '@jest/globals';
|
||||
import { In } from 'typeorm';
|
||||
import { UserSearchService } from '@/core/UserSearchService.js';
|
||||
import { FollowingsRepository, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
describe('UserSearchService', () => {
|
||||
let app: TestingModule;
|
||||
let service: UserSearchService;
|
||||
|
||||
let usersRepository: UsersRepository;
|
||||
let followingsRepository: FollowingsRepository;
|
||||
let idService: IdService;
|
||||
let userProfilesRepository: UserProfilesRepository;
|
||||
|
||||
let root: MiUser;
|
||||
let alice: MiUser;
|
||||
let alyce: MiUser;
|
||||
let alycia: MiUser;
|
||||
let alysha: MiUser;
|
||||
let alyson: MiUser;
|
||||
let alyssa: MiUser;
|
||||
let bob: MiUser;
|
||||
let bobbi: MiUser;
|
||||
let bobbie: MiUser;
|
||||
let bobby: MiUser;
|
||||
|
||||
async function createUser(data: Partial<MiUser> = {}) {
|
||||
const user = await usersRepository
|
||||
.insert({
|
||||
id: idService.gen(),
|
||||
...data,
|
||||
})
|
||||
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
await userProfilesRepository.insert({
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async function createFollowings(follower: MiUser, followees: MiUser[]) {
|
||||
for (const followee of followees) {
|
||||
await followingsRepository.insert({
|
||||
id: idService.gen(),
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function setActive(users: MiUser[]) {
|
||||
for (const user of users) {
|
||||
await usersRepository.update(user.id, {
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function setInactive(users: MiUser[]) {
|
||||
for (const user of users) {
|
||||
await usersRepository.update(user.id, {
|
||||
updatedAt: new Date(0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function setSuspended(users: MiUser[]) {
|
||||
for (const user of users) {
|
||||
await usersRepository.update(user.id, {
|
||||
isSuspended: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
UserSearchService,
|
||||
{
|
||||
provide: UserEntityService, useFactory: jest.fn(() => ({
|
||||
// とりあえずIDが返れば確認が出来るので
|
||||
packMany: (value: any) => value,
|
||||
})),
|
||||
},
|
||||
IdService,
|
||||
],
|
||||
})
|
||||
.compile();
|
||||
|
||||
await app.init();
|
||||
|
||||
usersRepository = app.get(DI.usersRepository);
|
||||
userProfilesRepository = app.get(DI.userProfilesRepository);
|
||||
followingsRepository = app.get(DI.followingsRepository);
|
||||
|
||||
service = app.get(UserSearchService);
|
||||
idService = app.get(IdService);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
|
||||
alice = await createUser({ username: 'Alice', usernameLower: 'alice' });
|
||||
alyce = await createUser({ username: 'Alyce', usernameLower: 'alyce' });
|
||||
alycia = await createUser({ username: 'Alycia', usernameLower: 'alycia' });
|
||||
alysha = await createUser({ username: 'Alysha', usernameLower: 'alysha' });
|
||||
alyson = await createUser({ username: 'Alyson', usernameLower: 'alyson', host: 'example.com' });
|
||||
alyssa = await createUser({ username: 'Alyssa', usernameLower: 'alyssa', host: 'example.com' });
|
||||
bob = await createUser({ username: 'Bob', usernameLower: 'bob' });
|
||||
bobbi = await createUser({ username: 'Bobbi', usernameLower: 'bobbi' });
|
||||
bobbie = await createUser({ username: 'Bobbie', usernameLower: 'bobbie', host: 'example.com' });
|
||||
bobby = await createUser({ username: 'Bobby', usernameLower: 'bobby', host: 'example.com' });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await usersRepository.delete({});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
test('フォロー中のアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setActive([alice, alyce, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setInactive([alycia, alysha, alyson]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
// alycia, alysha, alysonは非アクティブなので後ろに行く
|
||||
expect(result).toEqual([alice, alyce, alyssa, alycia, alysha, alyson].map(x => x.id));
|
||||
});
|
||||
|
||||
test('フォロー中の非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await createFollowings(root, [alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
// alice, alyceはフォローしていないので後ろに行く
|
||||
expect(result).toEqual([alycia, alysha, alyson, alyssa, alice, alyce].map(x => x.id));
|
||||
});
|
||||
|
||||
test('フォローしていないアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setInactive([alice, alyce, alycia]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
// alice, alyce, alyciaは非アクティブなので後ろに行く
|
||||
expect(result).toEqual([alysha, alyson, alyssa, alice, alyce, alycia].map(x => x.id));
|
||||
});
|
||||
|
||||
test('フォローしていない非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
expect(result).toEqual([alice, alyce, alycia, alysha, alyson, alyssa].map(x => x.id));
|
||||
});
|
||||
|
||||
test('フォロー(アクティブ)、フォロー(非アクティブ)、非フォロー(アクティブ)、非フォロー(非アクティブ)混在時の優先順位度確認', async () => {
|
||||
await createFollowings(root, [alyson, alyssa, bob, bobbi, bobbie]);
|
||||
await setActive([root, alyssa, bob, bobbi, alyce, alycia]);
|
||||
await setInactive([alyson, alice, alysha, bobbie, bobby]);
|
||||
|
||||
const result = await service.search(
|
||||
{ },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
// 見る用
|
||||
// const users = await usersRepository.findBy({ id: In(result) }).then(it => new Map(it.map(x => [x.id, x])));
|
||||
// console.log(result.map(x => users.get(x as any)).map(it => it?.username));
|
||||
|
||||
// フォローしててアクティブなので先頭: alyssa, bob, bobbi
|
||||
// フォローしてて非アクティブなので次: alyson, bobbie
|
||||
// フォローしてないけどアクティブなので次: alyce, alycia, root(アルファベット順的にここになる)
|
||||
// フォローしてないし非アクティブなので最後: alice, alysha, bobby
|
||||
expect(result).toEqual([alyssa, bob, bobbi, alyson, bobbie, alyce, alycia, root, alice, alysha, bobby].map(x => x.id));
|
||||
});
|
||||
|
||||
test('[非ログイン] アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setInactive([alice, alyce, alycia]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
);
|
||||
|
||||
// alice, alyce, alyciaは非アクティブなので後ろに行く
|
||||
expect(result).toEqual([alysha, alyson, alyssa, alice, alyce, alycia].map(x => x.id));
|
||||
});
|
||||
|
||||
test('[非ログイン] 非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
|
||||
await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
);
|
||||
|
||||
expect(result).toEqual([alice, alyce, alycia, alysha, alyson, alyssa].map(x => x.id));
|
||||
});
|
||||
|
||||
test('フォロー中のアクティブユーザのうち、"al"から始まり"example.com"にいる人が全員ヒットする', async () => {
|
||||
await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al', host: 'exam' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
expect(result).toEqual([alyson, alyssa].map(x => x.id));
|
||||
});
|
||||
|
||||
test('サスペンド済みユーザは出ない', async () => {
|
||||
await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
|
||||
await setSuspended([alice, alyce, alycia]);
|
||||
|
||||
const result = await service.search(
|
||||
{ username: 'al' },
|
||||
{ limit: 100 },
|
||||
root,
|
||||
);
|
||||
|
||||
expect(result).toEqual([alysha, alyson, alyssa].map(x => x.id));
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue