using CaptchaKit.AspNetCore; using CaptchaKit; using Microsoft.Extensions.DependencyInjection; using SkiaSharp; namespace Jiaowu.Api.Tests; public sealed class CaptchaKitIntegrationTests { [Fact] public async Task Challenge_is_valid_once_and_the_default_generator_returns_a_png() { var services = new ServiceCollection(); services.AddDistributedMemoryCache(); services.AddCaptchaKit(); services.AddSingleton(); using var provider = services.BuildServiceProvider(); var captcha = provider.GetRequiredService(); var challenge = await captcha.CreateAsync(); Assert.Equal("image/png", challenge.ContentType); Assert.True(challenge.ImageBytes!.AsSpan().StartsWith(new byte[] { 137, 80, 78, 71 })); Assert.True(await captcha.VerifyAsync(challenge.Id, "a1b2")); Assert.False(await captcha.VerifyAsync(challenge.Id, "a1b2")); } [Fact] public void Default_generator_returns_png_bytes() { var challenge = new SkiaTextCaptchaGenerator().Generate( new CaptchaGenerationRequest("test", DateTimeOffset.UtcNow.AddMinutes(1), 5)); Assert.Equal("image/png", challenge.Challenge.ContentType); Assert.True(challenge.Challenge.ImageBytes!.AsSpan().StartsWith(new byte[] { 137, 80, 78, 71 })); } [Fact] public void Selection_visual_keeps_the_target_character_visible_above_noise() { var challenge = new SkiaTextCaptchaGenerator().CreateVisualChallenge( new CaptchaGenerationRequest("test", DateTimeOffset.UtcNow.AddMinutes(1), 5), "A", "choice-A", CaptchaChallengeType.Selection, "请选择对应字符"); using var bitmap = SKBitmap.Decode(challenge.Challenge.ImageBytes!); Assert.NotNull(bitmap); var targetPixelCount = 0; for (var y = 0; y < bitmap!.Height; y++) for (var x = bitmap.Width / 4; x < bitmap.Width * 3 / 4; x++) { var pixel = bitmap.GetPixel(x, y); if (pixel.Red <= 30 && pixel.Green is >= 40 and <= 60 && pixel.Blue is >= 70 and <= 90) targetPixelCount++; } Assert.True(targetPixelCount > 20, "The target character should be visibly rendered in the image center."); } [Fact] public void Random_generator_produces_each_enabled_challenge_type() { var generator = new RandomCaptchaGenerator(new CaptchaKitOptions()); var types = new HashSet(); for (var index = 0; index < 100; index++) { var generated = generator.Generate(new CaptchaGenerationRequest( index.ToString(), DateTimeOffset.UtcNow.AddMinutes(1), 5)); types.Add(generated.Challenge.Type); if (generated.Challenge.Type == CaptchaChallengeType.Selection) Assert.Contains(generated.Challenge.Choices!, x => x.Id == generated.Answer); } Assert.Contains(CaptchaChallengeType.TextImage, types); Assert.Contains(CaptchaChallengeType.Selection, types); Assert.Contains(CaptchaChallengeType.MathImage, types); } private sealed class KnownGenerator : ICaptchaGenerator { public GeneratedCaptcha Generate(CaptchaGenerationRequest request) => new( new CaptchaChallenge(request.Id, CaptchaChallengeType.TextImage, request.ExpiresAt, "请输入图中的字符", null, "image/png", new byte[] { 137, 80, 78, 71 }), "A1B2"); } }