summaryrefslogtreecommitdiff
path: root/packages/backend/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/test/unit')
-rw-r--r--packages/backend/test/unit/AbuseReportNotificationService.ts343
-rw-r--r--packages/backend/test/unit/RoleService.ts132
-rw-r--r--packages/backend/test/unit/SystemWebhookService.ts515
3 files changed, 979 insertions, 11 deletions
diff --git a/packages/backend/test/unit/AbuseReportNotificationService.ts b/packages/backend/test/unit/AbuseReportNotificationService.ts
new file mode 100644
index 0000000000..e971659070
--- /dev/null
+++ b/packages/backend/test/unit/AbuseReportNotificationService.ts
@@ -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]);
+ });
+ });
+});
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index ec441735d7..69fa4162fb 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -3,8 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-
process.env.NODE_ENV = 'test';
import { jest } from '@jest/globals';
@@ -13,7 +11,14 @@ 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,6 +28,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 { UserEntityService } from '@/core/entities/UserEntityService.js';
import { sleep } from '../utils.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());
}
@@ -265,6 +285,96 @@ describe('RoleService', () => {
});
});
+ 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([
diff --git a/packages/backend/test/unit/SystemWebhookService.ts b/packages/backend/test/unit/SystemWebhookService.ts
new file mode 100644
index 0000000000..41b7f977ca
--- /dev/null
+++ b/packages/backend/test/unit/SystemWebhookService.ts
@@ -0,0 +1,515 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+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, sleep } 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 sleep(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 sleep(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 sleep(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 sleep(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 sleep(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 sleep(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 sleep(500);
+
+ const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+ expect(fetchedWebhooks.length).toEqual(0);
+ });
+ });
+ });
+ });
+});