pdo = new PDO('sqlite::memory:'); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->sqliteCreateFunction('NOW', static fn(): string => date('Y-m-d H:i:s')); $this->pdo->exec( 'CREATE TABLE orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, status_code TEXT, payment_status INTEGER, total_paid REAL, is_canceled_by_buyer INTEGER DEFAULT 0, source_updated_at TEXT, payload_json TEXT, fetched_at TEXT, updated_at TEXT )' ); $this->repository = new OrderImportRepository($this->pdo); } public function testTotalPaidPreservedWhenPaymentStatusUnchanged(): void { $this->seedOrder([ 'status_code' => 'wyslane', 'payment_status' => 2, 'total_paid' => 91.00, 'is_canceled_by_buyer' => 0, 'payload_json' => '{"a":1}', 'source_updated_at' => '2026-05-01 10:00:00', 'fetched_at' => '2026-05-01 10:00:00', ]); $orderData = [ 'status_code' => 'wyslane', 'payment_status' => 2, 'total_paid' => 119.00, 'is_canceled_by_buyer' => 0, 'source_updated_at' => '2026-05-10 12:00:00', 'payload_json' => ['a' => 2], 'fetched_at' => '2026-05-10 12:00:00', ]; $this->invokeUpdateOrderDelta(1, $orderData, true, false); $row = $this->fetchOrder(1); self::assertSame('91', $this->normalizeAmount($row['total_paid']), 'total_paid must remain at manually corrected value'); self::assertSame(2, (int) $row['payment_status']); self::assertSame('2026-05-10 12:00:00', $row['source_updated_at']); } public function testTotalPaidUpdatedOnPaymentTransition(): void { $this->seedOrder([ 'status_code' => 'nieoplacone', 'payment_status' => 0, 'total_paid' => 0.00, 'is_canceled_by_buyer' => 0, 'payload_json' => '{}', 'source_updated_at' => '2026-05-01 10:00:00', 'fetched_at' => '2026-05-01 10:00:00', ]); $orderData = [ 'status_code' => 'w_realizacji', 'payment_status' => 2, 'total_paid' => 119.00, 'is_canceled_by_buyer' => 0, 'source_updated_at' => '2026-05-10 12:00:00', 'payload_json' => ['a' => 1], 'fetched_at' => '2026-05-10 12:00:00', ]; $this->invokeUpdateOrderDelta(1, $orderData, false, false); $row = $this->fetchOrder(1); self::assertSame('119', $this->normalizeAmount($row['total_paid'])); self::assertSame(2, (int) $row['payment_status']); } public function testIsCanceledByBuyerPropagatedOnSourceCancelEvenWhenPaymentStable(): void { $this->seedOrder([ 'status_code' => 'wyslane', 'payment_status' => 2, 'total_paid' => 91.00, 'is_canceled_by_buyer' => 0, 'payload_json' => '{}', 'source_updated_at' => '2026-05-01 10:00:00', 'fetched_at' => '2026-05-01 10:00:00', ]); $orderData = [ 'status_code' => 'anulowane', 'payment_status' => 2, 'total_paid' => 119.00, 'is_canceled_by_buyer' => 1, 'source_updated_at' => '2026-05-10 12:00:00', 'payload_json' => ['a' => 9], 'fetched_at' => '2026-05-10 12:00:00', ]; $this->invokeUpdateOrderDelta(1, $orderData, true, true); $row = $this->fetchOrder(1); self::assertSame(1, (int) $row['is_canceled_by_buyer'], 'source cancellation must propagate'); self::assertSame('anulowane', $row['status_code']); self::assertSame('91', $this->normalizeAmount($row['total_paid']), 'total_paid still protected because payment_status unchanged'); } /** * @param array $orderData */ private function invokeUpdateOrderDelta(int $orderId, array $orderData, bool $paymentStatusUnchanged, bool $cancelledBySource): void { $method = new ReflectionMethod(OrderImportRepository::class, 'updateOrderDelta'); $method->setAccessible(true); $method->invoke($this->repository, $orderId, $orderData, $paymentStatusUnchanged, $cancelledBySource); } /** * @param array $values */ private function seedOrder(array $values): void { $columns = array_keys($values); $placeholders = array_map(static fn(string $c): string => ':' . $c, $columns); $sql = 'INSERT INTO orders (' . implode(', ', $columns) . ', updated_at) VALUES (' . implode(', ', $placeholders) . ", '2026-05-01 10:00:00')"; $stmt = $this->pdo->prepare($sql); $stmt->execute($values); } /** * @return array */ private function fetchOrder(int $id): array { $stmt = $this->pdo->prepare('SELECT * FROM orders WHERE id = :id'); $stmt->execute(['id' => $id]); $row = $stmt->fetch(PDO::FETCH_ASSOC); self::assertIsArray($row); return $row; } private function normalizeAmount(mixed $value): string { return (string) (float) $value; } }