diff --git a/composer.json b/composer.json index 7b70cee..b73adf4 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "phpstan/phpstan-phpunit": "^0.12", "nette/neon": "^3", "brick/date-time": "^0.3", - "zrnik/phpunit-exceptions": "^0.0.3" + "zrnik/phpunit-exceptions": "^0.0.4" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 3ce43c7..f088fc2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8d7506a9cc57acd7cc11f02ddd2da64a", + "content-hash": "3850d267f6fb010b7666d45e86fae63c", "packages": [ { "name": "jetbrains/phpstorm-attributes", @@ -942,23 +942,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.7", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.12.0", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -1007,7 +1007,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.8" }, "funding": [ { @@ -1015,7 +1015,7 @@ "type": "github" } ], - "time": "2021-09-17T05:39:03+00:00" + "time": "2021-10-30T08:01:38+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2588,16 +2588,16 @@ }, { "name": "zrnik/phpunit-exceptions", - "version": "v0.0.3", + "version": "v0.0.4", "source": { "type": "git", "url": "https://github.com/Zrnik/PHPUnit-Exceptions.git", - "reference": "9549d2b473c75ce345b581bc0aff37b34d22f08f" + "reference": "068853d903896248ad0cb981b83edc4cec32a763" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Zrnik/PHPUnit-Exceptions/zipball/9549d2b473c75ce345b581bc0aff37b34d22f08f", - "reference": "9549d2b473c75ce345b581bc0aff37b34d22f08f", + "url": "https://api.github.com/repos/Zrnik/PHPUnit-Exceptions/zipball/068853d903896248ad0cb981b83edc4cec32a763", + "reference": "068853d903896248ad0cb981b83edc4cec32a763", "shasum": "" }, "require": { @@ -2627,9 +2627,9 @@ "description": "Trait for easier exception testing in PHPUnit.", "support": { "issues": "https://github.com/Zrnik/PHPUnit-Exceptions/issues", - "source": "https://github.com/Zrnik/PHPUnit-Exceptions/tree/v0.0.3" + "source": "https://github.com/Zrnik/PHPUnit-Exceptions/tree/v0.0.4" }, - "time": "2021-10-13T10:42:23+00:00" + "time": "2021-11-03T08:21:13+00:00" } ], "aliases": [], diff --git a/src/Exceptions/TableCreationFailedException.php b/src/Exceptions/TableCreationFailedException.php new file mode 100644 index 0000000..70476af --- /dev/null +++ b/src/Exceptions/TableCreationFailedException.php @@ -0,0 +1,28 @@ +getMessage(); + + $message = $columnName === null + ? sprintf( + "Unable to create table '%s'\nreason: %s\nquery: %s", + $tableName, $reason, $query + ) + : sprintf( + "Unable to create column '%s::%s'\nreason: %s\nquery: %s", + $tableName, $columnName, $reason, $query + ); + + parent::__construct($message, (int) $previous->getCode(), $previous); + } +} diff --git a/src/Repository/BaseEntity.php b/src/Repository/BaseEntity.php index 2fe57ed..5dbfa49 100644 --- a/src/Repository/BaseEntity.php +++ b/src/Repository/BaseEntity.php @@ -74,9 +74,9 @@ public static function getReflectionClass(BaseEntity|string $obj): ReflectionCla if (!array_key_exists($objKey, self::$reflectionClasses)) { try { /** + * @throws ReflectionException * @var ReflectionClass $reflectionInstance * @noinspection PhpRedundantVariableDocTypeInspection - * @throws ReflectionException */ $reflectionInstance = new ReflectionClass($objKey); self::$reflectionClasses[$objKey] = $reflectionInstance; @@ -159,12 +159,12 @@ public function toArray(): array $foreignAttributeType = Reflection::propertyGetAttribute($reflectionProperty, ForeignKey::class); if ($foreignAttributeType !== null) { - // That means '$propertyValue' is an object extending 'BaseEntity' - if ($propertyValue instanceof self) { + // That means '$propertyValue' is an object extending 'BaseEntity' or null + if ($propertyValue instanceof self || $propertyValue === null) { /** @var BaseEntity $foreignEntityClassName */ $foreignEntityClassName = Reflection::attributeGetArgument($foreignAttributeType); $primaryKeyName = $foreignEntityClassName::getPrimaryKeyName(); - $propertyValue = $propertyValue->toArray()[$primaryKeyName]; + $propertyValue = $propertyValue?->toArray()[$primaryKeyName]; // Also, we need to update the `$rawData` property! @@ -222,9 +222,9 @@ public static function create(array $initValues = []): static $primaryKeyPropertyName = static::getPrimaryKeyPropertyName(); // add 'getDefaultValues' to the created entity - foreach($entity->getDefaults() as $key => $value) { + foreach ($entity->getDefaults() as $key => $value) { - if($primaryKeyPropertyName === $key) { + if ($primaryKeyPropertyName === $key) { throw new PrimaryKeyProvidedInDefaults(); } @@ -232,9 +232,9 @@ public static function create(array $initValues = []): static } // Overwrite "initialize" values - foreach($initValues as $key => $value) { + foreach ($initValues as $key => $value) { - if($primaryKeyPropertyName === $key) { + if ($primaryKeyPropertyName === $key) { throw new PrimaryKeyProvidedInDefaults(); } @@ -851,24 +851,30 @@ public static function hydrateUpdater(Updater $updater, array $baseEntitiesWeHav ); } + $addToForeignKeyReference = true; + if ( !$isFetchArray && in_array($referencedEntityName, $baseEntitiesWeHaveAlreadySeen, true) ) { - //dump($referencedEntityName, static::class); - - if($referencedEntityName === static::class) { - continue; + if ($referencedEntityName !== static::class) { + throw new CircularReferenceDetectedException( + static::class, $property->getName() + ); } - throw new CircularReferenceDetectedException( - static::class, $property->getName() - ); + + $addToForeignKeyReference = false; + } $referencedForeignKeys[$referencedEntityName] = $property->getName(); - $hydrateAfter[] = $referencedEntityName; + + + if ($addToForeignKeyReference) { + $hydrateAfter[] = $referencedEntityName; + } } //endregion @@ -1030,7 +1036,7 @@ public function fixSubEntityForeignKeys(): void /** * Sub entities (fetch array) - * @return BaseEntity[] + * @return array */ public function subEntities(): array { @@ -1048,7 +1054,7 @@ public function subEntities(): array /** * Superior entities (foreign key) - * @return BaseEntity[] + * @return array */ public function supEntities(): array { @@ -1084,16 +1090,16 @@ public function getOriginalData(): ?array */ final public function setOriginalData(?array $newOriginalData): void { - if(!Misc::isPhpUnitTest()) { + if (!Misc::isPhpUnitTest()) { throw new Exception("Method 'setOriginalData' is internal, and exists only for testing purpose!"); } $this->originalData = $newOriginalData; } - public function indicateSave(): void + public function indicateSave(bool $saved = true): void { - $this->originalData = $this->getRawData(); + $this->originalData = $saved ? $this->getRawData() : []; } } diff --git a/src/Repository/BaseRepository.php b/src/Repository/BaseRepository.php index 80cd73f..d8e3ef9 100644 --- a/src/Repository/BaseRepository.php +++ b/src/Repository/BaseRepository.php @@ -111,7 +111,9 @@ public function getResultsByKeys( ): array { $dispenser = new Dispenser(($this->getPdo())); - $entities = $dispenser->getResultsByKeys($baseEntityClassString, $propertyName, $values); + $entities = $dispenser->getResultsByKeys( + $baseEntityClassString, $propertyName, $values + ); $this->executedQueries += $dispenser->getExecutedQueryCount(); return $entities; } diff --git a/src/Repository/Fetch/EntityStorage.php b/src/Repository/Fetch/EntityStorage.php index a5aef10..e3e4e2c 100644 --- a/src/Repository/Fetch/EntityStorage.php +++ b/src/Repository/Fetch/EntityStorage.php @@ -75,7 +75,12 @@ public function getEntities(): array string $className, mixed $primaryKeyValue ): ?BaseEntity { - $index = $this->entitiesIndexPointer[$this->cnpk($className, $primaryKeyValue)]; + $index = + $this->entitiesIndexPointer + [$this->cnpk($className, $primaryKeyValue)] + ?? 'null' + ; + return $this->entities[$index] ?? null; } diff --git a/src/Repository/Saver/Saver.php b/src/Repository/Saver/Saver.php index 7356bfc..465ed2e 100644 --- a/src/Repository/Saver/Saver.php +++ b/src/Repository/Saver/Saver.php @@ -5,6 +5,7 @@ use JetBrains\PhpStorm\Pure; use PDO; use PDOException; +use Tests\Mock\Bugs\TreeSave\RepeatingNode; use Zrnik\MkSQL\Exceptions\MkSQLException; use Zrnik\MkSQL\Exceptions\SaveFailedException; use Zrnik\MkSQL\Repository\BaseEntity; @@ -22,7 +23,7 @@ public function __construct(private PDO $pdo) } /** - * @param BaseEntity[] $entities + * @param array $entities */ public function saveEntities(array $entities): void { @@ -33,6 +34,10 @@ public function saveEntities(array $entities): void foreach ($entityList as $classEntityList) { foreach ($classEntityList as $entity) { + if($entity === null) { + continue; + } + if ($entity->getPrimaryKeyValue() === null) { $insertEntities[] = $entity; } else { @@ -49,6 +54,25 @@ public function saveEntities(array $entities): void foreach ($chunkByTables as $insertChunk) { foreach (array_chunk($insertChunk, self::DEFAULT_INSERT_CHUNK_SIZE) as $chunk) { $this->insert($chunk); + + /** @var BaseEntity $entity */ + foreach ($chunk as $entity) { + + $hasSelfPointingForeignKeyThatIsNotNull = false; + foreach(EntityReflection::getForeignKeys($entity) as $foreignKeyData) { + if($foreignKeyData->getTargetClassName() === $entity::class) { + $propertyName = $foreignKeyData->getPropertyName(); + if($entity->$propertyName !== null) { + $hasSelfPointingForeignKeyThatIsNotNull = true; + } + } + } + + if($hasSelfPointingForeignKeyThatIsNotNull) { + $entity->indicateSave(false); + $updateEntities[] = $entity; + } + } } } @@ -60,8 +84,8 @@ public function saveEntities(array $entities): void } /** - * @param BaseEntity[] $entities - * @return array, array> + * @param array $entities + * @return array, array> */ private function listAllEntities(array $entities): array { @@ -75,25 +99,32 @@ private function listAllEntities(array $entities): array } /** - * @param BaseEntity $entity - * @param array, array> $entityList + * @param ?BaseEntity $entity + * @param array, array> $entityList */ - private function fillEntityListRecursively(BaseEntity $entity, array &$entityList): void + private function fillEntityListRecursively(?BaseEntity $entity, array &$entityList): void { - if (!array_key_exists($entity::class, $entityList)) { - $entityList[$entity::class] = []; + $entityClass = $entity === null ? 'null' : $entity::class; + + if (!array_key_exists($entityClass, $entityList)) { + $entityList[$entityClass] = []; } - if (!array_key_exists($entity->hash(), $entityList[$entity::class])) { + $hash = $entity === null ? 'null' : $entity->hash(); - $entityList[$entity::class][$entity->hash()] = $entity; - foreach ($entity->subEntities() as $subEntity) { - $this->fillEntityListRecursively($subEntity, $entityList); - } + if (!array_key_exists($hash, $entityList[$entityClass])) { + + $entityList[$entityClass][$hash] = $entity; + + if($entity !== null) { + foreach ($entity->subEntities() as $subEntity) { + $this->fillEntityListRecursively($subEntity, $entityList); + } - foreach ($entity->supEntities() as $subEntity) { - $this->fillEntityListRecursively($subEntity, $entityList); + foreach ($entity->supEntities() as $subEntity) { + $this->fillEntityListRecursively($subEntity, $entityList); + } } } @@ -106,9 +137,6 @@ private function update(BaseEntity $entity): void // Original data are only retrieved when fetching, // if null, it is created entity (so probably already // handled by "insert") and not saved - // - // But, if I am testing in PHPUnit, and I want to destroy - // the original data, I don't want to die here... if ($originalData === null) { return; } @@ -405,6 +433,10 @@ private static function addEntityToOrder(array $order, string $entityClassName, if (array_key_exists($entityClassName, $dependencyTable)) { foreach ($dependencyTable[$entityClassName] as $requiredSubTable) { + if($entityClassName === $requiredSubTable) { + continue; + } + $order = self::addEntityToOrder($order, $requiredSubTable, $dependencyTable, $stack + 1); } } diff --git a/src/Updater.php b/src/Updater.php index 38874ed..0940a3c 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -13,6 +13,7 @@ use Zrnik\MkSQL\Enum\DriverType; use Zrnik\MkSQL\Exceptions\InvalidArgumentException; use Zrnik\MkSQL\Exceptions\InvalidDriverException; +use Zrnik\MkSQL\Exceptions\TableCreationFailedException; use Zrnik\MkSQL\Exceptions\TableDefinitionExists; use Zrnik\MkSQL\Exceptions\UnexpectedCall; use Zrnik\MkSQL\Queries\Makers\IQueryMaker; @@ -150,12 +151,11 @@ public function tableRemove(string $tableName): bool //endregion /** - * @return bool * @throws InvalidArgumentException * @throws InvalidDriverException * @throws UnexpectedCall */ - public function install(): bool + public function install(): void { if ($this->installable !== null) { throw new UnexpectedCall( @@ -167,8 +167,6 @@ public function install(): bool ); } - $Success = true; - $timer_total = microtime(true); //region DriverCheck @@ -210,6 +208,8 @@ public function install(): bool $sortedTables = TableOrder::doOrder($this->tables); foreach ($sortedTables as $table) { + + if ($this->isAlreadyProcessed($table)) { continue; } @@ -249,7 +249,6 @@ public function install(): bool //region Query[] Executing - $stopped = false; if (count($QueryCommands) > 0) { /** @@ -260,20 +259,23 @@ public function install(): bool $queryExecuteSpeed = microtime(true); - if ($stopped) { - $QueryCommand->executed = false; - } else { + $QueryCommand->executed = true; try { $QueryCommand->execute($this->pdo, $this); } catch (PDOException $pdoEx) { - $stopped = true; - $Success = false; $QueryCommand->setError($pdoEx); $this->lastError = $pdoEx; + + throw new TableCreationFailedException( + $QueryCommand->getTable()->getName(), + $QueryCommand->getColumn()?->getName(), + $QueryCommand->getQuery(), + $pdoEx + ); } - } + $queryExecuteSpeed = @@ -293,8 +295,6 @@ public function install(): bool } Measure::reportTotalSpeed(microtime(true) - $timer_total); - - return $Success; } public function clear(): void diff --git a/src/Utilities/TableOrder.php b/src/Utilities/TableOrder.php index b2ca8b4..8d088a7 100644 --- a/src/Utilities/TableOrder.php +++ b/src/Utilities/TableOrder.php @@ -93,8 +93,14 @@ private static function addTableToOrder(array $order, string $tableName, array $ throw new MkSQLException('Stack Overflow'); } + if (array_key_exists($tableName, $dependencyTable)) { foreach ($dependencyTable[$tableName] as $requiredSubTable) { + + if($tableName === $requiredSubTable) { + continue; + } + $order = self::addTableToOrder($order, $requiredSubTable, $dependencyTable, $stack + 1); } } @@ -103,6 +109,7 @@ private static function addTableToOrder(array $order, string $tableName, array $ $order[] = $tableName; } + return $order; } diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index ba36757..8eeb000 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -19,6 +19,10 @@ use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\Auction; use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\AuctionItem; use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\AutoHydrateAndCircularReference\ReferencingEntityOne; +use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\NullableForeignKeyNoError\NullableForeignKeyNoErrorRepository; +use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\NullableForeignKeyNoError\SelfRepeating; +use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\NullableForeignKeyNoError\SubClass; +use Tests\Mock\BaseRepositoryAndBaseEntity\Entities\NullableForeignKeyNoError\SuperClass; use Tests\Mock\BaseRepositoryAndBaseEntity\HydrateTestEntities\FetchedEntity; use Tests\Mock\BaseRepositoryAndBaseEntity\HydrateTestEntities\FetchedEntityFromSubEntity; use Tests\Mock\BaseRepositoryAndBaseEntity\HydrateTestEntities\MainEntity; @@ -29,6 +33,8 @@ use Tests\Mock\Bugs\DoubleRetrieve\DoubleRetrieveRepository; use Tests\Mock\Bugs\DoubleRetrieve\Person; use Tests\Mock\Bugs\DoubleRetrieve\Reward; +use Tests\Mock\Bugs\TreeSave\RepeatingNode; +use Tests\Mock\Bugs\TreeSave\TreeSaveRepository; use Tracy\Debugger; use Zrnik\MkSQL\Column; use Zrnik\MkSQL\Enum\DriverType; @@ -38,17 +44,15 @@ use Zrnik\MkSQL\Exceptions\InvalidDriverException; use Zrnik\MkSQL\Exceptions\MkSQLException; use Zrnik\MkSQL\Exceptions\PrimaryKeyAutomaticException; +use Zrnik\MkSQL\Exceptions\TableCreationFailedException; use Zrnik\MkSQL\Exceptions\TableDefinitionExists; use Zrnik\MkSQL\Exceptions\UnexpectedCall; -use Zrnik\MkSQL\Repository\BaseEntity; -use Zrnik\MkSQL\Repository\BaseRepository; use Zrnik\MkSQL\Tracy\Measure; use Zrnik\MkSQL\Tracy\Panel; use Zrnik\MkSQL\Updater; use Zrnik\MkSQL\Utilities\Installable; use Zrnik\PHPUnit\Exceptions; use function count; -use function in_array; class IntegrationTest extends TestCase { @@ -115,7 +119,13 @@ private function processTest(PDO $pdo, string $version = ''): void //echo "Processing Test with: " . . PHP_EOL; $this->dropAll($pdo); - static::assertTrue($this->installDefault($Updater)); + + $this->assertNoExceptionThrown( + function () use ($Updater) { + $this->installDefault($Updater); + } + ); + $this->dot(); // ##################################### @@ -179,8 +189,9 @@ private function processTest(PDO $pdo, string $version = ''): void $this->subTestSingleInstallForMultipleDefinedTables($pdo); + $this->nullableForeignKeyNoError($pdo); - + $this->treeSavingFailed($pdo); echo ']' . PHP_EOL . 'Complete!'; @@ -252,10 +263,9 @@ private function dropAll(PDO $pdo): void /** * @param Updater $Updater - * @return bool * @throws MkSQLException */ - private function installDefault(Updater $Updater): bool + private function installDefault(Updater $Updater): void { //Table Accounts only with Login: $Accounts = $Updater->tableCreate('accounts')->setPrimaryKeyName('account_id'); @@ -289,7 +299,7 @@ private function installDefault(Updater $Updater): bool $Sessions->columnAdd(clone $TokenColumn); $LastAction->columnAdd(clone $TokenColumn); - return $Updater->install(); + $Updater->install(); } /** @@ -302,11 +312,19 @@ private function subTestPrimaryKeyNameChange(Updater $Updater): void static::assertNotNull($tableAccounts); $tableAccounts->setPrimaryKeyName('new_id'); - static::assertTrue($Updater->install()); + + $this->assertNoExceptionThrown(function () use ($Updater) { + $Updater->install(); + }); + $this->dot(); $tableAccounts->setPrimaryKeyName('id'); - static::assertTrue($Updater->install()); + + $this->assertNoExceptionThrown(function () use ($Updater) { + $Updater->install(); + }); + $this->dot(); } @@ -323,15 +341,27 @@ private function subTestTypeChange(Updater $Updater): void static::assertNotNull($columnActionTime); $columnActionTime->setType('varchar(10)'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setType('char(15)'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setType('int'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -351,11 +381,19 @@ private function subTestNotNull(Updater $Updater): void static::assertNotNull($columnLogin); $columnLogin->setNotNull(false); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnLogin->setNotNull(); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -372,11 +410,19 @@ private function subTestDefaultValue(Updater $Updater): void static::assertNotNull($columnActionTime); $columnActionTime->setDefault(time()); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setDefault(strtotime('-3 days')); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -393,15 +439,27 @@ private function subTestComment(Updater $Updater): void static::assertNotNull($columnActionTime); $columnActionTime->setComment('A tested column'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setComment(); // null - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setComment('Integration Tested Column'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -418,11 +476,19 @@ private function subTestForeignKeys(Updater $Updater): void static::assertNotNull($tableSessionsColumnToken); $tableSessionsColumnToken->addForeignKey('accounts.login'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $tableSessionsColumnToken->dropForeignKey('accounts.login'); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -440,13 +506,25 @@ private function subTestUniqueIndexes(Updater $Updater): void static::assertNotNull($columnActionTime); $columnActionTime->setUnique(); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setUnique(false); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); $columnActionTime->setUnique(); - static::assertTrue($Updater->install()); + $this->assertNoExceptionThrown( + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); } @@ -455,15 +533,21 @@ private function subTestErrors(Updater $Updater): void { $tables = $Updater->tableList(); - // Create error item (column name cannot be 'date') + // Create error item (column name cannot be 'unique' because its a keyword) $tables[array_keys($tables)[0]]->columnCreate( 'unique', 'int(11)' ); - $Updater->install(); + $this->assertExceptionThrown( + TableCreationFailedException::class, + function () use ($Updater) { + $Updater->install(); + } + ); $this->dot(); - static::assertCount(1, Measure::getErrors()); + // We do not look for errors, we are getting exception! + // static::assertCount(1, Measure::getErrors()); // Remove the error from cache, so we can continue... foreach (Measure::getQueryModification() as $qm) { @@ -1048,4 +1132,57 @@ private function subTestDoNotUpdateUpToDateEntities(PDO $pdo): void self::assertEquals($requiredQueryCount, $auctionRepository->getExecutedQueryCount()); } + + private function nullableForeignKeyNoError(PDO $pdo): void + { + $repository = new NullableForeignKeyNoErrorRepository($pdo); + + $this->assertNoExceptionThrown(static function () use ($repository) { + $selfRepeating1 = SelfRepeating::create(); + $selfRepeating2 = SelfRepeating::create(); + $selfRepeating2->referrer = $selfRepeating1; + $repository->save([$selfRepeating1, $selfRepeating2]); + }); + + $this->assertNoExceptionThrown(static function () use ($repository) { + $superClass1 = SuperClass::create(); + $superClass2 = SuperClass::create(); + $subClass1 = SubClass::create(); + $superClass1->subClass = $subClass1; + $repository->save([$superClass1, $superClass2]); + }); + + } + + /** + * @throws Exception + */ + private function treeSavingFailed(PDO $pdo): void + { + $repository = new TreeSaveRepository($pdo); + $repository->dropAll(); + + $headNode = RepeatingNode::create(['name' => 'Head Node']); + $subNode1 = RepeatingNode::create(['name' => 'sub level 1', 'parent' => $headNode]); + $subNode2 = RepeatingNode::create(['name' => 'sub level 3', 'parent' => $subNode1]); + $subNode3 = RepeatingNode::create(['name' => 'sub level 4', 'parent' => $subNode2]); + + $nodeList = [$headNode, $subNode1, $subNode2, $subNode3]; + + $repository->save($nodeList); + + $statement = $pdo->query('SELECT * FROM ' . RepeatingNode::getTableName()); + + if ($statement === false) { + throw new Exception('No result form database!'); + } + + /** @var mixed[] $saved */ + $saved = $statement->fetchAll(PDO::FETCH_ASSOC); + + static::assertNull($saved[0]['parent']); + static::assertSame($headNode->id, (int)$saved[1]['parent']); + static::assertSame($subNode1->id, (int)$saved[2]['parent']); + static::assertSame($subNode2->id, (int)$saved[3]['parent']); + } } diff --git a/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/NullableForeignKeyNoErrorRepository.php b/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/NullableForeignKeyNoErrorRepository.php new file mode 100644 index 0000000..2e0d5fa --- /dev/null +++ b/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/NullableForeignKeyNoErrorRepository.php @@ -0,0 +1,16 @@ +use(SelfRepeating::class); + $updater->use(SuperClass::class); + $updater->use(SubClass::class); + } +} diff --git a/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/SelfRepeating.php b/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/SelfRepeating.php new file mode 100644 index 0000000..bc4e2c1 --- /dev/null +++ b/tests/Mock/BaseRepositoryAndBaseEntity/Entities/NullableForeignKeyNoError/SelfRepeating.php @@ -0,0 +1,18 @@ +use(RepeatingNode::class); + } + + public function dropAll(): void + { + + while (count($this->getAll(RepeatingNode::class)) > 0) { + + $dropIds = []; + + $distinctPrimary = $this->distinctValues(RepeatingNode::class, 'id'); + $distinctParent = $this->distinctValues(RepeatingNode::class, 'parent'); + + foreach ($distinctPrimary as $key) { + if (!in_array($key, $distinctParent)) { + $dropIds[] = $key; + } + } + + if (count($dropIds) > 0) { + $statement = $this->pdo->query( + sprintf( + 'DELETE FROM ' . RepeatingNode::getTableName() . + ' WHERE id IN (%s)', + implode(', ', $dropIds) + ) + + ); + + if ($statement !== false) { + $statement->execute(); + } + } + } + + // + + + } +} diff --git a/tests/createTest.md b/tests/createTest.md index e8185d3..de3845e 100644 --- a/tests/createTest.md +++ b/tests/createTest.md @@ -4,4 +4,4 @@ Pokud Ukazuju s FetchArray na nejakou entitu, a ona ukazuje zpátky s více než vyjimku! Otestovat ze pokud ma entita pres FetchArray nejake childy, ale child entita má foreignKey ukazujici na neco jinneho nez -na me/nebo unset tak tu forignKey propertky zmenime na sebe! \ No newline at end of file +na me/nebo unset tak tu forignKey propertky zmenime na sebe!