Project · Testing

JSON request boundary lab

Check the actual local serializer and independent domain validation against explicit input fixtures. No remote service or full runner is simulated.

All C# source files below are loaded together in the playground. Files written by a run live in its virtual directory.

Program.cs

var checks = BoundaryChecks.Run();
foreach (var check in checks) Console.WriteLine($"{check.Name}:{(check.Passed ? "PASS" : "FAIL")}");
Console.WriteLine($"passed:{checks.Count(check => check.Passed)}/{checks.Count}");
return checks.All(check => check.Passed) ? 0 : 1;

Domain/Request.cs

public sealed record Request(string Code, int Quantity);
public sealed record DecodeResult(bool Success, Request? Value, string Error);

Domain/QuantityRules.cs

public static class QuantityRules
{
    public static bool Allowed(int quantity) => quantity >= 1 && quantity <= 5;
}

Adapters/JsonRequestCodec.cs

using System.Text.Json;
public static class JsonRequestCodec
{
    private sealed record RequestDto(string? Code, int Quantity);
    private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
    public static DecodeResult Decode(string json)
    {
        RequestDto? data;
        try { data = JsonSerializer.Deserialize<RequestDto>(json, Options); }
        catch (JsonException) { return new(false, null, "representation"); }
        if (data is null) return new(false, null, "shape");
        if (string.IsNullOrWhiteSpace(data.Code)) return new(false, null, "code");
        if (!QuantityRules.Allowed(data.Quantity)) return new(false, null, "quantity");
        return new(true, new Request(data.Code, data.Quantity), "");
    }
}

Checks/CheckResult.cs

public sealed record CheckResult(string Name, bool Passed);

Checks/BoundaryChecks.cs

public static class BoundaryChecks
{
    private sealed record Case(string Name, string Json, bool ExpectedSuccess, string ExpectedCode, int ExpectedQuantity, string ExpectedError);
    public static List<CheckResult> Run()
    {
        var cases = new[]
        {
            new Case("valid", "{\"code\":\"A1\",\"quantity\":2}", true, "A1", 2, ""),
            new Case("malformed", "{", false, "", 0, "representation"),
            new Case("blank-code", "{\"code\":\"\",\"quantity\":2}", false, "", 0, "code"),
            new Case("missing-quantity", "{\"code\":\"A1\"}", false, "", 0, "quantity"),
            new Case("excess", "{\"code\":\"A1\",\"quantity\":6}", false, "", 0, "quantity"),
            new Case("null-root", "null", false, "", 0, "shape"),
            new Case("quoted-quantity", "{\"code\":\"A1\",\"quantity\":\"2\"}", false, "", 0, "representation"),
            new Case("exact-limit", "{\"code\":\"A1\",\"quantity\":5}", true, "A1", 5, "")
        };
        var results = new List<CheckResult>();
        foreach (var item in cases)
        {
            DecodeResult actual = JsonRequestCodec.Decode(item.Json);
            bool correct = actual.Success == item.ExpectedSuccess && actual.Error == item.ExpectedError;
            if (item.ExpectedSuccess) correct &= actual.Value?.Code == item.ExpectedCode && actual.Value?.Quantity == item.ExpectedQuantity;
            else correct &= actual.Value is null;
            results.Add(new(item.Name, correct));
        }
        return results;
    }
}

JsonBoundaryLab.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>