mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-25 10:44:51 +00:00 
			
		
		
		
	* feat(frontend): CAPTCHAの設定変更時は実際に検証を通過しないと保存できないようにする * なしでも保存できるようにした * fix CHANGELOG.md * フォームが増殖するのを修正 * add comment * add server-side verify * fix ci * fix * fix * fix i18n * add current.ts * fix text * fix * regenerate locales * fix MkFormFooter.vue --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
		
			
				
	
	
		
			622 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			622 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',
 | |
| 						}),
 | |
| 					);
 | |
| 				});
 | |
| 			});
 | |
| 		});
 | |
| 	});
 | |
| });
 |