refactor: SystemWebhook/UserWebhookの配信処理呼び出し部分の改善 (#15035)

* UserWebhook側の対処

* SystemWebhook側の対処

* fix test
This commit is contained in:
おさむのひと
2025-01-14 20:14:02 +09:00
committed by GitHub
parent 5445b023e5
commit d2e22f9050
12 changed files with 258 additions and 140 deletions

View File

@@ -3,13 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { jest } from '@jest/globals';
import { describe, jest } from '@jest/globals';
import { Test, TestingModule } from '@nestjs/testing';
import { randomString } from '../utils.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
import {
AbuseReportNotificationRecipientRepository,
MiAbuseReportNotificationRecipient,
MiAbuseUserReport,
MiSystemWebhook,
MiUser,
SystemWebhooksRepository,
@@ -112,7 +113,10 @@ describe('AbuseReportNotificationService', () => {
provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }),
},
{
provide: UserEntityService, useFactory: () => ({ pack: (v: any) => v }),
provide: UserEntityService, useFactory: () => ({
pack: (v: any) => Promise.resolve(v),
packMany: (v: any) => Promise.resolve(v),
}),
},
{
provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
@@ -344,4 +348,46 @@ describe('AbuseReportNotificationService', () => {
expect(recipients).toEqual([recipient3]);
});
});
describe('notifySystemWebhook', () => {
test('非アクティブな通報通知はWebhook送信から除外される', async () => {
const recipient1 = await createRecipient({
method: 'webhook',
systemWebhookId: systemWebhook1.id,
isActive: true,
});
const recipient2 = await createRecipient({
method: 'webhook',
systemWebhookId: systemWebhook2.id,
isActive: false,
});
const reports: MiAbuseUserReport[] = [
{
id: idService.gen(),
targetUserId: alice.id,
targetUser: alice,
reporterId: bob.id,
reporter: bob,
assigneeId: null,
assignee: null,
resolved: false,
forwarded: false,
comment: 'test',
moderationNote: '',
resolvedAs: null,
targetUserHost: null,
reporterHost: null,
},
];
await service.notifySystemWebhook(reports, 'abuseReport');
// 実際に除外されるかはSystemWebhookService側で確認する.
// ここでは非アクティブな通報通知を除外設定できているかを確認する
expect(webhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
expect(webhookService.enqueueSystemWebhook.mock.calls[0][0]).toBe('abuseReport');
expect(webhookService.enqueueSystemWebhook.mock.calls[0][2]).toEqual({ excludes: [systemWebhook2.id] });
});
});
});

View File

