43 lines
1.9 KiB
C#
43 lines
1.9 KiB
C#
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.Routing;
|
|
using Xunit;
|
|
|
|
namespace JobTrackerApi.Tests;
|
|
|
|
public sealed class RouteUniquenessTests
|
|
{
|
|
[Fact]
|
|
public void Controller_actions_have_one_handler_per_http_method_and_route()
|
|
{
|
|
var actions = typeof(JobTrackerApi.Controllers.JobApplicationsController).Assembly.GetTypes()
|
|
.Where(type => !type.IsAbstract && typeof(ControllerBase).IsAssignableFrom(type))
|
|
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
|
.SelectMany(method => method.GetCustomAttributes<HttpMethodAttribute>()
|
|
.Select(attribute => new
|
|
{
|
|
Action = $"{type.Name}.{method.Name}",
|
|
Methods = string.Join(",", attribute.HttpMethods.OrderBy(x => x)),
|
|
Route = Normalize(type, attribute.Template),
|
|
})))
|
|
.ToList();
|
|
|
|
var duplicates = actions
|
|
.GroupBy(x => $"{x.Methods} {x.Route}", StringComparer.OrdinalIgnoreCase)
|
|
.Where(group => group.Count() > 1)
|
|
.Select(group => $"{group.Key}: {string.Join(", ", group.Select(x => x.Action))}")
|
|
.ToList();
|
|
|
|
Assert.Empty(duplicates);
|
|
}
|
|
|
|
private static string Normalize(Type controller, string? actionTemplate)
|
|
{
|
|
var prefix = controller.GetCustomAttribute<RouteAttribute>()?.Template ?? string.Empty;
|
|
prefix = prefix.Replace("[controller]", controller.Name[..^"Controller".Length], StringComparison.OrdinalIgnoreCase);
|
|
var route = $"{prefix.TrimEnd('/')}/{(actionTemplate ?? string.Empty).TrimStart('/')}".ToLowerInvariant();
|
|
return Regex.Replace(route, @"\{[^}:]+(?<constraint>:[^}]+)?\}", match => $"{{parameter{match.Groups["constraint"].Value}}}");
|
|
}
|
|
}
|