Merge branch 'develop' into ed25519
This commit is contained in:
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]);
|
||||
});
|
||||
});
|
||||
});
|
@@ -10,6 +10,7 @@ import { ModuleMocker } from 'jest-mock';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
|
||||
import type {
|
||||
AnnouncementReadsRepository,
|
||||
AnnouncementsRepository,
|
||||
@@ -67,6 +68,7 @@ describe('AnnouncementService', () => {
|
||||
],
|
||||
providers: [
|
||||
AnnouncementService,
|
||||
AnnouncementEntityService,
|
||||
CacheService,
|
||||
IdService,
|
||||
],
|
||||
|
@@ -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([
|
||||
|
515
packages/backend/test/unit/SystemWebhookService.ts
Normal file
515
packages/backend/test/unit/SystemWebhookService.ts
Normal file
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user