Merge branch 'develop' into ed25519
This commit is contained in:
@@ -38,7 +38,6 @@ describe('アンテナ', () => {
|
||||
excludeKeywords: [['']],
|
||||
keywords: [['keyword']],
|
||||
name: 'test',
|
||||
notify: false,
|
||||
src: 'all' as const,
|
||||
userListId: null,
|
||||
users: [''],
|
||||
@@ -151,7 +150,6 @@ describe('アンテナ', () => {
|
||||
isActive: true,
|
||||
keywords: [['keyword']],
|
||||
name: 'test',
|
||||
notify: false,
|
||||
src: 'all',
|
||||
userListId: null,
|
||||
users: [''],
|
||||
@@ -159,6 +157,7 @@ describe('アンテナ', () => {
|
||||
withReplies: false,
|
||||
excludeBots: false,
|
||||
localOnly: false,
|
||||
notify: false,
|
||||
};
|
||||
assert.deepStrictEqual(response, expected);
|
||||
});
|
||||
@@ -219,8 +218,6 @@ describe('アンテナ', () => {
|
||||
{ parameters: () => ({ withReplies: true }) },
|
||||
{ parameters: () => ({ withFile: false }) },
|
||||
{ parameters: () => ({ withFile: true }) },
|
||||
{ parameters: () => ({ notify: false }) },
|
||||
{ parameters: () => ({ notify: true }) },
|
||||
];
|
||||
test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
|
||||
const response = await successfulApiCall({
|
||||
|
@@ -9,7 +9,7 @@ process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { loadConfig } from '@/config.js';
|
||||
import { MiUser, UsersRepository } from '@/models/_.js';
|
||||
import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { jobQueue } from '@/boot/common.js';
|
||||
import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
|
||||
@@ -42,7 +42,7 @@ describe('Account Move', () => {
|
||||
dave = await signup({ username: 'dave' });
|
||||
eve = await signup({ username: 'eve' });
|
||||
frank = await signup({ username: 'frank' });
|
||||
Users = connection.getRepository(MiUser);
|
||||
Users = connection.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>);
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -191,7 +191,6 @@ describe('Account Move', () => {
|
||||
localOnly: false,
|
||||
withReplies: false,
|
||||
withFile: false,
|
||||
notify: false,
|
||||
}, alice);
|
||||
antennaId = antenna.body.id;
|
||||
|
||||
@@ -435,7 +434,6 @@ describe('Account Move', () => {
|
||||
localOnly: false,
|
||||
withReplies: false,
|
||||
withFile: false,
|
||||
notify: false,
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 403);
|
||||
|
33
packages/backend/test/e2e/reversi-game.ts
Normal file
33
packages/backend/test/e2e/reversi-game.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ReversiMatchResponse } from 'misskey-js/entities.js';
|
||||
import { api, signup } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
describe('ReversiGame', () => {
|
||||
let alice: misskey.entities.SignupResponse;
|
||||
let bob: misskey.entities.SignupResponse;
|
||||
|
||||
beforeAll(async () => {
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
test('matches when alice invites bob and bob accepts', async () => {
|
||||
const response1 = await api('reversi/match', { userId: bob.id }, alice);
|
||||
assert.strictEqual(response1.status, 204);
|
||||
assert.strictEqual(response1.body, null);
|
||||
const response2 = await api('reversi/match', { userId: alice.id }, bob);
|
||||
assert.strictEqual(response2.status, 200);
|
||||
assert.notStrictEqual(response2.body, null);
|
||||
const body = response2.body as ReversiMatchResponse;
|
||||
assert.strictEqual(body.user1.id, alice.id);
|
||||
assert.strictEqual(body.user2.id, bob.id);
|
||||
});
|
||||
});
|
401
packages/backend/test/e2e/synalio/abuse-report.ts
Normal file
401
packages/backend/test/e2e/synalio/abuse-report.ts
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { entities } from 'misskey-js';
|
||||
import { beforeEach, describe, test } from '@jest/globals';
|
||||
import Fastify from 'fastify';
|
||||
import { api, randomString, role, signup, startJobQueue, UserToken } from '../../utils.js';
|
||||
import type { INestApplicationContext } from '@nestjs/common';
|
||||
|
||||
const WEBHOOK_HOST = 'http://localhost:15080';
|
||||
const WEBHOOK_PORT = 15080;
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
describe('[シナリオ] ユーザ通報', () => {
|
||||
let queue: INestApplicationContext;
|
||||
let admin: entities.SignupResponse;
|
||||
let alice: entities.SignupResponse;
|
||||
let bob: entities.SignupResponse;
|
||||
|
||||
type SystemWebhookPayload = {
|
||||
server: string;
|
||||
hookId: string;
|
||||
eventId: string;
|
||||
createdAt: string;
|
||||
type: string;
|
||||
body: any;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>): Promise<T> {
|
||||
const fastify = Fastify();
|
||||
|
||||
let timeoutHandle: NodeJS.Timeout | null = null;
|
||||
const result = await new Promise<string>(async (resolve, reject) => {
|
||||
fastify.all('/', async (req, res) => {
|
||||
timeoutHandle && clearTimeout(timeoutHandle);
|
||||
|
||||
const body = JSON.stringify(req.body);
|
||||
res.status(200).send('ok');
|
||||
await fastify.close();
|
||||
resolve(body);
|
||||
});
|
||||
|
||||
await fastify.listen({ port: WEBHOOK_PORT });
|
||||
|
||||
timeoutHandle = setTimeout(async () => {
|
||||
await fastify.close();
|
||||
reject(new Error('timeout'));
|
||||
}, 3000);
|
||||
|
||||
try {
|
||||
await postAction();
|
||||
} catch (e) {
|
||||
await fastify.close();
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
await fastify.close();
|
||||
|
||||
return JSON.parse(result) as T;
|
||||
}
|
||||
|
||||
async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
|
||||
const res = await api(
|
||||
'admin/system-webhook/create',
|
||||
{
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
on: ['abuseReport'],
|
||||
url: WEBHOOK_HOST,
|
||||
secret: randomString(),
|
||||
...args,
|
||||
},
|
||||
credential ?? admin,
|
||||
);
|
||||
return res.body;
|
||||
}
|
||||
|
||||
async function createAbuseReportNotificationRecipient(args?: Partial<entities.AdminAbuseReportNotificationRecipientCreateRequest>, credential?: UserToken): Promise<entities.AdminAbuseReportNotificationRecipientCreateResponse> {
|
||||
const res = await api(
|
||||
'admin/abuse-report/notification-recipient/create',
|
||||
{
|
||||
isActive: true,
|
||||
name: randomString(),
|
||||
method: 'webhook',
|
||||
...args,
|
||||
},
|
||||
credential ?? admin,
|
||||
);
|
||||
return res.body;
|
||||
}
|
||||
|
||||
async function createAbuseReport(args?: Partial<entities.UsersReportAbuseRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
|
||||
const res = await api(
|
||||
'users/report-abuse',
|
||||
{
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
...args,
|
||||
},
|
||||
credential ?? admin,
|
||||
);
|
||||
return res.body;
|
||||
}
|
||||
|
||||
async function resolveAbuseReport(args?: Partial<entities.AdminResolveAbuseUserReportRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
|
||||
const res = await api(
|
||||
'admin/resolve-abuse-user-report',
|
||||
{
|
||||
reportId: admin.id,
|
||||
...args,
|
||||
},
|
||||
credential ?? admin,
|
||||
);
|
||||
return res.body;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
beforeAll(async () => {
|
||||
queue = await startJobQueue();
|
||||
admin = await signup({ username: 'admin' });
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
|
||||
await role(admin, { isAdministrator: true });
|
||||
}, 1000 * 60 * 2);
|
||||
|
||||
afterAll(async () => {
|
||||
await queue.close();
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
describe('SystemWebhook', () => {
|
||||
beforeEach(async () => {
|
||||
const webhooks = await api('admin/system-webhook/list', {}, admin);
|
||||
for (const webhook of webhooks.body) {
|
||||
await api('admin/system-webhook/delete', { id: webhook.id }, admin);
|
||||
}
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが送出される', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReport'],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(webhookBody, null, 2));
|
||||
|
||||
expect(webhookBody.hookId).toBe(webhook.id);
|
||||
expect(webhookBody.type).toBe('abuseReport');
|
||||
expect(webhookBody.body.targetUserId).toBe(alice.id);
|
||||
expect(webhookBody.body.reporterId).toBe(bob.id);
|
||||
expect(webhookBody.body.comment).toBe(abuse.comment);
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが送出される', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReport', 'abuseReportResolved'],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(webhookBody1, null, 2));
|
||||
expect(webhookBody1.hookId).toBe(webhook.id);
|
||||
expect(webhookBody1.type).toBe('abuseReport');
|
||||
expect(webhookBody1.body.targetUserId).toBe(alice.id);
|
||||
expect(webhookBody1.body.reporterId).toBe(bob.id);
|
||||
expect(webhookBody1.body.assigneeId).toBeNull();
|
||||
expect(webhookBody1.body.resolved).toBe(false);
|
||||
expect(webhookBody1.body.comment).toBe(abuse.comment);
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: webhookBody1.body.id,
|
||||
forward: false,
|
||||
}, admin);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(webhookBody2, null, 2));
|
||||
expect(webhookBody2.hookId).toBe(webhook.id);
|
||||
expect(webhookBody2.type).toBe('abuseReportResolved');
|
||||
expect(webhookBody2.body.targetUserId).toBe(alice.id);
|
||||
expect(webhookBody2.body.reporterId).toBe(bob.id);
|
||||
expect(webhookBody2.body.assigneeId).toBe(admin.id);
|
||||
expect(webhookBody2.body.resolved).toBe(true);
|
||||
expect(webhookBody2.body.comment).toBe(abuse.comment);
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが未許可の場合は送出されない', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: [],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody).toBe('timeout');
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが送出される', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReportResolved'],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody1).toBe('timeout');
|
||||
|
||||
const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(webhookBody2, null, 2));
|
||||
expect(webhookBody2.hookId).toBe(webhook.id);
|
||||
expect(webhookBody2.type).toBe('abuseReportResolved');
|
||||
expect(webhookBody2.body.targetUserId).toBe(alice.id);
|
||||
expect(webhookBody2.body.reporterId).toBe(bob.id);
|
||||
expect(webhookBody2.body.assigneeId).toBe(admin.id);
|
||||
expect(webhookBody2.body.resolved).toBe(true);
|
||||
expect(webhookBody2.body.comment).toBe(abuse.comment);
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReport'],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(webhookBody1, null, 2));
|
||||
expect(webhookBody1.hookId).toBe(webhook.id);
|
||||
expect(webhookBody1.type).toBe('abuseReport');
|
||||
expect(webhookBody1.body.targetUserId).toBe(alice.id);
|
||||
expect(webhookBody1.body.reporterId).toBe(bob.id);
|
||||
expect(webhookBody1.body.assigneeId).toBeNull();
|
||||
expect(webhookBody1.body.resolved).toBe(false);
|
||||
expect(webhookBody1.body.comment).toBe(abuse.comment);
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: webhookBody1.body.id,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody2).toBe('timeout');
|
||||
});
|
||||
|
||||
test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: [],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody1).toBe('timeout');
|
||||
|
||||
const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody2).toBe('timeout');
|
||||
});
|
||||
|
||||
test('通報を受けた -> Webhookが無効の場合は送出されない', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReport', 'abuseReportResolved'],
|
||||
isActive: false,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody1).toBe('timeout');
|
||||
|
||||
const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody2).toBe('timeout');
|
||||
});
|
||||
|
||||
test('通報を受けた -> 通知設定が無効の場合は送出されない', async () => {
|
||||
const webhook = await createSystemWebhook({
|
||||
on: ['abuseReport', 'abuseReportResolved'],
|
||||
isActive: true,
|
||||
});
|
||||
await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id, isActive: false });
|
||||
|
||||
// 通報(bob -> alice)
|
||||
const abuse = {
|
||||
userId: alice.id,
|
||||
comment: randomString(),
|
||||
};
|
||||
const webhookBody1 = await captureWebhook(async () => {
|
||||
await createAbuseReport(abuse, bob);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody1).toBe('timeout');
|
||||
|
||||
const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
|
||||
|
||||
// 解決
|
||||
const webhookBody2 = await captureWebhook(async () => {
|
||||
await resolveAbuseReport({
|
||||
reportId: abuseReportId,
|
||||
forward: false,
|
||||
}, admin);
|
||||
}).catch(e => e.message);
|
||||
|
||||
expect(webhookBody2).toBe('timeout');
|
||||
});
|
||||
});
|
||||
});
|
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