first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
<?php
/**
* Created by PhpStorm.
* User: ondra
* Date: 2.11.17
* Time: 16:07.
*/
namespace KupShop\DevelopmentBundle\Util\Tests;
use KupShop\OrderingBundle\Cart;
/**
* Trait CartTestTrait.
*/
trait CartTestTrait
{
use OrderFromPStateToggleTrait;
/** @var \Cart */
public $cart;
/** @var \Order */
public $order;
private $userKey = '123456test123456';
public function createCart()
{
$this->cart = $this->get(Cart::class);
\Cart::setCartID($this->userKey);
$this->visitStep('delivery');
return $this;
}
public function setCartUserKey(string $userKey): void
{
$this->userKey = $userKey;
}
public function prepareCart()
{
$this->createCart();
$this->setDeliveryType();
$this->setInvoice();
return $this->cart;
}
public function setDeliveryType($id = 1)
{
$this->cart->setTransport($id);
}
public function setInvoice()
{
$this->cart->updateAddresses([
'email' => 'test@wpj.cz',
'name' => 'Wpj',
'surname' => 'Wpj',
'street' => 'Wpj 123',
'city' => 'Vrchlabi',
'zip' => 12345,
'country' => 'CZ',
'phone' => '123456789',
], null);
}
public function insertProduct($id_product, $id_variation = null, $pieces = 1, $note = '')
{
$item = [
'id_product' => $id_product,
'id_variation' => $id_variation,
'pieces' => $pieces,
'note' => $note,
];
$itemId = $this->cart->addItem($item);
$this->assertGreaterThan(0, $itemId);
return $itemId;
}
public function visitStep($name)
{
$this->cart->findNextStep($name);
$this->cart->actualStep = $name;
}
public function checkOrderPriceIsSameAsCart()
{
$this->order = $this->submitOrder();
$this->assertEquals($this->cart->totalPricePay->asFloat(), $this->order->total_price->asFloat(), '', 0.01);
}
protected function loginUser($id_user)
{
$this->assertNull($this->cart, 'loginUser se musi volat jeste pred vytvorenim kosiku');
$user = \User::createFromId($id_user);
$user->activateUser();
}
public function saveCart()
{
$this->cart->save();
}
public function submitOrder(): \Order
{
$this->recalculateCart();
$this->order = $this->cart->submitOrder();
$this->assertInstanceOf(\Order::class, $this->order);
return $this->order;
}
public function recalculateCart(): \Decimal
{
// je to fuj, ale je to kvuli spravnym cenam... aby se zapocitala i cena DeliveryType
$this->cart->totalDiscountPrice = \DecimalConstants::zero();
$this->cart->totalDiscountPriceNoVat = \DecimalConstants::zero();
$this->cart->totalPricePay = \DecimalConstants::zero();
$this->cart->totalPricePayNoVat = \DecimalConstants::zero();
$this->cart->totalPriceNoVat = \DecimalConstants::zero();
$this->cart->totalPriceWithVat = \DecimalConstants::zero();
$this->cart->delivery_types = [];
// HACK: Donutit to znova spocitat
$prop = new \ReflectionProperty(\CartBase::class, 'initialized');
$prop->setAccessible(true);
$prop->setValue($this->cart, false);
$this->cart->only_virtual_products = null;
$this->cart->createFromDB();
return $this->cart->totalPricePay;
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Contracts\Service\Attribute\Required;
trait ContainerAwareTestTrait
{
/**
* @template T
*
* @param $service string|class-string<T>
*
* @return mixed|T
*/
public function get($service)
{
return ServiceContainer::getService($service);
}
public function set($service, $instance, $container = null)
{
$container = $container ?: ServiceContainer::getContainer();
// osklivost, ale jinak to proste nejde :/ ten container pro testy umoznuje pouze pristupovat k private
// servisam, ale setovani to uz nepodporuje a spadne to napr. na tom, ze se nemuze nasetovat private servica
$refObject = new \ReflectionObject($container);
$refMethod = $refObject->getMethod('getPublicContainer');
$refMethod->setAccessible(true);
$publicContainer = $refMethod->invoke($container);
$refObject = new \ReflectionObject($publicContainer);
// povolim to v syntheticIds - to umozni nasetovat tu servisu do public containeru
$refProperty = $refObject->getProperty('syntheticIds');
$refProperty->setAccessible(true);
$refProperty->setValue($publicContainer, array_merge($refProperty->getValue($publicContainer), [$service => $instance]));
// nasetuju tu servisu i do private containeru
$refProperty = $refObject->getProperty('privates');
$refProperty->setAccessible(true);
$refProperty->setValue($publicContainer, array_merge($refProperty->getValue($publicContainer), [$service => $instance]));
return $publicContainer->set($service, $instance);
}
/**
* Creates a Client.
*
* @param array $options An array of options to pass to the createKernel class
* @param array $server An array of server parameters
*
* @return KernelBrowser A Client instance
*/
protected function createClient(array $options = [], array $server = [])
{
$server = array_merge([
'HTTPS' => true,
'HTTP_HOST' => 'www.kupshop.local',
], $server);
/** @var KernelBrowser $client */
$client = $this->get('test.client');
$client->setServerParameters($server);
$client->disableReboot();
return $client;
}
public function tearDownContainer()
{
// shutdown kernel before being destroyed - to be memory freed correctly
ServiceContainer::getKernel()->shutdown();
// destroy kernel
ServiceContainer::destroy();
}
/**
* Instantiate a $class and autowire it with services from the Service Container.
*
* @template T
*
* @param class-string<T> $class
*
* @return T
*
* @throws \ReflectionException
*/
protected function autowire(string $class)
{
$rc = new \ReflectionClass($class);
$constructor = $rc->getConstructor();
// get constructor arguments from the container
$constructorArgs = [];
if ($constructor) {
foreach ($constructor->getParameters() as $name => $param) {
$classFqn = $param->getType()?->getName();
$constructorArgs[$name] = $this->get($classFqn);
}
}
$instance = $rc->newInstanceArgs($constructorArgs);
// setter autowiring - public and has the `@required` annotation
$setters = array_filter(
$rc->getMethods(\ReflectionMethod::IS_PUBLIC),
self::isRequired(...),
);
foreach ($setters as $setter) {
if ($setter->getNumberOfParameters() !== 1) {
continue;
}
$param = $setter->getParameters()[0];
$classFqn = $param->getType()?->getName();
$setter->invoke($instance, $this->get($classFqn));
}
// autowire public properties with the `@required` annotation or Required attribute
$requiredProperties = array_filter(
$rc->getProperties(\ReflectionProperty::IS_PUBLIC),
self::isRequired(...),
);
foreach ($requiredProperties as $property) {
$classFqn = $property->getType()?->getName();
$instance->{$property->getName()} = $this->get($classFqn);
}
return $instance;
}
protected function getRequiredProperties(object $class): array
{
$reflectionClass = new \ReflectionClass($class);
$requiredProperties = [];
// Check properties
foreach ($reflectionClass->getProperties() as $property) {
$attributes = $property->getAttributes(Required::class);
$requireDoc = str_contains($property->getDocComment() ?? '', '@required');
if (!empty($attributes) || !empty($requireDoc)) {
$requiredProperties[] = $property->getName();
}
}
// Check methods
foreach ($reflectionClass->getMethods() as $method) {
$attributes = $method->getAttributes(Required::class);
$requireDoc = str_contains($method->getDocComment() ?? '', '@required');
if (!empty($attributes) || !empty($requireDoc)) {
// Check if the method is a setter
if (preg_match('/^set(.+)$/', $method->getName(), $matches)) {
$propertyName = lcfirst($matches[1]);
$requiredProperties[] = $propertyName;
}
}
}
return $requiredProperties;
}
private static function isRequired(\ReflectionMethod|\ReflectionProperty $prop): bool
{
$requireAttr = !empty($prop->getAttributes(Required::class));
$requireDoc = str_contains($prop->getDocComment() ?? '', '@required');
return $requireDoc || $requireAttr;
}
}

View File

@@ -0,0 +1,157 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Upgrade;
class DatabaseUtils
{
private $databaseChecksum;
private $testDatabase = [
'engine' => 'phpunit_test',
'engine_units_float' => 'phpunit_test_units_float',
'shop' => 'phpunit_test_shop',
];
public function prepare()
{
$databaseName = $this->getDbName();
// Pass current database name to upgrade check_DBExists
define('TEST_DB_NAME', $databaseName);
// Load stored checksum
if (empty($this->databaseChecksum) && is_file('/tmp/kupshop_test_checksum')) {
$this->databaseChecksum = file_get_contents('/tmp/kupshop_test_checksum');
}
try {
$this->debugDatabaseChecksum();
$this->createSchema();
$this->debugDatabaseChecksum();
} catch (\Exception $e) {
echo "Error during database upgrade: {$e->getMessage()}\n{$e->getTraceAsString()}\n";
}
if (TEST_SUITE == 'engine') {
$this->checkDatabaseChecksum();
} else {
$this->databaseChecksum = $this->makeDatabaseChecksum();
}
// Clear memcached
getMemcached()->flush();
}
public function createSchema()
{
global $cfg;
/* @var \Doctrine\DBAL\Driver\PDOStatement $res */
$cfg['Connection']['database'] = $this->getDbName();
$coverage = false;
if (extension_loaded('xdebug')) {
if ($coverage = xdebug_code_coverage_started()) {
xdebug_stop_code_coverage(false);
}
}
// Run migrations
$upgrade = new \Upgrade(
\Upgrade::VERBOSE_NO,
TEST_SUITE === 'shop' ? \Upgrade::LOCAL_UPGRADES_YES : \Upgrade::LOCAL_UPGRADES_NO
);
$upgrade->run();
$upgrade->run();
if ($coverage) {
xdebug_start_code_coverage();
}
ServiceContainer::destroy();
}
private function debugDatabaseChecksum()
{
return;
try {
echo "Database tables checksum:\n";
$checksums = sqlFetchAll(sqlQuery(
sprintf(
'CHECKSUM TABLE %s',
join(',', sqlGetConnection()->getSchemaManager()->listTableNames())
)
));
array_map(function ($row) {
echo "{$row['Table']}: {$row['Checksum']}\n";
}, $checksums);
echo "\n{$this->makeDatabaseChecksum()}\n";
} catch (Exception $e) {
echo "exception\n";
}
}
private function makeDatabaseChecksum()
{
$tables = sqlGetConnection()->getSchemaManager()->listTableNames();
if (!count($tables)) {
return 'empty';
}
return sha1(implode(array_column(sqlFetchAll(sqlQuery(
sprintf(
'CHECKSUM TABLE %s',
join(',', $tables)
)
)), 'Checksum')));
}
private function checkDatabaseChecksum()
{
$checksum = $this->makeDatabaseChecksum();
if ($checksum != $this->databaseChecksum) {
echo "Database checksum does not match!\n
correct:".$this->databaseChecksum." current:{$checksum}\n";
$this->debugDatabaseChecksum();
echo "Deleting database {$this->getDbName()}...\n";
sqlQuery('DROP DATABASE IF EXISTS '.$this->getDbName());
sqlQuery('CREATE DATABASE '.$this->getDbName());
sqlQuery('USE '.$this->getDbName());
echo "Running upgrade ...\n";
$this->createSchema();
$this->databaseChecksum = $this->makeDatabaseChecksum();
$this->debugDatabaseChecksum();
echo 'Database checksum does not match!
Correct checksum: '.$this->databaseChecksum."\n\n";
echo "Storing checksum {$this->databaseChecksum} to /tmp/kupshop_test_checksum";
$f = fopen('/tmp/kupshop_test_checksum', 'w');
fwrite($f, $this->databaseChecksum);
fclose($f);
}
}
public function getDatabaseChecksum(): string
{
return $this->databaseChecksum;
}
public function getDbName()
{
return $this->testDatabase[TEST_SUITE].getenv('TEST_TOKEN');
}
public function checkDatabaseNotChanged()
{
return $this->databaseChecksum === $this->makeDatabaseChecksum();
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use Monolog\Handler\TestHandler;
use Symfony\Bridge\Monolog\Logger;
trait LoggerTestTrait
{
public function getLogger()
{
/** @var Logger $logger */
$logger = $this->get('logger');
$testHandler = new TestHandler('DEBUG');
$logger->pushHandler($testHandler);
}
public function getTestHandler()
{
$logger = $this->get('logger');
$testHandler = null;
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof TestHandler) {
$testHandler = $handler;
}
}
return $testHandler;
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace KupShop\DevelopmentBundle\Util\Tests;
trait OrderFromPStateToggleTrait
{
public function data_purchaseStateProvider(): \Generator
{
yield 'Test with module create_orders_from_purchase_state' => [function () {
$this->addModule(\Modules::ORDERS, \Modules::SUB_CREATE_ORDER_FROM_PURCHASE_STATE);
}];
yield 'Test without module create_orders_from_purchase_state' => [function () {
$this->removeModule(\Modules::ORDERS, \Modules::SUB_CREATE_ORDER_FROM_PURCHASE_STATE);
}];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
trait RequestCookiesTrait
{
/** Get request stack with cookies for functional testing */
protected function getRequestStackWithCookies($cookieName, $cookieValue)
{
$requestStack = $this->get(\Symfony\Component\HttpFoundation\RequestStack::class);
$request = new \Symfony\Component\HttpFoundation\Request();
$request->cookies->set($cookieName, $cookieValue);
$request->setSession(
$this->get('session.factory')->createSession()
);
$requestStack->push($request);
return $requestStack;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
trait RequestSessionTrait
{
/** Prepares request stack for functional testing */
protected function prepareRequestStackSession(): void
{
$requestStack = $this->get(\Symfony\Component\HttpFoundation\RequestStack::class);
$request = new \Symfony\Component\HttpFoundation\Request();
$request->setSession(
$this->get(SessionInterface::class)
);
$requestStack->push($request);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace KupShop\DevelopmentBundle\Util\Tests;
use KupShop\KupShopBundle\KupShopAppKernel;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Contracts\Service\ResetInterface;
class TestAppKernel extends KupShopAppKernel
{
protected static $connection;
public function registerContainerConfiguration(LoaderInterface $loader)
{
global $cfg;
$loader->load(realpath($cfg['Path']['shared_version']).'/bundles/KupShop/KupShopBundle/Resources/config/config_'.$this->getEnvironment().'.yml');
}
protected function initializeContainer()
{
parent::initializeContainer();
if (!self::$connection) {
self::$connection = sqlGetConnection();
} else {
$this->getContainer()->set('doctrine.dbal.default_connection', self::$connection);
}
}
public function shutdown()
{
// call reset before shutdown to be memory freed correctly
if ($this->getContainer() instanceof ResetInterface) {
$this->getContainer()->reset();
}
parent::shutdown();
}
}