admin/src/SimpleAdmin.Api.IntegrationTests/WeChatRouteIntegrationTests.cs
zzh 206dc3ef3c feat: ??????????????????????????
?????????????????????????????????? SQL?

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 15:16:55 +08:00

133 lines
5.4 KiB
C#

using System.Net;
using System.Security.Cryptography;
using System.Text;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using Oms.Domain.SystemAggregate.Consts;
using Oms.Domain.SystemAggregate.Entities;
using Oms.Domain.SystemAggregate.Repositories;
using SimpleAdmin.Api.IntegrationTests.Support;
using SimpleAdmin.Api.WeChat.Options;
namespace SimpleAdmin.Api.IntegrationTests;
public class WeChatRouteIntegrationTests
{
[Fact]
public async Task WeChatLegacyVerification_ShouldRegisterGetAndPostMethods()
{
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback);
var methods = host.GetHttpMethods("apiwechat/verification");
methods.Should().BeEquivalentTo(["GET", "POST"]);
}
[Fact]
public async Task WeChatRoutes_ShouldBeRegisteredOnAuthService()
{
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback);
host.RoutePatterns.Should().Contain("apiwechat/verification");
host.RoutePatterns.Should().Contain("wechat/mp/{appId}");
host.RoutePatterns.Should().Contain("wechat/mp/login-qrcode");
host.RoutePatterns.Should().Contain("wechat/mp/follow-qrcode");
host.RoutePatterns.Should().Contain("wechat/oauth/user-info");
host.RoutePatterns.Should().Contain("wechat/customer/register");
host.RoutePatterns.Should().Contain("wechat/customer/bind");
host.RoutePatterns.Should().Contain("rbac/auth/wx-login/status");
}
[Fact]
public async Task WeChatValidateLegacy_ShouldReturnEchostrWhenSignatureValid()
{
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
{
ConfigureWeChatFallback(services);
services.PostConfigure<WeChatMpOptions>(options => options.Token = "test-token");
});
const string timestamp = "1400000000";
const string nonce = "nonce";
const string echostr = "hello-wechat";
var signature = SignWeChat("test-token", timestamp, nonce);
var response = await host.Client.GetAsync(
$"/apiwechat/verification?signature={signature}&timestamp={timestamp}&nonce={nonce}&echostr={echostr}");
response.StatusCode.Should().Be(HttpStatusCode.OK);
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
}
[Fact]
public async Task WeChatValidateLegacy_ShouldReturnEchostrAsPlainTextWithoutJsonWrapper()
{
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
{
ConfigureWeChatFallback(services);
services.PostConfigure<WeChatMpOptions>(options => options.Token = "test-token");
});
const string timestamp = "1400000000";
const string nonce = "nonce";
const string echostr = "5938472619053847261";
var signature = SignWeChat("test-token", timestamp, nonce);
var response = await host.Client.GetAsync(
$"/apiwechat/verification?signature={signature}&timestamp={timestamp}&nonce={nonce}&echostr={echostr}");
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("text/plain");
var body = await response.Content.ReadAsStringAsync();
body.Should().Be(echostr);
body.Should().NotStartWith("{");
}
[Fact]
public async Task WeChatValidateLegacy_WithProductionToken_ShouldReturnEchostr()
{
const string token = "c6ca74013df823c0e1da0feaaaf8bf66";
const string timestamp = "1714037059";
const string nonce = "486452656";
const string echostr = "5938472619053847261";
var signature = SignWeChat(token, timestamp, nonce);
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
{
ConfigureWeChatFallback(services);
services.PostConfigure<WeChatMpOptions>(options => options.Token = token);
});
var response = await host.Client.GetAsync(
$"/apiwechat/verification?signature={signature}&timestamp={timestamp}&nonce={nonce}&echostr={echostr}");
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("text/plain");
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
}
private static void ConfigureWeChatFallback(IServiceCollection services)
{
services.PostConfigure<WeChatMpOptions>(options =>
{
options.Token = "test-token";
options.AppId = "wx-test";
});
var repository = new Mock<ISystemOtherConfigRepository>();
repository
.Setup(x => x.GetByConfigCodeAsync(SystemOtherConfigCodes.WeChatMp, It.IsAny<CancellationToken>()))
.ReturnsAsync((SystemOtherConfig?)null);
services.RemoveAll<ISystemOtherConfigRepository>();
services.AddSingleton(repository.Object);
}
private static string SignWeChat(string token, string timestamp, string nonce)
{
var parts = new[] { token, timestamp, nonce }.OrderBy(x => x, StringComparer.Ordinal).ToArray();
var hash = SHA1.HashData(Encoding.UTF8.GetBytes(string.Concat(parts)));
return Convert.ToHexString(hash).ToLowerInvariant();
}
}