'Widget', 'quantity' => 2, 'unitPriceInCents' => 1500, 'currency' => 'EUR'], ], )); $order = ($getHandler)(new GetOrderById($orderId)); $view = $assembler->toView($order); // Verifie que le retour est un DTO du Published Language self::assertInstanceOf(OrderView::class, $view); self::assertSame($orderId, $view->orderId); self::assertSame('placed', $view->status); self::assertSame(3000, $view->totalInCents); self::assertSame('EUR', $view->currency); self::assertCount(1, $view->lines); } public function test_order_view_does_not_expose_internal_model(): void { $assembler = new OrderViewAssembler(); $orderRepo = new InMemoryOrderRepository(); $clock = new SystemClock(); $publisher = new class implements \MiniShop\Sales\Application\Port\SalesEventPublisher { public function publishOrderPlaced(\MiniShop\Sales\Domain\Event\OrderPlaced $event): void {} public function publishOrderConfirmed(\MiniShop\Sales\Domain\Event\OrderConfirmed $event): void {} public function publishOrderCancelled(\MiniShop\Sales\Domain\Event\OrderCancelled $event): void {} }; ($placeHandler = new PlaceOrderHandler($orderRepo, $publisher, $clock))(new PlaceOrder( orderId: 'ohs-test-002', customerId: 'cust-001', lines: [['productName' => 'Widget', 'quantity' => 1, 'unitPriceInCents' => 1000, 'currency' => 'EUR']], )); $order = (new GetOrderByIdHandler($orderRepo))(new GetOrderById('ohs-test-002')); $view = $assembler->toView($order); $json = json_encode($view, JSON_THROW_ON_ERROR); $decoded = json_decode($json, true, flags: JSON_THROW_ON_ERROR); // Le JSON ne doit contenir que les champs du Published Language $allowedKeys = ['orderId', 'customerId', 'status', 'totalInCents', 'currency', 'lines']; self::assertSame($allowedKeys, array_keys($decoded)); // Les lignes ne doivent pas contenir de types internes (Money, OrderLine) $lineKeys = array_keys($decoded['lines'][0]); self::assertContains('productName', $lineKeys); self::assertContains('lineTotalInCents', $lineKeys); self::assertNotContains('unitPrice', $lineKeys); // pas d'objet Money } }