createMock(OrderRepository::class); } return new OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo); } public function testConstructorAcceptsOnlyOrderRepository(): void { $orderRepo = $this->createMock(OrderRepository::class); $service = new OrderAdminService($orderRepo); $this->assertInstanceOf(OrderAdminService::class, $service); } public function testConstructorAcceptsAllDependencies(): void { $orderRepo = $this->createMock(OrderRepository::class); $productRepo = $this->createMock(ProductRepository::class); $settingsRepo = $this->createMock(SettingsRepository::class); $transportRepo = $this->createMock(TransportRepository::class); $service = new OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo); $this->assertInstanceOf(OrderAdminService::class, $service); } public function testSearchProductsReturnsEmptyForEmptyQuery(): void { $productRepo = $this->createMock(ProductRepository::class); $productRepo->expects($this->never())->method('searchProductByNameAjax'); $service = $this->createService(null, $productRepo); $result = $service->searchProducts('', 'pl'); $this->assertSame([], $result); } public function testSearchProductsReturnsEmptyWithoutProductRepo(): void { $service = $this->createService(); $result = $service->searchProducts('test', 'pl'); $this->assertSame([], $result); } public function testSearchProductsReturnsFormattedResults(): void { $productRepo = $this->createMock(ProductRepository::class); $productRepo->method('searchProductByNameAjax') ->with('koszulka', 'pl') ->willReturn([ ['product_id' => 10], ['product_id' => 20], ]); $productRepo->method('findCached') ->willReturnCallback(function ($id) { if ($id === 10) { return [ 'language' => ['name' => 'Koszulka biała'], 'sku' => 'KB-001', 'ean' => '', 'price_brutto' => 49.99, 'price_brutto_promo' => 39.99, 'vat' => 23, 'quantity' => 15, 'parent_id' => 0, ]; } return null; // product 20 not found }); $productRepo->method('getProductImg') ->willReturn('/images/products/test.jpg'); $service = $this->createService(null, $productRepo); $results = $service->searchProducts('koszulka', 'pl'); $this->assertCount(1, $results); $this->assertSame(10, $results[0]['product_id']); $this->assertSame('Koszulka biała', $results[0]['name']); $this->assertSame('KB-001', $results[0]['sku']); $this->assertSame(49.99, $results[0]['price_brutto']); $this->assertSame(39.99, $results[0]['price_brutto_promo']); $this->assertSame(15, $results[0]['quantity']); } public function testSaveOrderProductsReturnsFalseForInvalidOrderId(): void { $service = $this->createService(); $this->assertFalse($service->saveOrderProducts(0, [])); } public function testSaveOrderProductsDeletesRemovedProducts(): void { $orderRepo = $this->createMock(OrderRepository::class); $productRepo = $this->createMock(ProductRepository::class); $settingsRepo = $this->createMock(SettingsRepository::class); $transportRepo = $this->createMock(TransportRepository::class); // Existing products $orderRepo->method('orderProducts') ->with(1) ->willReturn([ ['id' => 100, 'product_id' => 5, 'quantity' => 2, 'price_brutto' => 10, 'price_brutto_promo' => 0], ['id' => 101, 'product_id' => 6, 'quantity' => 1, 'price_brutto' => 20, 'price_brutto_promo' => 0], ]); // Product 100 is submitted with delete flag // Product 101 is not submitted at all (also deleted) $orderRepo->expects($this->exactly(2))->method('deleteOrderProduct'); $orderRepo->method('findRawById')->willReturn(['id' => 1, 'transport_id' => 1]); $transportRepo->method('findActiveById')->willReturn(null); // Stock should be returned for both deleted products $productRepo->method('getQuantity')->willReturn(10); $productRepo->expects($this->exactly(2))->method('updateQuantity'); $service = new OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo); $result = $service->saveOrderProducts(1, [ ['order_product_id' => 100, 'delete' => '1', 'quantity' => 2], ]); $this->assertTrue($result); } public function testSaveOrderProductsUpdatesQuantityAndAdjustsStock(): void { $orderRepo = $this->createMock(OrderRepository::class); $productRepo = $this->createMock(ProductRepository::class); $settingsRepo = $this->createMock(SettingsRepository::class); $transportRepo = $this->createMock(TransportRepository::class); // Existing: qty=3 $orderRepo->method('orderProducts') ->willReturn([ ['id' => 100, 'product_id' => 5, 'quantity' => 3, 'price_brutto' => 10, 'price_brutto_promo' => 0], ]); // Submit: qty=5 (increased by 2 → stock decreases by 2) $orderRepo->expects($this->once())->method('updateOrderProduct') ->with(100, $this->callback(function ($data) { return $data['quantity'] === 5; })); $orderRepo->method('findRawById')->willReturn(['id' => 1, 'transport_id' => 1]); $transportRepo->method('findActiveById')->willReturn(null); $productRepo->method('getQuantity')->with(5)->willReturn(20); $productRepo->expects($this->once())->method('updateQuantity') ->with(5, 18); // 20 + (3 - 5) = 18 $service = new OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo); $service->saveOrderProducts(1, [ ['order_product_id' => 100, 'product_id' => 5, 'quantity' => 5, 'price_brutto' => 10, 'price_brutto_promo' => 0], ]); } public function testSaveOrderProductsAddsNewProductAndDecreasesStock(): void { $orderRepo = $this->createMock(OrderRepository::class); $productRepo = $this->createMock(ProductRepository::class); $settingsRepo = $this->createMock(SettingsRepository::class); $transportRepo = $this->createMock(TransportRepository::class); $orderRepo->method('orderProducts')->willReturn([]); $orderRepo->expects($this->once())->method('addOrderProduct') ->with(1, $this->callback(function ($data) { return $data['product_id'] === 10 && $data['name'] === 'New Product' && $data['quantity'] === 2; })); $orderRepo->method('findRawById')->willReturn(['id' => 1, 'transport_id' => 1]); $transportRepo->method('findActiveById')->willReturn(null); $productRepo->method('getQuantity')->with(10)->willReturn(15); $productRepo->expects($this->once())->method('updateQuantity') ->with(10, 13); // 15 - 2 $service = new OrderAdminService($orderRepo, $productRepo, $settingsRepo, $transportRepo); $service->saveOrderProducts(1, [ [ 'order_product_id' => 0, 'product_id' => 10, 'parent_product_id' => 10, 'name' => 'New Product', 'vat' => 23, 'price_brutto' => 50, 'price_brutto_promo' => 0, 'quantity' => 2, ], ]); } public function testGetFreeDeliveryThresholdReturnsZeroWithoutSettingsRepo(): void { $service = $this->createService(); $this->assertSame(0.0, $service->getFreeDeliveryThreshold()); } public function testGetFreeDeliveryThresholdReturnsValue(): void { $settingsRepo = $this->createMock(SettingsRepository::class); $settingsRepo->method('getSingleValue') ->with('free_delivery') ->willReturn('150.00'); $service = $this->createService(null, null, $settingsRepo); $this->assertSame(150.0, $service->getFreeDeliveryThreshold()); } // ========================================================================= // queueApiloSync — DB-based via CronJobRepository // ========================================================================= public function testConstructorAcceptsCronJobRepo(): void { $orderRepo = $this->createMock(OrderRepository::class); $cronJobRepo = $this->createMock(CronJobRepository::class); $service = new OrderAdminService($orderRepo, null, null, null, $cronJobRepo); $this->assertInstanceOf(OrderAdminService::class, $service); } }