184 lines
7.1 KiB
C#
184 lines
7.1 KiB
C#
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using CampusErrand.Data;
|
|
using CampusErrand.Models.Dtos;
|
|
using FsCheck;
|
|
using FsCheck.Xunit;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace CampusErrand.Tests;
|
|
|
|
/// <summary>
|
|
/// Property 25: Banner 创建校验
|
|
/// 对任意 Banner 创建或更新请求,图片地址不为空、链接类型为合法枚举值、链接地址不为空时应成功;否则应返回校验错误。
|
|
/// **Feature: login-and-homepage, Property 25: Banner 创建校验**
|
|
/// **Validates: Requirements 30.6, 30.7**
|
|
/// </summary>
|
|
public class BannerValidationPropertyTests : IDisposable
|
|
{
|
|
private const string JwtSecret = "YourSuperSecretKeyForJwtTokenGeneration_AtLeast32Chars!";
|
|
private const string JwtIssuer = "CampusErrand";
|
|
private const string JwtAudience = "CampusErrandApp";
|
|
|
|
private readonly List<WebApplicationFactory<Program>> _factories = [];
|
|
|
|
private WebApplicationFactory<Program> CreateFactory(string dbName)
|
|
{
|
|
var factory = new WebApplicationFactory<Program>()
|
|
.WithWebHostBuilder(builder =>
|
|
{
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
var efDescriptors = services
|
|
.Where(d =>
|
|
d.ServiceType.FullName?.Contains("EntityFrameworkCore") == true
|
|
|| d.ServiceType == typeof(DbContextOptions<AppDbContext>)
|
|
|| d.ServiceType == typeof(DbContextOptions)
|
|
|| d.ImplementationType?.FullName?.Contains("EntityFrameworkCore") == true)
|
|
.ToList();
|
|
foreach (var d in efDescriptors) services.Remove(d);
|
|
|
|
services.AddDbContext<AppDbContext>(options =>
|
|
options.UseInMemoryDatabase(dbName));
|
|
});
|
|
});
|
|
_factories.Add(factory);
|
|
return factory;
|
|
}
|
|
|
|
private static string GenerateAdminToken()
|
|
{
|
|
var key = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(
|
|
System.Text.Encoding.UTF8.GetBytes(JwtSecret));
|
|
var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(
|
|
key, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256);
|
|
var claims = new[]
|
|
{
|
|
new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.NameIdentifier, "1"),
|
|
new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, "Admin")
|
|
};
|
|
var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
|
|
issuer: JwtIssuer, audience: JwtAudience, claims: claims,
|
|
expires: DateTime.UtcNow.AddHours(1), signingCredentials: credentials);
|
|
return new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(token);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 属性:合法的 Banner 请求(非空图片、合法链接类型、非空链接地址)应创建成功
|
|
/// </summary>
|
|
[Property(MaxTest = 20)]
|
|
public bool 合法Banner请求应创建成功(PositiveInt sortOrder, bool isExternal)
|
|
{
|
|
var dbName = $"banner_valid_{Guid.NewGuid()}";
|
|
using var factory = CreateFactory(dbName);
|
|
var client = factory.CreateClient();
|
|
client.DefaultRequestHeaders.Authorization =
|
|
new AuthenticationHeaderValue("Bearer", GenerateAdminToken());
|
|
|
|
var request = new BannerRequest
|
|
{
|
|
ImageUrl = $"https://img.example.com/{sortOrder.Get}.png",
|
|
LinkType = isExternal ? "External" : "Internal",
|
|
LinkUrl = isExternal ? "https://example.com" : "/pages/index/index",
|
|
SortOrder = sortOrder.Get,
|
|
IsEnabled = true
|
|
};
|
|
|
|
var response = client.PostAsJsonAsync("/api/admin/banners", request).Result;
|
|
return response.StatusCode == HttpStatusCode.Created;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 属性:图片地址为空时应返回 400
|
|
/// </summary>
|
|
[Property(MaxTest = 20)]
|
|
public bool 图片地址为空时返回400(PositiveInt sortOrder)
|
|
{
|
|
var dbName = $"banner_noimg_{Guid.NewGuid()}";
|
|
using var factory = CreateFactory(dbName);
|
|
var client = factory.CreateClient();
|
|
client.DefaultRequestHeaders.Authorization =
|
|
new AuthenticationHeaderValue("Bearer", GenerateAdminToken());
|
|
|
|
var request = new BannerRequest
|
|
{
|
|
ImageUrl = "",
|
|
LinkType = "External",
|
|
LinkUrl = "https://example.com",
|
|
SortOrder = sortOrder.Get,
|
|
IsEnabled = true
|
|
};
|
|
|
|
var response = client.PostAsJsonAsync("/api/admin/banners", request).Result;
|
|
return response.StatusCode == HttpStatusCode.BadRequest;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 属性:链接地址为空时应返回 400
|
|
/// </summary>
|
|
[Property(MaxTest = 20)]
|
|
public bool 链接地址为空时返回400(PositiveInt sortOrder)
|
|
{
|
|
var dbName = $"banner_nolink_{Guid.NewGuid()}";
|
|
using var factory = CreateFactory(dbName);
|
|
var client = factory.CreateClient();
|
|
client.DefaultRequestHeaders.Authorization =
|
|
new AuthenticationHeaderValue("Bearer", GenerateAdminToken());
|
|
|
|
var request = new BannerRequest
|
|
{
|
|
ImageUrl = "https://img.example.com/test.png",
|
|
LinkType = "External",
|
|
LinkUrl = "",
|
|
SortOrder = sortOrder.Get,
|
|
IsEnabled = true
|
|
};
|
|
|
|
var response = client.PostAsJsonAsync("/api/admin/banners", request).Result;
|
|
return response.StatusCode == HttpStatusCode.BadRequest;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 属性:链接类型不合法时应返回 400
|
|
/// </summary>
|
|
[Property(MaxTest = 20)]
|
|
public bool 链接类型不合法时返回400(NonEmptyString invalidType)
|
|
{
|
|
// 跳过合法的枚举值(名称和数字形式)
|
|
var val = invalidType.Get;
|
|
if (val.Equals("External", StringComparison.OrdinalIgnoreCase) ||
|
|
val.Equals("Internal", StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
// 跳过能被解析为已定义枚举值的数字字符串
|
|
if (Enum.TryParse<CampusErrand.Models.LinkType>(val, true, out var parsed) && Enum.IsDefined(parsed))
|
|
return true;
|
|
|
|
var dbName = $"banner_badtype_{Guid.NewGuid()}";
|
|
using var factory = CreateFactory(dbName);
|
|
var client = factory.CreateClient();
|
|
client.DefaultRequestHeaders.Authorization =
|
|
new AuthenticationHeaderValue("Bearer", GenerateAdminToken());
|
|
|
|
var request = new BannerRequest
|
|
{
|
|
ImageUrl = "https://img.example.com/test.png",
|
|
LinkType = val,
|
|
LinkUrl = "https://example.com",
|
|
SortOrder = 1,
|
|
IsEnabled = true
|
|
};
|
|
|
|
var response = client.PostAsJsonAsync("/api/admin/banners", request).Result;
|
|
return response.StatusCode == HttpStatusCode.BadRequest;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (var f in _factories) f.Dispose();
|
|
_factories.Clear();
|
|
}
|
|
}
|