增加Shop API
This commit is contained in:
1 parent
3769193696
commit
5d017740c6
83 files changed
+4451
-696
No files matched your search
@@ -0,0 +1,147 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using Asp.Versioning;
|
||||
using MAX.Framework.Commons;
|
||||
using MAX.Framework.Conventions;
|
||||
using MAX.Framework.Dtos;
|
||||
using MAX.Framework.Security;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Store.Shops.Controllers;
|
||||
using Store.Shops.Entities;
|
||||
using Store.Shops.Services;
|
||||
using Store.Users.Contracts;
|
||||
|
||||
namespace Store.Shops.Tests;
|
||||
|
||||
public sealed class ShopHttpTests : IAsyncLifetime
|
||||
{
|
||||
private readonly ShopTestContext context = new();
|
||||
private WebApplication app = null!;
|
||||
private HttpClient client = null!;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions { EnvironmentName = "Testing" });
|
||||
builder.WebHost.UseTestServer();
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.Conventions.Add(new ApiRoutePrefixConvention());
|
||||
options.Filters.Add<GlobalExceptionFilter>();
|
||||
options.Filters.Add<UnifyResultFilter>();
|
||||
}).AddApplicationPart(typeof(ShopController).Assembly).ConfigureApiBehaviorOptions(options =>
|
||||
{
|
||||
options.InvalidModelStateResponseFactory = _ =>
|
||||
new ObjectResult(ApiResult.Fail("参数错误", ResultCode.ValidationError)) { StatusCode = 200 };
|
||||
});
|
||||
builder.Services.AddApiVersioning(options =>
|
||||
{
|
||||
options.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
options.ApiVersionReader = new UrlSegmentApiVersionReader();
|
||||
}).AddMvc();
|
||||
builder.Services.AddAuthentication("Test").AddScheme<AuthenticationSchemeOptions, ShopTestAuthentication>("Test", _ => { });
|
||||
builder.Services.AddAuthorization();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddScoped<ICurrentUser, CurrentUser>();
|
||||
builder.Services.AddSingleton(context.Db);
|
||||
builder.Services.AddSingleton<IUserQueryService>(context);
|
||||
builder.Services.AddSingleton(context.Configuration);
|
||||
builder.Services.AddSingleton(context.Gateway);
|
||||
builder.Services.AddTransient<ShopManagementService>();
|
||||
builder.Services.AddTransient<ShopService>();
|
||||
app = builder.Build();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
await app.StartAsync();
|
||||
client = app.GetTestClient();
|
||||
client.DefaultRequestHeaders.Add("X-Test-User", "owner");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GetPddClients")]
|
||||
[InlineData("GetPddShopClients")]
|
||||
[InlineData("GetPddShopToken")]
|
||||
[InlineData("GetAllPddShops")]
|
||||
[InlineData("AddWeidianShop")]
|
||||
[InlineData("UpdateWeidianShop")]
|
||||
[InlineData("UpdateWeidianShopLogin")]
|
||||
[InlineData("DeleteWeidianShop")]
|
||||
public async Task ActionsOnlyAcceptPost(string action)
|
||||
{
|
||||
using var response = await client.GetAsync("/api/v1/shop/" + action);
|
||||
Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
|
||||
Assert.Contains("POST", response.Content.Headers.Allow);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ActionsBindDtoAndReturnSingleApiResultEnvelope()
|
||||
{
|
||||
using var create = await client.PostAsJsonAsync("/api/v1/shop/AddWeidianShop", new { loginName = "login", shopId = "shop", notes = "old" });
|
||||
Assert.Equal(HttpStatusCode.OK, create.StatusCode);
|
||||
var json = await create.Content.ReadFromJsonAsync<JsonElement>();
|
||||
Assert.Equal(200, json.GetProperty("code").GetInt32());
|
||||
Assert.Equal(JsonValueKind.Null, json.GetProperty("data").ValueKind);
|
||||
using var update = await client.PostAsJsonAsync("/api/v1/shop/UpdateWeidianShop", new { loginName = "login", shopId = "shop", notes = "new" });
|
||||
Assert.Equal(200, (await update.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("code").GetInt32());
|
||||
using var cookie = await client.PostAsJsonAsync("/api/v1/shop/UpdateWeidianShopLogin", new { shopId = "shop", cookie = "session" });
|
||||
Assert.Equal(200, (await cookie.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("code").GetInt32());
|
||||
var row = Assert.Single(await context.Db.Select<WeidianShopEntity>().ToListAsync());
|
||||
Assert.Equal("new", row.Notes);
|
||||
Assert.Equal("session", row.Cookie);
|
||||
Assert.Equal("owner", row.UserName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidationAndBusinessErrorsReturnHttp200WithFailureCode()
|
||||
{
|
||||
using var invalid = await client.PostAsJsonAsync("/api/v1/shop/AddWeidianShop", new { });
|
||||
Assert.Equal(HttpStatusCode.OK, invalid.StatusCode);
|
||||
Assert.Equal(ResultCode.ValidationError, (await invalid.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("code").GetInt32());
|
||||
using var denied = await client.PostAsJsonAsync("/api/v1/shop/GetAllPddShops", new { });
|
||||
Assert.Equal(HttpStatusCode.OK, denied.StatusCode);
|
||||
Assert.Equal(ResultCode.BusinessError, (await denied.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("code").GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CredentialResponseIsWrappedAndNotCached_AnonymousCannotRead()
|
||||
{
|
||||
using var response = await client.PostAsJsonAsync("/api/v1/shop/GetPddClients", new { });
|
||||
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
Assert.Equal(200, result.GetProperty("code").GetInt32());
|
||||
Assert.Equal("test-client", result.GetProperty("data")[0].GetProperty("clientId").GetString());
|
||||
Assert.True(response.Headers.CacheControl!.NoStore);
|
||||
client.DefaultRequestHeaders.Remove("X-Test-User");
|
||||
using var anonymous = await client.PostAsJsonAsync("/api/v1/shop/GetPddClients", new { });
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, anonymous.StatusCode);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
client.Dispose();
|
||||
await app.DisposeAsync();
|
||||
context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShopTestAuthentication(IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
|
||||
{
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Request.Headers.TryGetValue("X-Test-User", out var name))
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.UserName, name.ToString())], Scheme.Name));
|
||||
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name)));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user