merge upstream 2025-02-03
This commit is contained in:
commit
a4e86758c1
264 changed files with 15775 additions and 4919 deletions
|
|
@ -397,7 +397,7 @@ describe('Timelines', () => {
|
|||
assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
|
||||
}, 1000 * 10);
|
||||
}, 1000 * 15);
|
||||
|
||||
test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
|
||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||
|
|
|
|||
|
|
@ -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] });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
622
packages/backend/test/unit/CaptchaService.ts
Normal file
622
packages/backend/test/unit/CaptchaService.ts
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, jest } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Response } from 'node-fetch';
|
||||
import {
|
||||
CaptchaError,
|
||||
CaptchaErrorCode,
|
||||
captchaErrorCodes,
|
||||
CaptchaSaveResult,
|
||||
CaptchaService,
|
||||
} from '@/core/CaptchaService.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { MiMeta } from '@/models/Meta.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
|
||||
describe('CaptchaService', () => {
|
||||
let app: TestingModule;
|
||||
let service: CaptchaService;
|
||||
let httpRequestService: jest.Mocked<HttpRequestService>;
|
||||
let metaService: jest.Mocked<MetaService>;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
CaptchaService,
|
||||
LoggerService,
|
||||
{
|
||||
provide: HttpRequestService, useFactory: () => ({ send: jest.fn() }),
|
||||
},
|
||||
{
|
||||
provide: MetaService, useFactory: () => ({
|
||||
fetch: jest.fn(),
|
||||
update: jest.fn(),
|
||||
}),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app.enableShutdownHooks();
|
||||
|
||||
service = app.get(CaptchaService);
|
||||
httpRequestService = app.get(HttpRequestService) as jest.Mocked<HttpRequestService>;
|
||||
metaService = app.get(MetaService) as jest.Mocked<MetaService>;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
httpRequestService.send.mockClear();
|
||||
metaService.update.mockClear();
|
||||
metaService.fetch.mockClear();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
function successMock(result: object) {
|
||||
httpRequestService.send.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => (result),
|
||||
} as Response);
|
||||
}
|
||||
|
||||
function failureHttpMock() {
|
||||
httpRequestService.send.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 400,
|
||||
} as Response);
|
||||
}
|
||||
|
||||
function failureVerificationMock(result: object) {
|
||||
httpRequestService.send.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => (result),
|
||||
} as Response);
|
||||
}
|
||||
|
||||
async function testCaptchaError(code: CaptchaErrorCode, test: () => Promise<void>) {
|
||||
try {
|
||||
await test();
|
||||
expect(false).toBe(true);
|
||||
} catch (e) {
|
||||
expect(e instanceof CaptchaError).toBe(true);
|
||||
|
||||
const _e = e as CaptchaError;
|
||||
expect(_e.code).toBe(code);
|
||||
}
|
||||
}
|
||||
|
||||
describe('verifyRecaptcha', () => {
|
||||
test('success', async () => {
|
||||
successMock({ success: true });
|
||||
await service.verifyRecaptcha('secret', 'response');
|
||||
});
|
||||
|
||||
test('noResponseProvided', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.noResponseProvided, () => service.verifyRecaptcha('secret', null));
|
||||
});
|
||||
|
||||
test('requestFailed', async () => {
|
||||
failureHttpMock();
|
||||
await testCaptchaError(captchaErrorCodes.requestFailed, () => service.verifyRecaptcha('secret', 'response'));
|
||||
});
|
||||
|
||||
test('verificationFailed', async () => {
|
||||
failureVerificationMock({ success: false, 'error-codes': ['code01', 'code02'] });
|
||||
await testCaptchaError(captchaErrorCodes.verificationFailed, () => service.verifyRecaptcha('secret', 'response'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyHcaptcha', () => {
|
||||
test('success', async () => {
|
||||
successMock({ success: true });
|
||||
await service.verifyHcaptcha('secret', 'response');
|
||||
});
|
||||
|
||||
test('noResponseProvided', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.noResponseProvided, () => service.verifyHcaptcha('secret', null));
|
||||
});
|
||||
|
||||
test('requestFailed', async () => {
|
||||
failureHttpMock();
|
||||
await testCaptchaError(captchaErrorCodes.requestFailed, () => service.verifyHcaptcha('secret', 'response'));
|
||||
});
|
||||
|
||||
test('verificationFailed', async () => {
|
||||
failureVerificationMock({ success: false, 'error-codes': ['code01', 'code02'] });
|
||||
await testCaptchaError(captchaErrorCodes.verificationFailed, () => service.verifyHcaptcha('secret', 'response'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyMcaptcha', () => {
|
||||
const host = 'https://localhost';
|
||||
|
||||
test('success', async () => {
|
||||
successMock({ valid: true });
|
||||
await service.verifyMcaptcha('secret', 'sitekey', host, 'response');
|
||||
});
|
||||
|
||||
test('noResponseProvided', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.noResponseProvided, () => service.verifyMcaptcha('secret', 'sitekey', host, null));
|
||||
});
|
||||
|
||||
test('requestFailed', async () => {
|
||||
failureHttpMock();
|
||||
await testCaptchaError(captchaErrorCodes.requestFailed, () => service.verifyMcaptcha('secret', 'sitekey', host, 'response'));
|
||||
});
|
||||
|
||||
test('verificationFailed', async () => {
|
||||
failureVerificationMock({ valid: false });
|
||||
await testCaptchaError(captchaErrorCodes.verificationFailed, () => service.verifyMcaptcha('secret', 'sitekey', host, 'response'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyTurnstile', () => {
|
||||
test('success', async () => {
|
||||
successMock({ success: true });
|
||||
await service.verifyTurnstile('secret', 'response');
|
||||
});
|
||||
|
||||
test('noResponseProvided', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.noResponseProvided, () => service.verifyTurnstile('secret', null));
|
||||
});
|
||||
|
||||
test('requestFailed', async () => {
|
||||
failureHttpMock();
|
||||
await testCaptchaError(captchaErrorCodes.requestFailed, () => service.verifyTurnstile('secret', 'response'));
|
||||
});
|
||||
|
||||
test('verificationFailed', async () => {
|
||||
failureVerificationMock({ success: false, 'error-codes': ['code01', 'code02'] });
|
||||
await testCaptchaError(captchaErrorCodes.verificationFailed, () => service.verifyTurnstile('secret', 'response'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyTestcaptcha', () => {
|
||||
test('success', async () => {
|
||||
await service.verifyTestcaptcha('testcaptcha-passed');
|
||||
});
|
||||
|
||||
test('noResponseProvided', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.noResponseProvided, () => service.verifyTestcaptcha(null));
|
||||
});
|
||||
|
||||
test('verificationFailed', async () => {
|
||||
await testCaptchaError(captchaErrorCodes.verificationFailed, () => service.verifyTestcaptcha('testcaptcha-failed'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
function setupMeta(meta: Partial<MiMeta>) {
|
||||
metaService.fetch.mockResolvedValue(meta as MiMeta);
|
||||
}
|
||||
|
||||
test('values', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
hcaptchaSiteKey: 'hcaptcha-sitekey',
|
||||
hcaptchaSecretKey: 'hcaptcha-secret',
|
||||
mcaptchaSitekey: 'mcaptcha-sitekey',
|
||||
mcaptchaSecretKey: 'mcaptcha-secret',
|
||||
mcaptchaInstanceUrl: 'https://localhost',
|
||||
recaptchaSiteKey: 'recaptcha-sitekey',
|
||||
recaptchaSecretKey: 'recaptcha-secret',
|
||||
turnstileSiteKey: 'turnstile-sitekey',
|
||||
turnstileSecretKey: 'turnstile-secret',
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('none');
|
||||
expect(result.hcaptcha.siteKey).toBe('hcaptcha-sitekey');
|
||||
expect(result.hcaptcha.secretKey).toBe('hcaptcha-secret');
|
||||
expect(result.mcaptcha.siteKey).toBe('mcaptcha-sitekey');
|
||||
expect(result.mcaptcha.secretKey).toBe('mcaptcha-secret');
|
||||
expect(result.mcaptcha.instanceUrl).toBe('https://localhost');
|
||||
expect(result.recaptcha.siteKey).toBe('recaptcha-sitekey');
|
||||
expect(result.recaptcha.secretKey).toBe('recaptcha-secret');
|
||||
expect(result.turnstile.siteKey).toBe('turnstile-sitekey');
|
||||
expect(result.turnstile.secretKey).toBe('turnstile-secret');
|
||||
});
|
||||
|
||||
describe('provider', () => {
|
||||
test('none', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('none');
|
||||
});
|
||||
|
||||
test('hcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: true,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('hcaptcha');
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: true,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('mcaptcha');
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: true,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('recaptcha');
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: true,
|
||||
enableTestcaptcha: false,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('turnstile');
|
||||
});
|
||||
|
||||
test('testcaptcha', async () => {
|
||||
setupMeta({
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: true,
|
||||
});
|
||||
|
||||
const result = await service.get();
|
||||
expect(result.provider).toBe('testcaptcha');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('save', () => {
|
||||
const host = 'https://localhost';
|
||||
|
||||
describe('[success] 検証に成功した時だけ保存できる+他のプロバイダの設定値を誤って更新しない', () => {
|
||||
beforeEach(() => {
|
||||
successMock({ success: true, valid: true });
|
||||
});
|
||||
|
||||
async function assertSuccess(promise: Promise<CaptchaSaveResult>, expectMeta: Partial<MiMeta>) {
|
||||
await expect(promise)
|
||||
.resolves
|
||||
.toStrictEqual({ success: true });
|
||||
const partialParams = metaService.update.mock.calls[0][0];
|
||||
expect(partialParams).toStrictEqual(expectMeta);
|
||||
}
|
||||
|
||||
test('none', async () => {
|
||||
await assertSuccess(
|
||||
service.save('none'),
|
||||
{
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('hcaptcha', async () => {
|
||||
await assertSuccess(
|
||||
service.save('hcaptcha', {
|
||||
sitekey: 'hcaptcha-sitekey',
|
||||
secret: 'hcaptcha-secret',
|
||||
captchaResult: 'hcaptcha-passed',
|
||||
}),
|
||||
{
|
||||
enableHcaptcha: true,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
hcaptchaSiteKey: 'hcaptcha-sitekey',
|
||||
hcaptchaSecretKey: 'hcaptcha-secret',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
await assertSuccess(
|
||||
service.save('mcaptcha', {
|
||||
sitekey: 'mcaptcha-sitekey',
|
||||
secret: 'mcaptcha-secret',
|
||||
instanceUrl: host,
|
||||
captchaResult: 'mcaptcha-passed',
|
||||
}),
|
||||
{
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: true,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
mcaptchaSitekey: 'mcaptcha-sitekey',
|
||||
mcaptchaSecretKey: 'mcaptcha-secret',
|
||||
mcaptchaInstanceUrl: host,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
await assertSuccess(
|
||||
service.save('recaptcha', {
|
||||
sitekey: 'recaptcha-sitekey',
|
||||
secret: 'recaptcha-secret',
|
||||
captchaResult: 'recaptcha-passed',
|
||||
}),
|
||||
{
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: true,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: false,
|
||||
recaptchaSiteKey: 'recaptcha-sitekey',
|
||||
recaptchaSecretKey: 'recaptcha-secret',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
await assertSuccess(
|
||||
service.save('turnstile', {
|
||||
sitekey: 'turnstile-sitekey',
|
||||
secret: 'turnstile-secret',
|
||||
captchaResult: 'turnstile-passed',
|
||||
}),
|
||||
{
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: true,
|
||||
enableTestcaptcha: false,
|
||||
turnstileSiteKey: 'turnstile-sitekey',
|
||||
turnstileSecretKey: 'turnstile-secret',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('testcaptcha', async () => {
|
||||
await assertSuccess(
|
||||
service.save('testcaptcha', {
|
||||
sitekey: 'testcaptcha-sitekey',
|
||||
secret: 'testcaptcha-secret',
|
||||
captchaResult: 'testcaptcha-passed',
|
||||
}),
|
||||
{
|
||||
enableHcaptcha: false,
|
||||
enableMcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableTurnstile: false,
|
||||
enableTestcaptcha: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[failure] 検証に失敗した場合は保存できない+設定値の更新そのものが発生しない', () => {
|
||||
async function assertFailure(code: CaptchaErrorCode, promise: Promise<CaptchaSaveResult>) {
|
||||
const res = await promise;
|
||||
expect(res.success).toBe(false);
|
||||
if (!res.success) {
|
||||
expect(res.error.code).toBe(code);
|
||||
}
|
||||
expect(metaService.update).not.toBeCalled();
|
||||
}
|
||||
|
||||
describe('invalidParameters', () => {
|
||||
test('hcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.invalidParameters,
|
||||
service.save('hcaptcha', {
|
||||
sitekey: 'hcaptcha-sitekey',
|
||||
secret: 'hcaptcha-secret',
|
||||
captchaResult: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.invalidParameters,
|
||||
service.save('mcaptcha', {
|
||||
sitekey: 'mcaptcha-sitekey',
|
||||
secret: 'mcaptcha-secret',
|
||||
instanceUrl: host,
|
||||
captchaResult: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.invalidParameters,
|
||||
service.save('recaptcha', {
|
||||
sitekey: 'recaptcha-sitekey',
|
||||
secret: 'recaptcha-secret',
|
||||
captchaResult: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.invalidParameters,
|
||||
service.save('turnstile', {
|
||||
sitekey: 'turnstile-sitekey',
|
||||
secret: 'turnstile-secret',
|
||||
captchaResult: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('testcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.invalidParameters,
|
||||
service.save('testcaptcha', {
|
||||
captchaResult: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestFailed', () => {
|
||||
beforeEach(() => {
|
||||
failureHttpMock();
|
||||
});
|
||||
|
||||
test('hcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.requestFailed,
|
||||
service.save('hcaptcha', {
|
||||
sitekey: 'hcaptcha-sitekey',
|
||||
secret: 'hcaptcha-secret',
|
||||
captchaResult: 'hcaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.requestFailed,
|
||||
service.save('mcaptcha', {
|
||||
sitekey: 'mcaptcha-sitekey',
|
||||
secret: 'mcaptcha-secret',
|
||||
instanceUrl: host,
|
||||
captchaResult: 'mcaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.requestFailed,
|
||||
service.save('recaptcha', {
|
||||
sitekey: 'recaptcha-sitekey',
|
||||
secret: 'recaptcha-secret',
|
||||
captchaResult: 'recaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.requestFailed,
|
||||
service.save('turnstile', {
|
||||
sitekey: 'turnstile-sitekey',
|
||||
secret: 'turnstile-secret',
|
||||
captchaResult: 'turnstile-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// testchapchaはrequestFailedがない
|
||||
});
|
||||
|
||||
describe('verificationFailed', () => {
|
||||
beforeEach(() => {
|
||||
failureVerificationMock({ success: false, valid: false, 'error-codes': ['code01', 'code02'] });
|
||||
});
|
||||
|
||||
test('hcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.verificationFailed,
|
||||
service.save('hcaptcha', {
|
||||
sitekey: 'hcaptcha-sitekey',
|
||||
secret: 'hcaptcha-secret',
|
||||
captchaResult: 'hccaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('mcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.verificationFailed,
|
||||
service.save('mcaptcha', {
|
||||
sitekey: 'mcaptcha-sitekey',
|
||||
secret: 'mcaptcha-secret',
|
||||
instanceUrl: host,
|
||||
captchaResult: 'mcaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('recaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.verificationFailed,
|
||||
service.save('recaptcha', {
|
||||
sitekey: 'recaptcha-sitekey',
|
||||
secret: 'recaptcha-secret',
|
||||
captchaResult: 'recaptcha-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('turnstile', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.verificationFailed,
|
||||
service.save('turnstile', {
|
||||
sitekey: 'turnstile-sitekey',
|
||||
secret: 'turnstile-secret',
|
||||
captchaResult: 'turnstile-passed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('testcaptcha', async () => {
|
||||
await assertFailure(
|
||||
captchaErrorCodes.verificationFailed,
|
||||
service.save('testcaptcha', {
|
||||
captchaResult: 'testcaptcha-failed',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
817
packages/backend/test/unit/CustomEmojiService.ts
Normal file
817
packages/backend/test/unit/CustomEmojiService.ts
Normal file
|
|
@ -0,0 +1,817 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { afterEach, beforeAll, describe, test } from '@jest/globals';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { EmojisRepository } from '@/models/_.js';
|
||||
import { MiEmoji } from '@/models/Emoji.js';
|
||||
|
||||
describe('CustomEmojiService', () => {
|
||||
let app: TestingModule;
|
||||
let service: CustomEmojiService;
|
||||
|
||||
let emojisRepository: EmojisRepository;
|
||||
let idService: IdService;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test
|
||||
.createTestingModule({
|
||||
imports: [
|
||||
GlobalModule,
|
||||
],
|
||||
providers: [
|
||||
CustomEmojiService,
|
||||
UtilityService,
|
||||
IdService,
|
||||
EmojiEntityService,
|
||||
ModerationLogService,
|
||||
GlobalEventService,
|
||||
],
|
||||
})
|
||||
.compile();
|
||||
app.enableShutdownHooks();
|
||||
|
||||
service = app.get<CustomEmojiService>(CustomEmojiService);
|
||||
emojisRepository = app.get<EmojisRepository>(DI.emojisRepository);
|
||||
idService = app.get<IdService>(IdService);
|
||||
});
|
||||
|
||||
describe('fetchEmojis', () => {
|
||||
async function insert(data: Partial<MiEmoji>[]) {
|
||||
for (const d of data) {
|
||||
const id = idService.gen();
|
||||
await emojisRepository.insert({
|
||||
id: id,
|
||||
updatedAt: new Date(),
|
||||
...d,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function call(params: Parameters<CustomEmojiService['fetchEmojis']>['0']) {
|
||||
return service.fetchEmojis(
|
||||
params,
|
||||
{
|
||||
// テスト向けに
|
||||
sortKeys: ['+id'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function defaultData(suffix: string, override?: Partial<MiEmoji>): Partial<MiEmoji> {
|
||||
return {
|
||||
name: `emoji${suffix}`,
|
||||
host: null,
|
||||
category: 'default',
|
||||
originalUrl: `https://example.com/emoji${suffix}.png`,
|
||||
publicUrl: `https://example.com/emoji${suffix}.png`,
|
||||
type: 'image/png',
|
||||
aliases: [`emoji${suffix}`],
|
||||
license: 'CC0',
|
||||
isSensitive: false,
|
||||
localOnly: false,
|
||||
roleIdsThatCanBeUsedThisEmojiAsReaction: [],
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await emojisRepository.delete({});
|
||||
});
|
||||
|
||||
describe('単独', () => {
|
||||
test('updatedAtFrom', async () => {
|
||||
await insert([
|
||||
defaultData('001', { updatedAt: new Date('2021-01-01T00:00:00.000Z') }),
|
||||
defaultData('002', { updatedAt: new Date('2021-01-02T00:00:00.000Z') }),
|
||||
defaultData('003', { updatedAt: new Date('2021-01-03T00:00:00.000Z') }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
updatedAtFrom: '2021-01-02T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('updatedAtTo', async () => {
|
||||
await insert([
|
||||
defaultData('001', { updatedAt: new Date('2021-01-01T00:00:00.000Z') }),
|
||||
defaultData('002', { updatedAt: new Date('2021-01-02T00:00:00.000Z') }),
|
||||
defaultData('003', { updatedAt: new Date('2021-01-03T00:00:00.000Z') }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
updatedAtTo: '2021-01-02T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
describe('name', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001'),
|
||||
defaultData('002'),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
name: 'emoji001',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001'),
|
||||
defaultData('002'),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
name: 'emoji001 emoji002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001'),
|
||||
defaultData('002'),
|
||||
defaultData('003', { name: 'em003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
name: 'oji',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001'),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
name: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('host', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { host: 'example.com' }),
|
||||
defaultData('002', { host: 'example.com' }),
|
||||
defaultData('003', { host: '1.example.com' }),
|
||||
defaultData('004', { host: '2.example.com' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
host: 'example.com',
|
||||
hostType: 'remote',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(4);
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { host: 'example.com' }),
|
||||
defaultData('002', { host: 'example.com' }),
|
||||
defaultData('003', { host: '1.example.com' }),
|
||||
defaultData('004', { host: '2.example.com' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
host: '1.example.com 2.example.com',
|
||||
hostType: 'remote',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji003');
|
||||
expect(actual.emojis[1].name).toBe('emoji004');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { host: 'example.com' }),
|
||||
defaultData('002', { host: 'example.com' }),
|
||||
defaultData('003', { host: '1.example.com' }),
|
||||
defaultData('004', { host: '2.example.com' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
host: 'example',
|
||||
hostType: 'remote',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(4);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { host: 'example.com' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
host: '%',
|
||||
hostType: 'remote',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uri', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { uri: 'uri001' }),
|
||||
defaultData('002', { uri: 'uri002' }),
|
||||
defaultData('003', { uri: 'uri003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
uri: 'uri002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { uri: 'uri001' }),
|
||||
defaultData('002', { uri: 'uri002' }),
|
||||
defaultData('003', { uri: 'uri003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
uri: 'uri001 uri003',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { uri: 'uri001' }),
|
||||
defaultData('002', { uri: 'uri002' }),
|
||||
defaultData('003', { uri: 'uri003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
uri: 'ri',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { uri: 'uri001' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
uri: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('publicUrl', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { publicUrl: 'publicUrl001' }),
|
||||
defaultData('002', { publicUrl: 'publicUrl002' }),
|
||||
defaultData('003', { publicUrl: 'publicUrl003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
publicUrl: 'publicUrl002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { publicUrl: 'publicUrl001' }),
|
||||
defaultData('002', { publicUrl: 'publicUrl002' }),
|
||||
defaultData('003', { publicUrl: 'publicUrl003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
publicUrl: 'publicUrl001 publicUrl003',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { publicUrl: 'publicUrl001' }),
|
||||
defaultData('002', { publicUrl: 'publicUrl002' }),
|
||||
defaultData('003', { publicUrl: 'publicUrl003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
publicUrl: 'Url',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { publicUrl: 'publicUrl001' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
publicUrl: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('type', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { type: 'type001' }),
|
||||
defaultData('002', { type: 'type002' }),
|
||||
defaultData('003', { type: 'type003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
type: 'type002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { type: 'type001' }),
|
||||
defaultData('002', { type: 'type002' }),
|
||||
defaultData('003', { type: 'type003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
type: 'type001 type003',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { type: 'type001' }),
|
||||
defaultData('002', { type: 'type002' }),
|
||||
defaultData('003', { type: 'type003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
type: 'pe',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { type: 'type001' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
type: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('aliases', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { aliases: ['alias001', 'alias002'] }),
|
||||
defaultData('002', { aliases: ['alias002'] }),
|
||||
defaultData('003', { aliases: ['alias003'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
aliases: 'alias002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { aliases: ['alias001', 'alias002'] }),
|
||||
defaultData('002', { aliases: ['alias002', 'alias004'] }),
|
||||
defaultData('003', { aliases: ['alias003'] }),
|
||||
defaultData('004', { aliases: ['alias004'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
aliases: 'alias001 alias004',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
expect(actual.emojis[2].name).toBe('emoji004');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { aliases: ['alias001', 'alias002'] }),
|
||||
defaultData('002', { aliases: ['alias002', 'alias004'] }),
|
||||
defaultData('003', { aliases: ['alias003'] }),
|
||||
defaultData('004', { aliases: ['alias004'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
aliases: 'ias',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(4);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { aliases: ['alias001', 'alias002'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
aliases: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('category', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { category: 'category001' }),
|
||||
defaultData('002', { category: 'category002' }),
|
||||
defaultData('003', { category: 'category003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
category: 'category002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { category: 'category001' }),
|
||||
defaultData('002', { category: 'category002' }),
|
||||
defaultData('003', { category: 'category003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
category: 'category001 category003',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { category: 'category001' }),
|
||||
defaultData('002', { category: 'category002' }),
|
||||
defaultData('003', { category: 'category003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
category: 'egory',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { category: 'category001' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
category: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('license', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { license: 'license001' }),
|
||||
defaultData('002', { license: 'license002' }),
|
||||
defaultData('003', { license: 'license003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
license: 'license002',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { license: 'license001' }),
|
||||
defaultData('002', { license: 'license002' }),
|
||||
defaultData('003', { license: 'license003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
license: 'license001 license003',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('keyword', async () => {
|
||||
await insert([
|
||||
defaultData('001', { license: 'license001' }),
|
||||
defaultData('002', { license: 'license002' }),
|
||||
defaultData('003', { license: 'license003' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
license: 'cense',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
|
||||
test('escape', async () => {
|
||||
await insert([
|
||||
defaultData('001', { license: 'license001' }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
license: '%',
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSensitive', () => {
|
||||
test('true', async () => {
|
||||
await insert([
|
||||
defaultData('001', { isSensitive: true }),
|
||||
defaultData('002', { isSensitive: false }),
|
||||
defaultData('003', { isSensitive: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
isSensitive: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('false', async () => {
|
||||
await insert([
|
||||
defaultData('001', { isSensitive: true }),
|
||||
defaultData('002', { isSensitive: false }),
|
||||
defaultData('003', { isSensitive: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
isSensitive: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('null', async () => {
|
||||
await insert([
|
||||
defaultData('001', { isSensitive: true }),
|
||||
defaultData('002', { isSensitive: false }),
|
||||
defaultData('003', { isSensitive: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('localOnly', () => {
|
||||
test('true', async () => {
|
||||
await insert([
|
||||
defaultData('001', { localOnly: true }),
|
||||
defaultData('002', { localOnly: false }),
|
||||
defaultData('003', { localOnly: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
localOnly: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(2);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji003');
|
||||
});
|
||||
|
||||
test('false', async () => {
|
||||
await insert([
|
||||
defaultData('001', { localOnly: true }),
|
||||
defaultData('002', { localOnly: false }),
|
||||
defaultData('003', { localOnly: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
localOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('null', async () => {
|
||||
await insert([
|
||||
defaultData('001', { localOnly: true }),
|
||||
defaultData('002', { localOnly: false }),
|
||||
defaultData('003', { localOnly: true }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('roleId', () => {
|
||||
test('single', async () => {
|
||||
await insert([
|
||||
defaultData('001', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role001'] }),
|
||||
defaultData('002', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role002'] }),
|
||||
defaultData('003', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role003'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
roleIds: ['role002'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(1);
|
||||
expect(actual.emojis[0].name).toBe('emoji002');
|
||||
});
|
||||
|
||||
test('multi', async () => {
|
||||
await insert([
|
||||
defaultData('001', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role001'] }),
|
||||
defaultData('002', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role002', 'role003'] }),
|
||||
defaultData('003', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role003'] }),
|
||||
defaultData('004', { roleIdsThatCanBeUsedThisEmojiAsReaction: ['role004'] }),
|
||||
]);
|
||||
|
||||
const actual = await call({
|
||||
query: {
|
||||
roleIds: ['role001', 'role003'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(actual.allCount).toBe(3);
|
||||
expect(actual.emojis[0].name).toBe('emoji001');
|
||||
expect(actual.emojis[1].name).toBe('emoji002');
|
||||
expect(actual.emojis[2].name).toBe('emoji003');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -108,6 +108,24 @@ describe('MfmService', () => {
|
|||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <a></a> d</p>'), 'a d');
|
||||
});
|
||||
|
||||
test('ruby', () => {
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'), 'a $[ruby Misskey ミスキー] b');
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'), 'a $[ruby Misskey ミスキー]$[ruby Misskey ミスキー] b');
|
||||
});
|
||||
|
||||
test('ruby with spaces', () => {
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Miss key<rp>(</rp><rt>ミスキー</rt><rp>)</rp> b</ruby> c</p>'), 'a Miss key(ミスキー) b c');
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミス キー</rt><rp>)</rp> b</ruby> c</p>'), 'a Misskey(ミス キー) b c');
|
||||
assert.deepStrictEqual(
|
||||
mfmService.fromHtml('<p>a <ruby>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミス キー</rt><rp>)</rp>Misskey<rp>(</rp><rt>ミスキー</rt><rp>)</rp></ruby> b</p>'),
|
||||
'a Misskey(ミスキー)Misskey(ミス キー)Misskey(ミスキー) b'
|
||||
);
|
||||
});
|
||||
|
||||
test('ruby with other inline tags', () => {
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <ruby><strong>Misskey</strong><rp>(</rp><rt>ミスキー</rt><rp>)</rp> b</ruby> c</p>'), 'a **Misskey**(ミスキー) b c');
|
||||
});
|
||||
|
||||
test('mention', () => {
|
||||
assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue