using System.Text.Json; public static class ContractLabs { public static void Map(WebApplication app) { app.MapGet("/contracts/compatible",()=>Results.Ok(new { id=1,name="rune",optional="new" })); app.MapGet("/contracts/breaking",()=>Results.Ok(new { id=1,name=42 })); app.MapGet("/contracts/effect",(Store store)=> { using var c=store.Open(); return new { value=Store.Number(c,"SELECT value FROM contract_effect WHERE id=1") }; }); app.MapPost("/contracts/change/{phase}",(string phase,Store store)=> { if(phase!="before"&&phase!="after")return Results.BadRequest(); using var c=store.Open(); using var tx=c.BeginTransaction(); Store.Execute(c,"UPDATE contract_effect SET value=value+1 WHERE id=1",tx); if(phase=="before") { tx.Rollback(); return Results.Problem(statusCode:503,title:"Controlled failure before commit"); } tx.Commit(); return Results.Problem(statusCode:503,title:"Controlled uncertain response after commit"); }); } public static bool Accept(JsonElement root) => root.ValueKind==JsonValueKind.Object && root.TryGetProperty("id",out var id)&&id.ValueKind==JsonValueKind.Number&&id.TryGetInt32(out int value)&&value>0&&root.TryGetProperty("name",out var name)&&name.ValueKind==JsonValueKind.String&&!string.IsNullOrWhiteSpace(name.GetString()); public static async Task VerifyAsync(string address) { var uri=new Uri(address); if(!uri.IsLoopback||uri.Scheme!="http"||uri.AbsolutePath!="/"||!string.IsNullOrEmpty(uri.UserInfo))throw new ArgumentException("Owned loopback root required"); using var client=new HttpClient { BaseAddress=uri,Timeout=TimeSpan.FromSeconds(5) }; int checks=0; foreach(var item in new[] { ("contracts/compatible",true),("contracts/breaking",false) }) { using var response=await client.GetAsync(item.Item1); response.EnsureSuccessStatusCode(); using var document=JsonDocument.Parse(await response.Content.ReadAsStringAsync()); if(Accept(document.RootElement)!=item.Item2)throw new Exception("Consumer contract expectation failed"); checks++; } async Task State() { using var response=await client.GetAsync("contracts/effect"); response.EnsureSuccessStatusCode(); using var document=JsonDocument.Parse(await response.Content.ReadAsStringAsync()); return document.RootElement.GetProperty("value").GetInt64(); } if(await State()!=0)throw new Exception("Use a fresh fixture"); checks++; using(var response=await client.PostAsync("contracts/change/before",null)) { if((int)response.StatusCode!=503)throw new Exception("Expected failure"); checks++; } if(await State()!=0)throw new Exception("Rollback failed"); checks++; using(var response=await client.PostAsync("contracts/change/after",null)) { if((int)response.StatusCode!=503)throw new Exception("Expected uncertain response"); checks++; } if(await State()!=1)throw new Exception("Commit state missing"); checks++; Console.WriteLine(JsonSerializer.Serialize(new { checks,transport="real owned loopback HTTP" })); } }