using System.Net; using System.Text; using Kitchen.Ops.Bff.Application.Adapters; using Kitchen.Ops.Bff.Contracts.Requests; namespace Kitchen.Ops.Bff.Application.UnitTests; public sealed class KitchenWorkflowServiceClientTests { [Fact] public async Task FetchBoardAsync_MapsBoardLanesAndStations() { var adapter = new KitchenWorkflowServiceClient(CreateClient(BoardPayload)); var response = await adapter.FetchBoardAsync(new GetKitchenOpsBoardRequest("demo-context"), CancellationToken.None); Assert.NotEmpty(response.Lanes); Assert.NotEmpty(response.AvailableStations); } [Fact] public async Task ClaimWorkItemAsync_MapsClaimResponse() { var adapter = new KitchenWorkflowServiceClient(CreateClient(""" { "contextId": "demo-context", "workItemId": "WK-1001", "claimed": true, "claimedBy": "chef-maya", "message": "Work item WK-1001 claimed by chef-maya." } """)); var response = await adapter.ClaimWorkItemAsync( new ClaimKitchenWorkItemRequest("demo-context", "WK-1001", "chef-maya"), CancellationToken.None); Assert.True(response.Claimed); Assert.Equal("chef-maya", response.ClaimedBy); } [Fact] public async Task ReleaseWorkItemAsync_ProjectsReleaseMessage() { var adapter = new KitchenWorkflowServiceClient(CreateClient(""" { "contextId": "demo-context", "workItemId": "WK-1001", "claimed": true, "claimedBy": "chef-maya", "message": "Work item WK-1001 claimed by chef-maya." } """)); var response = await adapter.ReleaseWorkItemAsync( new ReleaseKitchenWorkItemRequest("demo-context", "WK-1001", "chef-maya"), CancellationToken.None); Assert.True(response.Released); Assert.Contains("release requested", response.Message); } [Fact] public async Task TransitionWorkItemAsync_MapsTransitionResponse() { var adapter = new KitchenWorkflowServiceClient(CreateClient(""" { "orderId": "ORD-1001", "ticketId": "KT-1001", "previousState": "Queued", "currentState": "Preparing", "transitioned": true, "error": null } """)); var response = await adapter.TransitionWorkItemAsync( new TransitionKitchenWorkItemRequest("ORD-1001", "KT-1001", "Cooking", "chef-maya", "demo-context"), CancellationToken.None); Assert.True(response.Transitioned); Assert.Equal("Cooking", response.CurrentState); } [Fact] public async Task SetOrderPriorityAsync_MapsPriorityResponse() { var adapter = new KitchenWorkflowServiceClient(CreateClient(""" { "contextId": "demo-context", "workItemId": "WK-1001", "priority": 5, "message": "Priority for WK-1001 updated to 5 by chef-maya." } """)); var response = await adapter.SetOrderPriorityAsync( new SetKitchenOrderPriorityRequest("demo-context", "WK-1001", 5, "chef-maya"), CancellationToken.None); Assert.True(response.Updated); Assert.Equal(5, response.Priority); } private static HttpClient CreateClient(string json) { return new HttpClient(new StubHttpMessageHandler(json)) { BaseAddress = new Uri("http://kitchen-service:8080/") }; } private sealed class StubHttpMessageHandler(string json) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(json, Encoding.UTF8, "application/json") }); } } private const string BoardPayload = """ { "contextId": "demo-context", "summary": "Kitchen board now reflects persisted tickets linked to shared restaurant orders.", "lanes": [ { "lane": "queued", "items": [ { "workItemId": "WK-1001", "orderId": "ORD-1001", "ticketId": "KT-1001", "tableId": "T-08", "station": "hot-line", "state": "Queued", "priority": 3, "claimedBy": null, "etaMinutes": 12 } ] } ], "availableStations": [ "hot-line", "grill", "pickup" ] } """; }