@@ -314,9 +314,10 @@ describe('SystemWebhookService', () => {
isActive: true,
on: ['abuseReport'],
});
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' } as any);
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
expect(queueService.systemWebhookDeliver).toHaveBeenCalled();
expect(queueService.systemWebhookDeliver).toHaveBeenCalledTimes(1);
expect(queueService.systemWebhookDeliver.mock.calls[0][0] as MiSystemWebhook).toEqual(webhook);
});
test('非アクティブなWebhookはキューに追加されない', async () => {
@@ -324,7 +325,7 @@ describe('SystemWebhookService', () => {
isActive: false,
on: ['abuseReport'],
});
await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' } as any);
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
});
@@ -338,11 +339,49 @@ describe('SystemWebhookService', () => {
isActive: true,
on: ['abuseReportResolved'],
});
await service.enqueueSystemWebhook(webhook1.id, 'abuseReport', { foo: 'bar' } as any);
await service.enqueueSystemWebhook(webhook2.id, 'abuseReport', { foo: 'bar' } as any);
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
});
test('混在した時、有効かつ許可されたイベント種別のみ', async () => {
const webhook1 = await createWebhook({
isActive: true,
on: ['abuseReport'],
});
const webhook2 = await createWebhook({
isActive: true,
on: ['abuseReportResolved'],
});
const webhook3 = await createWebhook({
isActive: false,
on: ['abuseReport'],
});
const webhook4 = await createWebhook({
isActive: false,
on: ['abuseReportResolved'],
});
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any);
expect(queueService.systemWebhookDeliver).toHaveBeenCalledTimes(1);
expect(queueService.systemWebhookDeliver.mock.calls[0][0] as MiSystemWebhook).toEqual(webhook1);
});
test('除外指定した場合は送信されない', async () => {
const webhook1 = await createWebhook({
isActive: true,
on: ['abuseReport'],
});
const webhook2 = await createWebhook({
isActive: true,
on: ['abuseReport'],
});
await service.enqueueSystemWebhook('abuseReport', { foo: 'bar' } as any, { excludes: [webhook2.id] });
expect(queueService.systemWebhookDeliver).toHaveBeenCalledTimes(1);
expect(queueService.systemWebhookDeliver.mock.calls[0][0] as MiSystemWebhook).toEqual(webhook1);
});
});
describe('fetchActiveSystemWebhooks', () => {

View File

@@ -1,4 +1,3 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
@@ -71,7 +70,7 @@ describe('UserWebhookService', () => {
LoggerService,
GlobalEventService,
{
provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
provide: QueueService, useFactory: () => ({ userWebhookDeliver: jest.fn() }),
},
],
})
@@ -242,4 +241,92 @@ describe('UserWebhookService', () => {
});
});
});
describe('アプリを毎回作り直す必要があるグループ', () => {
beforeEach(async () => {
await beforeAllImpl();
await beforeEachImpl();
});
afterEach(async () => {
await afterEachImpl();
await afterAllImpl();
});
describe('enqueueUserWebhook', () => {
test('キューに追加成功', async () => {
const webhook = await createWebhook({
active: true,
on: ['note'],
});
await service.enqueueUserWebhook(webhook.userId, 'note', { foo: 'bar' } as any);
expect(queueService.userWebhookDeliver).toHaveBeenCalledTimes(1);
expect(queueService.userWebhookDeliver.mock.calls[0][0] as MiWebhook).toEqual(webhook);
});
test('非アクティブなWebhookはキューに追加されない', async () => {
const webhook = await createWebhook({
active: false,
on: ['note'],
});
await service.enqueueUserWebhook(webhook.userId, 'note', { foo: 'bar' } as any);
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
});
test('未許可のイベント種別が渡された場合はWebhookはキューに追加されない', async () => {
const webhook1 = await createWebhook({
active: true,
on: [],
});
const webhook2 = await createWebhook({
active: true,
on: ['note'],
});
await service.enqueueUserWebhook(webhook1.userId, 'renote', { foo: 'bar' } as any);
await service.enqueueUserWebhook(webhook2.userId, 'renote', { foo: 'bar' } as any);
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
});
test('ユーザIDが異なるWebhookはキューに追加されない', async () => {
const webhook = await createWebhook({
active: true,
on: ['note'],
});
await service.enqueueUserWebhook(idService.gen(), 'note', { foo: 'bar' } as any);
expect(queueService.userWebhookDeliver).not.toHaveBeenCalled();
});
test('混在した時、有効かつ許可されたイベント種別のみ', async () => {
const userId = root.id;
const webhook1 = await createWebhook({
userId,
active: true,
on: ['note'],
});
const webhook2 = await createWebhook({
userId,
active: true,
on: ['renote'],
});
const webhook3 = await createWebhook({
userId,
active: false,
on: ['note'],
});
const webhook4 = await createWebhook({
userId,
active: false,
on: ['renote'],
});
await service.enqueueUserWebhook(userId, 'note', { foo: 'bar' } as any);
expect(queueService.userWebhookDeliver).toHaveBeenCalledTimes(1);
expect(queueService.userWebhookDeliver.mock.calls[0][0] as MiWebhook).toEqual(webhook1);
});
});
});
});

View File

@@ -18,6 +18,7 @@ import { QueueLoggerService } from '@/queue/QueueLoggerService.js';
import { EmailService } from '@/core/EmailService.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
import { SystemWebhookEventType } from '@/models/SystemWebhook.js';
const baseDate = new Date(Date.UTC(2000, 11, 15, 12, 0, 0));
@@ -334,9 +335,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
mockModeratorRole([user1]);
await service.notifyInactiveModeratorsWarning({ time: 1, asDays: 0, asHours: 0 });
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(2);
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0]).toEqual(systemWebhook1);
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[1][0]).toEqual(systemWebhook2);
// typeとactiveによる絞り込みが機能しているかはSystemWebhookServiceのテストで確認する.
// ここでは呼び出されているか、typeが正しいかのみを確認する
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0] as SystemWebhookEventType).toEqual('inactiveModeratorsWarning');
});
});
@@ -372,8 +374,10 @@ describe('CheckModeratorsActivityProcessorService', () => {
mockModeratorRole([user1]);
await service.notifyChangeToInvitationOnly();
// typeとactiveによる絞り込みが機能しているかはSystemWebhookServiceのテストで確認する.
// ここでは呼び出されているか、typeが正しいかのみを確認する
expect(systemWebhookService.enqueueSystemWebhook).toHaveBeenCalledTimes(1);
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0]).toEqual(systemWebhook2);
expect(systemWebhookService.enqueueSystemWebhook.mock.calls[0][0] as SystemWebhookEventType).toEqual('inactiveModeratorsInvitationOnlyChanged');
});
});
});