From c4149af23a32410d4ae8b63cd51a65feb0cf8562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ren=C3=A9=20White=20Enciso?= Date: Wed, 25 Feb 2026 13:13:56 -0600 Subject: [PATCH] feat(furniture-domain): enforce availability decision invariants --- docs/migration/behavior-invariants.md | 2 + .../FurnitureAvailabilityDecisionService.cs | 11 +++++- ...rnitureAvailabilityDecisionServiceTests.cs | 38 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/migration/behavior-invariants.md b/docs/migration/behavior-invariants.md index 0233310..ba3889c 100644 --- a/docs/migration/behavior-invariants.md +++ b/docs/migration/behavior-invariants.md @@ -4,6 +4,8 @@ - Availability decision outcome remains unchanged for equivalent inputs. - Correlation propagation behavior remains unchanged. - Transport contracts stay stable at service boundary. +- Missing display names map to `Unknown Furniture`. +- Negative inventory quantities are clamped to `0`. ## Validation Approach - Compare pre/post extraction contract examples. diff --git a/src/Furniture.Domain/Decisions/FurnitureAvailabilityDecisionService.cs b/src/Furniture.Domain/Decisions/FurnitureAvailabilityDecisionService.cs index c2337ef..c9a9a24 100644 --- a/src/Furniture.Domain/Decisions/FurnitureAvailabilityDecisionService.cs +++ b/src/Furniture.Domain/Decisions/FurnitureAvailabilityDecisionService.cs @@ -38,10 +38,17 @@ public sealed class FurnitureAvailabilityDecisionService : IFurnitureAvailabilit ProductContractResponse catalogResponse, InventoryItemLookupResponse inventoryResponse) { + var displayName = string.IsNullOrWhiteSpace(catalogResponse.DisplayName) + ? "Unknown Furniture" + : catalogResponse.DisplayName; + var quantityAvailable = inventoryResponse.QuantityAvailable < 0 + ? 0 + : inventoryResponse.QuantityAvailable; + return new FurnitureAvailabilityDecisionResponse( request.FurnitureId, - catalogResponse.DisplayName, - inventoryResponse.QuantityAvailable); + displayName, + quantityAvailable); } private static string ResolveCorrelationId(string correlationId) diff --git a/tests/Furniture.Domain.UnitTests/FurnitureAvailabilityDecisionServiceTests.cs b/tests/Furniture.Domain.UnitTests/FurnitureAvailabilityDecisionServiceTests.cs index 67c3b20..3dbc098 100644 --- a/tests/Furniture.Domain.UnitTests/FurnitureAvailabilityDecisionServiceTests.cs +++ b/tests/Furniture.Domain.UnitTests/FurnitureAvailabilityDecisionServiceTests.cs @@ -51,4 +51,42 @@ public class FurnitureAvailabilityDecisionServiceTests Assert.Equal("Chair", response.DisplayName); Assert.Equal(12, response.QuantityAvailable); } + + [Fact] + public void ComposeResponse_WhenCatalogDisplayNameMissing_UsesFallbackName() + { + var service = new FurnitureAvailabilityDecisionService(); + var request = new FurnitureAvailabilityDecisionRequest("FUR-004", "corr-004"); + var catalog = new ProductContractResponse( + new CatalogContractEnvelope("1.0.0", "corr-004"), + "FUR-004", + string.Empty); + var inventory = new InventoryItemLookupResponse( + new InventoryContractEnvelope("1.0.0", "corr-004"), + "FUR-004", + 5); + + var response = service.ComposeResponse(request, catalog, inventory); + + Assert.Equal("Unknown Furniture", response.DisplayName); + } + + [Fact] + public void ComposeResponse_WhenInventoryNegative_ClampsQuantityToZero() + { + var service = new FurnitureAvailabilityDecisionService(); + var request = new FurnitureAvailabilityDecisionRequest("FUR-005", "corr-005"); + var catalog = new ProductContractResponse( + new CatalogContractEnvelope("1.0.0", "corr-005"), + "FUR-005", + "Shelf"); + var inventory = new InventoryItemLookupResponse( + new InventoryContractEnvelope("1.0.0", "corr-005"), + "FUR-005", + -3); + + var response = service.ComposeResponse(request, catalog, inventory); + + Assert.Equal(0, response.QuantityAvailable); + } }