diff --git a/tests/Unit/DBTest.php b/tests/Unit/DBTest.php index 551100fd0c..8a968ee422 100644 --- a/tests/Unit/DBTest.php +++ b/tests/Unit/DBTest.php @@ -4,11 +4,12 @@ use PHPUnit\Framework\TestCase; use Tests\Unit\Mock\DBTestable; -use Tests\Unit\Mock\ObjectMock\DecryptableAndChangableMock; -use Tests\Unit\Mock\ObjectMock\DecryptableMock; +use Tests\Unit\Mock\ObjectMock\ObjectWithDecryptAndChange; +use Tests\Unit\Mock\ObjectMock\ObjectWithDecrypt; use Tests\Unit\Mock\ObjectMock\DecryptableWithChangedMock; use Tests\Unit\Mock\ObjectMock\ChangableMock; use Tests\Unit\Mock\ObjectMock\ObjectMock; +use Tests\Unit\Mock\ObjectMock\ObjectWithEncrypt; use Tests\Unit\Mock\ObjectMock\ObjectWithIdField; use Tests\Unit\Mock\ObjectMock\ObjectWithPreInsert; use Tests\Unit\Mock\ObjectMock\ObjectWithPreSave; @@ -124,41 +125,43 @@ public function test_query_with_bad_parameter(): void public function test_fetch_all_class_decrypt(): void { - $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, DecryptableMock::class); + $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, ObjectWithDecrypt::class); - $this->assertInstanceOf(DecryptableMock::class, $results[0]); + $this->assertInstanceOf(ObjectWithDecrypt::class, $results[0]); $this->assertSame('1', $results[0]->var); - $this->assertTrue($results[0]->isDecrypted()); + $this->assertTrue($results[0]->isMethodCalled('decrypt')); } public function test_fetch_all_class_decrypt_changed(): void { - $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, DecryptableAndChangableMock::class); + $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, ObjectWithDecryptAndChange::class); - $this->assertInstanceOf(DecryptableAndChangableMock::class, $results[0]); + $this->assertInstanceOf(ObjectWithDecryptAndChange::class, $results[0]); + $this->assertTrue($results[0]->isMethodCalled('setChanged')); $this->assertFalse($results[0]->isChanged()); } public function test_fetch_class_decrypt(): void { - $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, DecryptableMock::class); + $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, ObjectWithDecrypt::class); - $this->assertInstanceOf(DecryptableMock::class, $results); + $this->assertInstanceOf(ObjectWithDecrypt::class, $results); $this->assertSame('1', $results->publicVar); - $this->assertTrue($results->isDecrypted()); + $this->assertTrue($results->isMethodCalled('decrypt')); } public function test_fetch_class_decrypt_changed(): void { - $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, DecryptableAndChangableMock::class); + $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, ObjectWithDecryptAndChange::class); - $this->assertInstanceOf(DecryptableAndChangableMock::class, $results); + $this->assertInstanceOf(ObjectWithDecryptAndChange::class, $results); + $this->assertTrue($results->isMethodCalled('setChanged')); $this->assertFalse($results->isChanged()); } public function test_fetch_class_no_decrypt_do_not_set_changed(): void { - $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, ChangableMock::class); + $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ROW, \PDO::FETCH_CLASS, ChangableMock::class); $this->assertInstanceOf(ChangableMock::class, $results); $this->assertNull($results->isChanged()); @@ -166,7 +169,7 @@ public function test_fetch_class_no_decrypt_do_not_set_changed(): void public function test_fetch_all_class_no_decrypt_do_not_set_changed(): void { - $results = \DB::Prepare('SELECT 1 AS var', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, ChangableMock::class); + $results = \DB::Prepare('SELECT 1 AS publicVar', [], \DB::FETCH_TYPE_ALL, \PDO::FETCH_CLASS, ChangableMock::class); $this->assertInstanceOf(ChangableMock::class, $results[0]); $this->assertNull($results[0]->isChanged()); @@ -210,7 +213,7 @@ public function test_optimize_database(): void public function test_save_data(): void { - $object = new ObjectMock(); + $object = $this->thereIsAnObject(); $this->thereIsATableForObject($object); \DB::save($object); @@ -219,29 +222,50 @@ public function test_save_data(): void } - public function test_call_presave_hook(): void + public static function hookProvider(): iterable { - $object = new ObjectWithPreSave(); + yield from self::hookCalledWithDirectFlagProvider(); + yield from self::hookSkippedWithDirectFlagProvider(); + } + + /** + * @dataProvider hookProvider + */ + public function test_save_call_hooks(string $hook): void + { + $object = $this->thereIsAnObject()->withMethod($hook); $this->thereIsATableForObject($object); \DB::save($object); - $this->assertTrue($object->isPreSaveCalled()); + $this->assertTrue($object->isMethodCalled($hook)); + } - public function test_skip_presave_hook_with_direct_flag(): void + public static function hookSkippedWithDirectFlagProvider(): iterable + { + yield ['preSave']; + yield ['preInsert']; + yield ['postInsert']; + yield ['postSave']; + } + + /** + * @dataProvider hookSkippedWithDirectFlagProvider + */ + public function test_save_skip_hook_with_direct_flag(string $hook): void { - $object = new ObjectWithPreSave(); + $object = $this->thereIsAnObject()->withMethod($hook); $this->thereIsATableForObject($object); \DB::save($object, true); - $this->assertFalse($object->isPreSaveCalled()); + $this->assertFalse($object->isMethodCalled($hook)); } public function test_save_set_private_id(): void { - $object = new ObjectWithIdField(); + $object = $this->thereIsAnObject()->withField('id'); $this->thereIsATableForObject($object); \DB::save($object); @@ -249,26 +273,45 @@ public function test_save_set_private_id(): void $this->assertTrue($object->isIdSet()); } - public function test_call_preinsert_hook(): void + /** + * @dataProvider hookProvider + */ + public function test_save_skip_call_hook_on_object_without_method(string $hook): void { - $object = new ObjectWithPreInsert(); + $object = $this->thereIsAnObject()->withoutMethod($hook); $this->thereIsATableForObject($object); \DB::save($object); - $this->assertTrue($object->isPreInsertCalled()); + $this->assertFalse($object->isMethodCalled($hook)); } - public function test_skip_preinsert_hook_with_direct_flag(): void + public static function hookCalledWithDirectFlagProvider(): iterable { - $object = new ObjectWithPreInsert(); + yield ['encrypt']; + yield ['decrypt']; + } + + /** + * @dataProvider hookCalledWithDirectFlagProvider + */ + public function test_save_call_encrypt_hook_with_direct_flag(string $hook): void + { + $object = $this->thereIsAnObject()->withMethod($hook); $this->thereIsATableForObject($object); \DB::save($object, true); - $this->assertFalse($object->isPreInsertCalled()); + $this->assertTrue($object->isMethodCalled($hook)); } + /** + * @return ObjectMock + */ + private function thereIsAnObject(): ObjectMock + { + return new ObjectMock(); + } /** * @before @@ -348,11 +391,11 @@ private function thereIsABadDatabaseConfiguration(): void $reflection->setStaticPropertyValue('connection', null); } - private function expectPhpError(string $message): void + private function expectPhpError(string $message, int $errorType = E_USER_ERROR): void { $this->rollback(); - $this->originalErrorHandler = set_error_handler(function (int $errno, string $errstr) use ($message): void { - $this->assertSame(E_USER_ERROR, $errno); + $this->originalErrorHandler = set_error_handler(function (int $errno, string $errstr) use ($message, $errorType): void { + $this->assertSame($errorType, $errno); $this->assertEquals($message, $errstr); }); } diff --git a/tests/Unit/Mock/DBTestable.php b/tests/Unit/Mock/DBTestable.php index 89fdb480c8..3b085b1293 100644 --- a/tests/Unit/Mock/DBTestable.php +++ b/tests/Unit/Mock/DBTestable.php @@ -16,7 +16,7 @@ public static function &Prepare($_query, $_params, $_fetchType = self::FETCH_TYP return $res; } - public static function getOptimizationQueries() + public static function getOptimizationQueries(): array { $result = array_filter(self::$queries, static function ($query) { return strpos($query[0], 'OPTIMIZE TABLE') === 0; diff --git a/tests/Unit/Mock/ObjectMock/DecryptableMock.php b/tests/Unit/Mock/ObjectMock/DecryptableMock.php deleted file mode 100644 index bc4f499b8a..0000000000 --- a/tests/Unit/Mock/ObjectMock/DecryptableMock.php +++ /dev/null @@ -1,18 +0,0 @@ -decrypted = true; - } - - public function isDecrypted(): bool - { - return $this->decrypted; - } -} \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectMock.php b/tests/Unit/Mock/ObjectMock/ObjectMock.php index aae0393ad4..6e51edf42a 100644 --- a/tests/Unit/Mock/ObjectMock/ObjectMock.php +++ b/tests/Unit/Mock/ObjectMock/ObjectMock.php @@ -6,6 +6,11 @@ class ObjectMock { public $publicVar = null; + public function __construct() + { + $this->methodsCalled = []; // do not declare as property + } + public function getTableName(): string { return 'object_mock'; @@ -15,4 +20,51 @@ public function getTableStructure(): string { return 'id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, publicVar VARCHAR(255)'; } + + public function withMethod(string $method): self + { + switch ($method) { + case 'setId': + return new ObjectWithSetId(); + case 'preSave': + return new ObjectWithPreSave(); + case 'preInsert': + return new ObjectWithPreInsert(); + case 'postInsert': + return new ObjectWithPostInsert(); + case 'encrypt': + return new ObjectWithEncrypt(); + case 'decrypt': + return new ObjectWithDecrypt(); + case 'postSave': + return new ObjectWithPostSave(); + + } + + throw new \Exception('Unknown method ' . $method); + } + + public function __call(string $method, array $arguments) + { + $this->methodsCalled[] = $method; + } + + public function isMethodCalled(string $method): bool + { + return in_array($method, $this->methodsCalled ?? [], true); + } + + public function withField(string $field): self + { + if ($field === 'id') { + return new ObjectWithIdField(); + } + + throw new \Exception('Unknown field ' . $field); + } + + public function withoutMethod(string $method): self + { + return $this; + } } \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithBadTypedId.php b/tests/Unit/Mock/ObjectMock/ObjectWithBadTypedId.php new file mode 100644 index 0000000000..c2b99d240f --- /dev/null +++ b/tests/Unit/Mock/ObjectMock/ObjectWithBadTypedId.php @@ -0,0 +1,10 @@ +methodsCalled[] = 'decrypt'; + } +} \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/DecryptableAndChangableMock.php b/tests/Unit/Mock/ObjectMock/ObjectWithDecryptAndChange.php similarity index 71% rename from tests/Unit/Mock/ObjectMock/DecryptableAndChangableMock.php rename to tests/Unit/Mock/ObjectMock/ObjectWithDecryptAndChange.php index b5ac78a96d..99a15e5b40 100644 --- a/tests/Unit/Mock/ObjectMock/DecryptableAndChangableMock.php +++ b/tests/Unit/Mock/ObjectMock/ObjectWithDecryptAndChange.php @@ -2,12 +2,13 @@ namespace Tests\Unit\Mock\ObjectMock; -class DecryptableAndChangableMock extends DecryptableMock +class ObjectWithDecryptAndChange extends ObjectWithDecrypt { private $changed = null; public function setChanged(bool $changed): void { + $this->methodsCalled[] = 'setChanged'; $this->changed = $changed; } diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithEncrypt.php b/tests/Unit/Mock/ObjectMock/ObjectWithEncrypt.php new file mode 100644 index 0000000000..8c453389a7 --- /dev/null +++ b/tests/Unit/Mock/ObjectMock/ObjectWithEncrypt.php @@ -0,0 +1,12 @@ +methodsCalled[] = 'encrypt'; + } + +} \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithPostInsert.php b/tests/Unit/Mock/ObjectMock/ObjectWithPostInsert.php new file mode 100644 index 0000000000..8a5b3c35a5 --- /dev/null +++ b/tests/Unit/Mock/ObjectMock/ObjectWithPostInsert.php @@ -0,0 +1,11 @@ +methodsCalled[] = 'postInsert'; + } +} \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithPostSave.php b/tests/Unit/Mock/ObjectMock/ObjectWithPostSave.php new file mode 100644 index 0000000000..69e6f1cc56 --- /dev/null +++ b/tests/Unit/Mock/ObjectMock/ObjectWithPostSave.php @@ -0,0 +1,11 @@ +methodsCalled[] = 'postSave'; + } +} \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithPreInsert.php b/tests/Unit/Mock/ObjectMock/ObjectWithPreInsert.php index c79cdc2a6d..8f3668a33d 100644 --- a/tests/Unit/Mock/ObjectMock/ObjectWithPreInsert.php +++ b/tests/Unit/Mock/ObjectMock/ObjectWithPreInsert.php @@ -4,15 +4,8 @@ class ObjectWithPreInsert extends ObjectMock { - private $preInsertCalled = false; - public function preInsert(): void { - $this->preInsertCalled = true; - } - - public function isPreInsertCalled(): bool - { - return $this->preInsertCalled; + $this->methodsCalled[] = 'preInsert'; } } \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithPreSave.php b/tests/Unit/Mock/ObjectMock/ObjectWithPreSave.php index fe8cc7010a..6ce51c8d21 100644 --- a/tests/Unit/Mock/ObjectMock/ObjectWithPreSave.php +++ b/tests/Unit/Mock/ObjectMock/ObjectWithPreSave.php @@ -4,15 +4,8 @@ class ObjectWithPreSave extends ObjectMock { - private $preSaveCalled = false; - public function preSave(): void { - $this->preSaveCalled = true; - } - - public function isPreSaveCalled(): bool - { - return $this->preSaveCalled; + $this->methodsCalled[] = 'preSave'; } } \ No newline at end of file diff --git a/tests/Unit/Mock/ObjectMock/ObjectWithSetId.php b/tests/Unit/Mock/ObjectMock/ObjectWithSetId.php new file mode 100644 index 0000000000..94031d33f1 --- /dev/null +++ b/tests/Unit/Mock/ObjectMock/ObjectWithSetId.php @@ -0,0 +1,15 @@ +