using System.Reflection; using DodoSSH.Contracts; namespace DodoSSH.Contracts.Tests; /// /// Problem codes are part of the public wire contract: the client switches on them. /// public sealed class ProblemCodesTests { private static IReadOnlyList CodeFields => [.. typeof(ProblemCodes) .GetFields(BindingFlags.Public | BindingFlags.Static) .Where(f => f.IsLiteral && f.FieldType == typeof(string)) .Where(f => !string.Equals(f.Name, nameof(ProblemCodes.TypeBaseUri), StringComparison.Ordinal))]; [Fact] public void AllCodes_AreUnique() { // A duplicated value would make two distinct failures indistinguishable to the // client, which is the whole point of having codes rather than parsing messages. var values = CodeFields.Select(f => (string)f.GetRawConstantValue()!).ToList(); values.Distinct(StringComparer.Ordinal).Count().ShouldBe(values.Count); } [Fact] public void AllCodes_AreKebabCase() { // Codes are appended to TypeBaseUri to form the ProblemDetails type URI, so they // must be URL-safe and stylistically consistent. foreach (var field in CodeFields) { var value = (string)field.GetRawConstantValue()!; value.ShouldMatch("^[a-z][a-z0-9]*(-[a-z0-9]+)*$", $"{field.Name} is not kebab-case"); } } [Fact] public void TypeBaseUri_IsAnAbsoluteHttpsUriEndingInSlash() { Uri.TryCreate(ProblemCodes.TypeBaseUri, UriKind.Absolute, out var uri).ShouldBeTrue(); uri!.Scheme.ShouldBe(Uri.UriSchemeHttps); ProblemCodes.TypeBaseUri.ShouldEndWith("/"); } }