using NewLife.Http;
using Xunit;
namespace XUnitTest.Http;
/// <summary>傿•°åŒ–路由器测试:{param} / {param?} / {*path} 的段对é½ä¸Žå‚æ•°æå–</summary>
public class HttpRouterTests
{
private sealed class StubHandler : IHttpHandler
{
public Task ProcessRequestAsync(IHttpContext context) => TaskEx.CompletedTask;
}
[Fact(DisplayName = "路由器_{id}傿•°æ®µ_命ä¸å¹¶æå–傿•°")]
public void ParameterizedRoute_Matches()
{
var handler = new StubHandler();
var router = new HttpRouter();
router.Register("/api/users/{id}", handler);
var ps = new Dictionary<String, Object?>();
Assert.Same(handler, router.Match("/api/users/123", ps));
Assert.Equal("123", ps["id"]);
}
[Fact(DisplayName = "路由器_å—颿®µå¤šçš„æ¨¡å¼ä¼˜å…ˆ_宽泛模å¼ä¸é®è”½å…·ä½“模å¼")]
public void MoreLiterals_Wins()
{
// åŒç±»æ¨¡å¼ï¼ˆå‚数化路由)之间æ¤å‰æŒ‰æ³¨å†Œé¡ºåºå‘½ä¸ï¼šå…ˆæ³¨å†Œçš„宽泛模å¼ä¼šé®ä½åŽæ³¨å†Œçš„具体模å¼
var wide = new StubHandler();
var specific = new StubHandler();
var router = new HttpRouter();
router.Register("/api/{controller}/{id}", wide);
router.Register("/api/users/{id}", specific);
var ps = new Dictionary<String, Object?>();
Assert.Same(specific, router.Match("/api/users/123", ps));
Assert.Equal("123", ps["id"]);
// ä¸åŒ¹é…å…·ä½“æ¨¡å¼æ—¶ä»ç”±å®½æ³›æ¨¡å¼å…œåº•
var ps2 = new Dictionary<String, Object?>();
Assert.Same(wide, router.Match("/api/orders/9", ps2));
Assert.Equal("orders", ps2["controller"]);
}
[Fact(DisplayName = "路由器_è·¯å¾„å‚æ•°ç™¾åˆ†å·è§£ç _与查询串å£å¾„一致")]
public void ParameterValue_UrlDecoded()
{
var router = new HttpRouter();
router.Register("/api/files/{name}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.NotNull(router.Match("/api/files/a%20b%2Fc", ps));
Assert.Equal("a b/c", ps["name"]);
}
[Fact(DisplayName = "路由器_通é…傿•°é€æ®µè§£ç _段内%2F䏿”¹æ®µè¾¹ç•Œ")]
public void WildcardValue_EachSegmentDecoded()
{
var router = new HttpRouter();
router.Register("/files/{*path}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.NotNull(router.Match("/files/a%2Fb/c%20d", ps));
Assert.Equal("a/b/c d", ps["path"]);
}
[Fact(DisplayName = "路由器_å¤šå‚æ•°æ®µ_{controller}{action}_逿®µæå–")]
public void MultipleParameters_Extracted()
{
var router = new HttpRouter();
router.Register("/api/{controller}/{action}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.NotNull(router.Match("/api/user/info", ps));
Assert.Equal("user", ps["controller"]);
Assert.Equal("info", ps["action"]);
}
[Fact(DisplayName = "路由器_å¤±è´¥å€™é€‰å‚æ•°ä¸æ®‹ç•™_䏿±¡æŸ“命ä¸è·¯ç”±")]
public void FailedCandidate_NoStaleParameters()
{
var hit = new StubHandler();
var router = new HttpRouter();
// 先注册的候选会先写入 id=5,å†å› å—颿®µ detail ä¸åŒ¹é… info 而失败
router.Register("/api/{id}/detail", new StubHandler());
router.Register("/api/{ver}/info", hit);
var ps = new Dictionary<String, Object?>();
Assert.Same(hit, router.Match("/api/5/info", ps));
// 失败候选残留的 {id} ä¸å¾—å‡ºçŽ°åœ¨å‚æ•°å—å…¸ï¼šå®ƒä¼šåœ¨è·¯ç”±å‚æ•°æœ€åŽåˆå¹¶æ—¶è¦†ç›–åŒåæŸ¥è¯¢å‚æ•°
Assert.False(ps.ContainsKey("id"));
Assert.Equal("5", ps["ver"]);
}
[Fact(DisplayName = "路由器_{id?}å¯é€‰å‚数段_çœç•¥æ—¶å‘½ä¸ä¸”傿•°ä¸ºç©º")]
public void OptionalRoute_MatchesWithoutSegment()
{
var router = new HttpRouter();
router.Register("/api/users/{id?}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.NotNull(router.Match("/api/users", ps));
Assert.Null(ps["id"]);
Assert.NotNull(router.Match("/api/users/5", ps));
Assert.Equal("5", ps["id"]);
}
[Fact(DisplayName = "路由器_{*path}é€šé…æ®µ_æ•获剩余多段路径")]
public void WildcardRoute_CapturesRemainingSegments()
{
var router = new HttpRouter();
router.Register("/api/files/{*path}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.NotNull(router.Match("/api/files/a/b/c.txt", ps));
Assert.Equal("a/b/c.txt", ps["path"]);
}
[Fact(DisplayName = "路由器_å—颿®µä¸åŒæˆ–段数ä¸ç¬¦_ä¸å‘½ä¸")]
public void LiteralMismatch_ReturnsNull()
{
var router = new HttpRouter();
router.Register("/api/users/{id}", new StubHandler());
var ps = new Dictionary<String, Object?>();
Assert.Null(router.Match("/api/orders/123", ps));
Assert.Null(router.Match("/api/users/123/extra", ps));
Assert.Null(router.Match("/api", ps));
}
}
|