mirror of
https://github.com/paricafe/misskey.git
synced 2025-01-23 23:08:41 -06:00
623 lines
17 KiB
TypeScript
623 lines
17 KiB
TypeScript
|
/*
|
||
|
* 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',
|
||
|
}),
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|