diff --git a/composer.json b/composer.json index 805e628..140f868 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "psr-4": { "Test\\": "./tests", "Test\\Integration\\": "./tests/integration", - "Test\\Integration\\PostgreSQL\\": "./tests/integration/postgresql", + "Test\\Integration\\PostgreSQL\\": "./tests/Integration/PostgreSQL", "Test\\Integration\\SQLite\\": "./tests/integration/sqlite" } }, diff --git a/composer.lock b/composer.lock index 5fb20eb..32331b9 100644 --- a/composer.lock +++ b/composer.lock @@ -1234,16 +1234,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.5.1", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f" + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/0c70d2c566e899666f367ab7b80986beb3581e6f", - "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", "shasum": "" }, "require": { @@ -1252,7 +1252,7 @@ "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { @@ -1292,9 +1292,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" }, - "time": "2024-11-06T11:58:54+00:00" + "time": "2024-11-12T11:25:25+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1356,30 +1356,30 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.33.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^5.3.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "symfony/process": "^5.2" }, "type": "library", @@ -1397,9 +1397,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0" }, - "time": "2024-10-13T11:25:22+00:00" + "time": "2024-10-13T11:29:49+00:00" }, { "name": "phpstan/phpstan", @@ -3009,16 +3009,16 @@ }, { "name": "symfony/console", - "version": "v7.1.7", + "version": "v7.2.0-RC1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a" + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a", - "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a", + "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", "shasum": "" }, "require": { @@ -3082,7 +3082,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.7" + "source": "https://github.com/symfony/console/tree/v7.2.0-RC1" }, "funding": [ { @@ -3098,7 +3098,7 @@ "type": "tidelift" } ], - "time": "2024-11-05T15:34:55+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3169,16 +3169,16 @@ }, { "name": "symfony/finder", - "version": "v7.1.6", + "version": "v7.2.0-RC1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", "shasum": "" }, "require": { @@ -3213,7 +3213,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.6" + "source": "https://github.com/symfony/finder/tree/v7.2.0-RC1" }, "funding": [ { @@ -3229,7 +3229,7 @@ "type": "tidelift" } ], - "time": "2024-10-01T08:31:23+00:00" + "time": "2024-10-23T06:56:12+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3551,16 +3551,16 @@ }, { "name": "symfony/process", - "version": "v7.1.7", + "version": "v7.2.0-RC1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "9b8a40b7289767aa7117e957573c2a535efe6585" + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585", - "reference": "9b8a40b7289767aa7117e957573c2a535efe6585", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "shasum": "" }, "require": { @@ -3592,7 +3592,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.7" + "source": "https://github.com/symfony/process/tree/v7.2.0-RC1" }, "funding": [ { @@ -3608,7 +3608,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T09:25:12+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/service-contracts", @@ -3695,16 +3695,16 @@ }, { "name": "symfony/string", - "version": "v7.1.6", + "version": "v7.2.0-RC1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", - "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { @@ -3762,7 +3762,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.6" + "source": "https://github.com/symfony/string/tree/v7.2.0-RC1" }, "funding": [ { @@ -3778,7 +3778,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/tests/Integration/PostgreSQL/DocumentTest.php b/tests/Integration/PostgreSQL/DocumentTest.php index 0c968b6..8716c58 100644 --- a/tests/Integration/PostgreSQL/DocumentTest.php +++ b/tests/Integration/PostgreSQL/DocumentTest.php @@ -8,8 +8,8 @@ declare(strict_types=1); use BitBadger\PDODocument\{AutoId, Configuration, Custom, Document, DocumentException, Field, Find, Query}; use BitBadger\PDODocument\Mapper\ArrayMapper; -use Test\Integration\PostgreSQL\ThrowawayDb; use Test\Integration\{NumDocument, SubDocument, TestDocument}; +use Test\Integration\PostgreSQL\ThrowawayDb; pest()->group('integration', 'postgresql'); diff --git a/tests/Integration/PostgreSQL/ExistsTest.php b/tests/Integration/PostgreSQL/ExistsTest.php new file mode 100644 index 0000000..44efbc4 --- /dev/null +++ b/tests/Integration/PostgreSQL/ExistsTest.php @@ -0,0 +1,48 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\PDODocument\{Exists, Field}; +use Test\Integration\PostgreSQL\ThrowawayDb; + +pest()->group('integration', 'postgresql'); + +describe('::byId()', function () { + test('returns true when a document exists', function () { + expect(Exists::byId(ThrowawayDb::TABLE, 'three'))->toBeTrue(); + }); + test('returns false when a document does not exist', function () { + expect(Exists::byId(ThrowawayDb::TABLE, 'seven'))->toBeFalse(); + }); +}); + +describe('::byFields()', function () { + test('returns true when matching documents exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 10)]))->toBeTrue(); + }); + test('returns false when no matching documents exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::less('nothing', 'none')]))->toBeFalse(); + }); +}); + +describe('::byContains()', function () { + test('returns true when matching documents exist', function () { + expect(Exists::byContains(ThrowawayDb::TABLE, ['value' => 'purple']))->toBeTrue(); + }); + test('returns false when no matching documents exist', function () { + expect(Exists::byContains(ThrowawayDb::TABLE, ['value' => 'violet']))->toBeFalse(); + }); +}); + +describe('::byJsonPath()', function () { + test('returns true when matching documents exist', function () { + expect(Exists::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)'))->toBeTrue(); + }); + test('returns false when no matching documents exist', function () { + expect(Exists::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10.1)'))->toBeFalse(); + }); +}); diff --git a/tests/Integration/PostgreSQL/FindTest.php b/tests/Integration/PostgreSQL/FindTest.php new file mode 100644 index 0000000..f46ea28 --- /dev/null +++ b/tests/Integration/PostgreSQL/FindTest.php @@ -0,0 +1,216 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\PDODocument\{Custom, Delete, Document, Field, FieldMatch, Find}; +use Test\Integration\{ArrayDocument, NumDocument, TestDocument}; +use Test\Integration\PostgreSQL\ThrowawayDb; + +pest()->group('integration', 'postgresql'); + +describe('::all()', function () { + test('retrieves data', function () { + $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(5); + }); + test('sorts data ascending', function () { + $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, [Field::named('id')]); + expect($docs)->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false)) + ->toBe(['five', 'four', 'one', 'three', 'two']); + }); + test('sorts data descending', function () { + $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, [Field::named('id DESC')]); + expect($docs)->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false)) + ->toBe(['two', 'three', 'one', 'four', 'five']); + }); + test('sorts data numerically', function () { + $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, + [Field::named('sub.foo NULLS LAST'), Field::named('n:num_value')]); + expect($docs)->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false)) + ->toBe(['two', 'four', 'one', 'three', 'five']); + }); + test('retrieves empty results', function () { + Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); + expect(Find::all(ThrowawayDb::TABLE, TestDocument::class)) + ->not->toBeNull() + ->hasItems()->toBeFalse(); + }); +}); + +describe('::byId()', function () { + test('retrieves a document via string ID', function () { + expect(Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class)) + ->isSome()->toBeTrue() + ->get()->id->toBe('two'); + }); + test('retrieves a document via numeric ID', function () { + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absent')]); + Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); + expect(Find::byId(ThrowawayDb::TABLE, 18, NumDocument::class)) + ->isSome()->toBeTrue() + ->get()->id->toBe(18); + }); + test('returns None when a document is not found', function () { + expect(Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class))->isNone()->toBeTrue(); + }); +}); + +describe('::byFields()', function () { + test('retrieves matching documents', function () { + $docs = Find::byFields(ThrowawayDb::TABLE, [Field::in('value', ['blue', 'purple']), Field::exists('sub')], + TestDocument::class, FieldMatch::All); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(1); + }); + test('retrieves ordered matching documents', function () { + $docs = Find::byFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], TestDocument::class, + FieldMatch::All, [Field::named('id')]); + expect($docs)->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false))->toBe(['five', 'four']); + }); + test('retrieves documents matching a numeric IN clause', function () { + $docs = Find::byFields(ThrowawayDb::TABLE, [Field::in('num_value', [2, 4, 6, 8])], TestDocument::class); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(1); + }); + test('returns an empty list when no matching documents are found', function () { + expect(Find::byFields(ThrowawayDb::TABLE, [Field::greater('num_value', 100)], TestDocument::class)) + ->not->toBeNull() + ->hasItems()->toBeFalse(); + }); + test('retrieves documents matching an inArray condition', function () { + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); + foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); + $docs = Find::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['c'])], + ArrayDocument::class); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(2); + }); + test('returns an empty list when no documents match an inArray condition', function () { + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); + foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); + expect(Find::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['j'])], + ArrayDocument::class)) + ->not->toBeNull() + ->hasItems()->toBeFalse(); + }); +}); + +describe('::byContains()', function () { + test('retrieves matching documents', function () { + $docs = Find::byContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(2); + }); + test('retrieves ordered matching documents', function () { + $docs = Find::byContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], TestDocument::class, + [Field::named('value')]); + expect($docs) + ->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false))->toBe(['two', 'four']); + }); + test('returns an empty list when no documents match', function () { + expect(Find::byContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class)) + ->not->toBeNull() + ->hasItems()->toBeFalse(); + }); +}); + +describe('::byJsonPath()', function () { + test('retrieves matching documents', function () { + $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); + expect($docs)->not->toBeNull(); + $count = 0; + foreach ($docs->items() as $ignored) $count++; + expect($count)->toBe(2); + }); + test('retrieves ordered matching documents', function () { + $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class, + [Field::named('id')]); + expect($docs)->not->toBeNull() + ->and(iterator_to_array($docs->map(fn ($it) => $it->id), false))->toBe(['five', 'four']); + }); + test('returns an empty list when no documents match', function () { + expect(Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class)) + ->not->toBeNull() + ->hasItems()->toBeFalse(); + }); +}); + +describe('::firstByFields()', function () { + test('retrieves a matching document', function () { + expect(Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'another')], TestDocument::class)) + ->isSome()->toBeTrue()->get()->id->toBe('two'); + }); + test('retrieves a document for multiple results', function () { + $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], TestDocument::class); + expect($doc)->isSome()->toBeTrue()->and(['two', 'four'])->toContain($doc->get()->id); + }); + test('retrieves a document for multiple ordered results', function () { + expect(Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], TestDocument::class, + orderBy: [Field::named('n:num_value DESC')])) + ->isSome()->toBeTrue()->get()->id->toBe('four'); + }); + test('returns None when no documents match', function () { + expect(Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'absent')], TestDocument::class)) + ->isNone()->toBeTrue(); + }); +}); + +describe('::firstByContains()', function () { + test('retrieves a matching document', function () { + expect(Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'], TestDocument::class)) + ->isSome()->toBeTrue()->get()->id->toBe('one'); + }); + test('retrieves a document for multiple results', function () { + $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class); + expect($doc)->isSome()->toBeTrue()->and(['four', 'five'])->toContain($doc->get()->id); + }); + test('retrieves a document for multiple ordered results', function () { + expect(Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class, + [Field::named('sub.bar NULLS FIRST')])) + ->isSome()->toBeTrue()->get()->id->toBe('five'); + }); + test('returns None when no documents match', function () { + expect(Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class)) + ->isNone()->toBeTrue(); + }); +}); + +describe('::firstByJsonPath()', function () { + test('retrieves a matching document', function () { + expect(Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)', TestDocument::class)) + ->isSome()->toBeTrue()->get()->id->toBe('two'); + }); + test('retrieves a document for multiple results', function () { + $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); + expect($doc)->isSome()->toBeTrue()->and(['four', 'five'])->toContain($doc->get()->id); + }); + test('retrieves a document for multiple ordered results', function () { + expect(Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class, + [Field::named('id DESC')])) + ->isSome()->toBeTrue()->get()->id->toBe('four'); + }); + test('returns None when no documents match', function () { + expect(Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class)) + ->isNone()->toBeTrue(); + }); +}); diff --git a/tests/Integration/PostgreSQL/PatchTest.php b/tests/Integration/PostgreSQL/PatchTest.php new file mode 100644 index 0000000..751bf6b --- /dev/null +++ b/tests/Integration/PostgreSQL/PatchTest.php @@ -0,0 +1,72 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\PDODocument\{Count, Exists, Field, Find, Patch}; +use Test\Integration\PostgreSQL\ThrowawayDb; +use Test\Integration\TestDocument; + +pest()->group('integration', 'postgresql'); + +describe('::byId()', function () { + test('updates an existing document', function () { + Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]); + expect(Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class)) + ->isSome()->toBeTrue() + ->get()->num_value->toBe(44); + }); + test('does nothing when a document does not exist', function () { + $id = 'forty-seven'; + expect(Exists::byId(ThrowawayDb::TABLE, $id))->toBeFalse(); + Patch::byId(ThrowawayDb::TABLE, $id, ['foo' => 'green']); // no exception = pass + }); +}); + +describe('::byFields()', function () { + test('updates existing documents', function () { + Patch::byFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], ['num_value' => 77]); + expect(Count::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 77)]))->toBe(2); + }); + test('does nothing when no matching documents exist', function () { + $fields = [Field::equal('value', 'burgundy')]; + expect(Count::byFields(ThrowawayDb::TABLE, $fields))->toBe(0); + Patch::byFields(ThrowawayDb::TABLE, $fields, ['foo' => 'green']); // no exception = pass + }); +}); + +describe('::byContains()', function () { + test('updates existing documents', function () { + Patch::byContains(ThrowawayDb::TABLE, ['value' => 'another'], ['num_value' => 12]); + $tryDoc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'another'], TestDocument::class); + expect($tryDoc)->isSome()->toBeTrue() + ->and($tryDoc->get()) + ->id->toBe('two') + ->num_value->toBe(12); + }); + test('does nothing when no matching documents exist', function () { + $criteria = ['value' => 'updated']; + expect(Count::byContains(ThrowawayDb::TABLE, $criteria))->toBe(0); + Patch::byContains(ThrowawayDb::TABLE, $criteria, ['sub.foo' => 'green']); // no exception = pass + }); +}); + +describe('::byJsonPath()', function () { + test('updates existing documents', function () { + Patch::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', ['value' => 'blue']); + $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); + expect($docs)->not->toBeNull()->hasItems()->toBeTrue(); + foreach ($docs->items() as $item) { + expect(['four', 'five'])->toContain($item->id) + ->and($item->value)->toBe('blue'); + } + }); + test('does nothing when no matching documents exist', function () { + $path = '$.num_value ? (@ > 100)'; + expect(Count::byJsonPath(ThrowawayDb::TABLE, $path))->toBe(0); + Patch::byJsonPath(ThrowawayDb::TABLE, $path, ['value' => 'blue']); // no exception = pass + }); +}); diff --git a/tests/Integration/PostgreSQL/RemoveFieldsTest.php b/tests/Integration/PostgreSQL/RemoveFieldsTest.php new file mode 100644 index 0000000..8047644 --- /dev/null +++ b/tests/Integration/PostgreSQL/RemoveFieldsTest.php @@ -0,0 +1,91 @@ + + * @license MIT + */ + +declare(strict_types=1); + +use BitBadger\PDODocument\{Exists, Field, Find, RemoveFields}; +use Test\Integration\PostgreSQL\ThrowawayDb; +use Test\Integration\TestDocument; + +pest()->group('integration', 'postgresql'); + +describe('::byId()', function () { + test('removes fields', function () { + RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']); + $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); + expect($tryDoc)->isSome()->toBeTrue(); + $doc = $tryDoc->get(); + expect($doc)->sub->toBeNull() + ->and($doc->value)->toBeEmpty(); + }); + test('does nothing when the field to remove does not exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::exists('a_field_that_does_not_exist')]))->toBeFalse(); + RemoveFields::byId(ThrowawayDb::TABLE, 'one', ['a_field_that_does_not_exist']); // no exception = pass + }); + test('does nothing when the document does not exist', function () { + expect(Exists::byId(ThrowawayDb::TABLE, 'fifty'))->toBeFalse(); + RemoveFields::byId(ThrowawayDb::TABLE, 'fifty', ['sub']); // no exception = pass + }); +}); + +describe('::byFields()', function () { + test('removes fields from matching documents', function () { + RemoveFields::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], ['sub']); + expect(Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], TestDocument::class)) + ->isSome()->toBeTrue() + ->get()->sub->toBeNull(); + }); + test('does nothing when the field to remove does not exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::exists('nada')]))->toBeFalse(); + RemoveFields::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], ['nada']); // no exception = pass + }); + test('does nothing when no documents match', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::notEqual('missing', 'nope')]))->toBeFalse(); + RemoveFields::byFields(ThrowawayDb::TABLE, [Field::notEqual('missing', 'nope')], ['value']); // no exn = pass + }); +}); + +describe('::byContains()', function () { + test('removes fields from matching documents', function () { + $criteria = ['sub' => ['foo' => 'green']]; + RemoveFields::byContains(ThrowawayDb::TABLE, $criteria, ['value']); + $docs = Find::byContains(ThrowawayDb::TABLE, $criteria, TestDocument::class); + expect($docs)->not->toBeNull()->hasItems()->toBeTrue(); + foreach ($docs->items() as $item) { + expect(['two', 'four'])->toContain($item->id) + ->and($item->value)->toBeEmpty(); + } + }); + test('does nothing when the field to remove does not exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::exists('invalid_field')]))->toBeFalse(); + RemoveFields::byContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], ['invalid_field']); // no exn = pass + }); + test('does nothing when no documents match', function () { + expect(Exists::byContains(ThrowawayDb::TABLE, ['value' => 'substantial']))->toBeFalse(); + RemoveFields::byContains(ThrowawayDb::TABLE, ['value' => 'substantial'], ['num_value']); // no exception = pass + }); +}); + +describe('::byJsonPath()', function () { + test('removes fields from matching documents', function () { + $path = '$.value ? (@ == "purple")'; + RemoveFields::byJsonPath(ThrowawayDb::TABLE, $path, ['sub']); + $docs = Find::byJsonPath(ThrowawayDb::TABLE, $path, TestDocument::class); + expect($docs)->not->toBeNull()->hasItems()->toBeTrue(); + foreach ($docs->items() as $item) { + expect(['four', 'five'])->toContain($item->id) + ->and($item->sub)->toBeNull(); + } + }); + test('does nothing when the field to remove does not exist', function () { + expect(Exists::byFields(ThrowawayDb::TABLE, [Field::exists('submarine')]))->toBeFalse(); + RemoveFields::byJsonPath(ThrowawayDb::TABLE, '$.value ? (@ == "purple")', ['submarine']); // no exception = pass + }); + test('does nothing when no documents match', function () { + expect(Exists::byJsonPath(ThrowawayDb::TABLE, '$.value ? (@ == "mauve")'))->toBeFalse(); + RemoveFields::byJsonPath(ThrowawayDb::TABLE, '$.value ? (@ == "mauve")', ['value']); // no exception = pass + }); +}); diff --git a/tests/integration/postgresql/ThrowawayDb.php b/tests/Integration/PostgreSQL/ThrowawayDb.php similarity index 100% rename from tests/integration/postgresql/ThrowawayDb.php rename to tests/Integration/PostgreSQL/ThrowawayDb.php diff --git a/tests/integration/postgresql/ExistsTest.php b/tests/integration/postgresql/ExistsTest.php deleted file mode 100644 index 097d08c..0000000 --- a/tests/integration/postgresql/ExistsTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test\Integration\PostgreSQL; - -use BitBadger\PDODocument\{Exists, Field}; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; - -/** - * PostgreSQL integration tests for the Exists class - */ -#[TestDox('Exists (PostgreSQL integration)')] -class ExistsTest extends TestCase -{ - /** @var string Database name for throwaway database */ - private string $dbName; - - protected function setUp(): void - { - parent::setUp(); - $this->dbName = ThrowawayDb::create(); - } - - protected function tearDown(): void - { - ThrowawayDb::destroy($this->dbName); - parent::tearDown(); - } - - #[TestDox('byId() succeeds when a document exists')] - public function testByIdSucceedsWhenADocumentExists(): void - { - $this->assertTrue(Exists::byId(ThrowawayDb::TABLE, 'three'), 'There should have been an existing document'); - } - - #[TestDox('byId() succeeds when a document does not exist')] - public function testByIdSucceedsWhenADocumentDoesNotExist(): void - { - $this->assertFalse(Exists::byId(ThrowawayDb::TABLE, 'seven'), - 'There should not have been an existing document'); - } - - #[TestDox('byFields() succeeds when documents exist')] - public function testByFieldsSucceedsWhenDocumentsExist(): void - { - $this->assertTrue(Exists::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 10)]), - 'There should have been existing documents'); - } - - #[TestDox('byFields() succeeds when no matching documents exist')] - public function testByFieldsSucceedsWhenNoMatchingDocumentsExist(): void - { - $this->assertFalse(Exists::byFields(ThrowawayDb::TABLE, [Field::less('nothing', 'none')]), - 'There should not have been any existing documents'); - } - - #[TestDox('byContains() succeeds when documents exist')] - public function testByContainsSucceedsWhenDocumentsExist(): void - { - $this->assertTrue(Exists::byContains(ThrowawayDb::TABLE, ['value' => 'purple']), - 'There should have been existing documents'); - } - - #[TestDox('byContains() succeeds when no matching documents exist')] - public function testByContainsSucceedsWhenNoMatchingDocumentsExist(): void - { - $this->assertFalse(Exists::byContains(ThrowawayDb::TABLE, ['value' => 'violet']), - 'There should not have been existing documents'); - } - - #[TestDox('byJsonPath() succeeds when documents exist')] - public function testByJsonPathSucceedsWhenDocumentsExist(): void - { - $this->assertTrue(Exists::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)'), - 'There should have been existing documents'); - } - - #[TestDox('byJsonPath() succeeds when no matching documents exist')] - public function testByJsonPathSucceedsWhenNoMatchingDocumentsExist(): void - { - $this->assertFalse(Exists::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10.1)'), - 'There should have been existing documents'); - } -} diff --git a/tests/integration/postgresql/FindTest.php b/tests/integration/postgresql/FindTest.php deleted file mode 100644 index 3fd2683..0000000 --- a/tests/integration/postgresql/FindTest.php +++ /dev/null @@ -1,323 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test\Integration\PostgreSQL; - -use BitBadger\PDODocument\{Custom, Delete, Document, Field, FieldMatch, Find}; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; -use Test\Integration\{ArrayDocument, NumDocument, TestDocument}; - -/** - * PostgreSQL integration tests for the Find class - */ -#[TestDox('Find (PostgreSQL integration)')] -class FindTest extends TestCase -{ - /** @var string Database name for throwaway database */ - private string $dbName; - - protected function setUp(): void - { - parent::setUp(); - $this->dbName = ThrowawayDb::create(); - } - - protected function tearDown(): void - { - ThrowawayDb::destroy($this->dbName); - parent::tearDown(); - } - - #[TestDox('all() succeeds when there is data')] - public function testAllSucceedsWhenThereIsData(): void - { - $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(5, $count, 'There should have been 5 documents in the list'); - } - - #[TestDox('all() succeeds when ordering data ascending')] - public function testAllSucceedsWhenOrderingDataAscending(): void - { - $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, [Field::named('id')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['five', 'four', 'one', 'three', 'two'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('all() succeeds when ordering data descending')] - public function testAllSucceedsWhenOrderingDataDescending(): void - { - $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, [Field::named('id DESC')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['two', 'three', 'one', 'four', 'five'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('all() succeeds when ordering data numerically')] - public function testAllSucceedsWhenOrderingDataNumerically(): void - { - $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class, - [Field::named('sub.foo NULLS LAST'), Field::named('n:num_value')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['two', 'four', 'one', 'three', 'five'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('all() succeeds when there is no data')] - public function testAllSucceedsWhenThereIsNoData(): void - { - Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); - $docs = Find::all(ThrowawayDb::TABLE, TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertFalse($docs->hasItems(), 'There should have been no documents in the list'); - } - - #[TestDox('byId() succeeds when a document is found')] - public function testByIdSucceedsWhenADocumentIsFound(): void - { - $doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('two', $doc->get()->id, 'An incorrect document was returned'); - } - - #[TestDox('byId() succeeds when a document is found with numeric ID')] - public function testByIdSucceedsWhenADocumentIsFoundWithNumericId(): void - { - Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absent')]); - Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); - $doc = Find::byId(ThrowawayDb::TABLE, 18, NumDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals(18, $doc->get()->id, 'An incorrect document was returned'); - } - - #[TestDox('byId() succeeds when a document is not found')] - public function testByIdSucceedsWhenADocumentIsNotFound(): void - { - $doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class); - $this->assertTrue($doc->isNone(), 'There should not have been a document returned'); - } - - #[TestDox('byFields() succeeds when documents are found')] - public function testByFieldsSucceedsWhenDocumentsAreFound(): void - { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::in('value', ['blue', 'purple']), Field::exists('sub')], - TestDocument::class, FieldMatch::All); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(1, $count, 'There should have been 1 document in the list'); - } - - #[TestDox('byFields() succeeds when documents are found and ordered')] - public function testByFieldsSucceedsWhenDocumentsAreFoundAndOrdered(): void - { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], TestDocument::class, - FieldMatch::All, [Field::named('id')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['five', 'four'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('byFields() succeeds when documents are found using IN with numeric field')] - public function testByFieldsSucceedsWhenDocumentsAreFoundUsingInWithNumericField(): void - { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::in('num_value', [2, 4, 6, 8])], TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(1, $count, 'There should have been 1 document in the list'); - } - - #[TestDox('byFields() succeeds when no documents are found')] - public function testByFieldsSucceedsWhenNoDocumentsAreFound(): void - { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::greater('num_value', 100)], TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertFalse($docs->hasItems(), 'There should have been no documents in the list'); - } - - #[TestDox('byFields() succeeds for inArray when matching documents exist')] - public function testByFieldsSucceedsForInArrayWhenMatchingDocumentsExist(): void - { - Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); - foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['c'])], - ArrayDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(2, $count, 'There should have been 2 documents in the list'); - } - - #[TestDox('byFields() succeeds for inArray when no matching documents exist')] - public function testByFieldsSucceedsForInArrayWhenNoMatchingDocumentsExist(): void - { - Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); - foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['j'])], - ArrayDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertFalse($docs->hasItems(), 'There should have been no documents in the list'); - } - - #[TestDox('byContains() succeeds when documents are found')] - public function testByContainsSucceedsWhenDocumentsAreFound(): void - { - $docs = Find::byContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(2, $count, 'There should have been 2 documents in the list'); - } - - #[TestDox('byContains() succeeds when documents are found and ordered')] - public function testByContainsSucceedsWhenDocumentsAreFoundAndOrdered(): void - { - $docs = Find::byContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], TestDocument::class, - [Field::named('value')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['two', 'four'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('byContains() succeeds when no documents are found')] - public function testByContainsSucceedsWhenNoDocumentsAreFound(): void - { - $docs = Find::byContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertFalse($docs->hasItems(), 'The document list should be empty'); - } - - #[TestDox('byJsonPath() succeeds when documents are found')] - public function testByJsonPathSucceedsWhenDocumentsAreFound(): void - { - $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $count = 0; - foreach ($docs->items() as $ignored) $count++; - $this->assertEquals(2, $count, 'There should have been 2 documents in the list'); - } - - #[TestDox('byJsonPath() succeeds when documents are found and ordered')] - public function testByJsonPathSucceedsWhenDocumentsAreFoundAndOrdered(): void - { - $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class, - [Field::named('id')]); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $ids = iterator_to_array($docs->map(fn ($it) => $it->id), false); - $this->assertEquals(['five', 'four'], $ids, 'The documents were not ordered correctly'); - } - - #[TestDox('byJsonPath() succeeds when no documents are found')] - public function testByJsonPathSucceedsWhenNoDocumentsAreFound(): void - { - $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertFalse($docs->hasItems(), 'The document list should be empty'); - } - - #[TestDox('firstByFields() succeeds when a document is found')] - public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void - { - $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'another')], TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('two', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByFields() succeeds when multiple documents are found')] - public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void - { - $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertContains($doc->get()->id, ['two', 'four'], 'An incorrect document was returned'); - } - - #[TestDox('firstByFields() succeeds when multiple ordered documents are found')] - public function testFirstByFieldsSucceedsWhenMultipleOrderedDocumentsAreFound(): void - { - $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], TestDocument::class, - orderBy: [Field::named('n:num_value DESC')]); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('four', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByFields() succeeds when a document is not found')] - public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void - { - $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'absent')], TestDocument::class); - $this->assertTrue($doc->isNone(), 'There should not have been a document returned'); - } - - #[TestDox('firstByContains() succeeds when a document is found')] - public function testFirstByContainsSucceedsWhenADocumentIsFound(): void - { - $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'], TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('one', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByContains() succeeds when multiple documents are found')] - public function testFirstByContainsSucceedsWhenMultipleDocumentsAreFound(): void - { - $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertContains($doc->get()->id, ['four', 'five'], 'An incorrect document was returned'); - } - - #[TestDox('firstByContains() succeeds when multiple ordered documents are found')] - public function testFirstByContainsSucceedsWhenMultipleOrderedDocumentsAreFound(): void - { - $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class, - [Field::named('sub.bar NULLS FIRST')]); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('five', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByContains() succeeds when a document is not found')] - public function testFirstByContainsSucceedsWhenADocumentIsNotFound(): void - { - $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class); - $this->assertTrue($doc->isNone(), 'There should not have been a document returned'); - } - - #[TestDox('firstByJsonPath() succeeds when a document is found')] - public function testFirstByJsonPathSucceedsWhenADocumentIsFound(): void - { - $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)', TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('two', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByJsonPath() succeeds when multiple documents are found')] - public function testFirstByJsonPathSucceedsWhenMultipleDocumentsAreFound(): void - { - $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertContains($doc->get()->id, ['four', 'five'], 'An incorrect document was returned'); - } - - #[TestDox('firstByJsonPath() succeeds when multiple ordered documents are found')] - public function testFirstByJsonPathSucceedsWhenMultipleOrderedDocumentsAreFound(): void - { - $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class, - [Field::named('id DESC')]); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals('four', $doc->get()->id, 'The incorrect document was returned'); - } - - #[TestDox('firstByJsonPath() succeeds when a document is not found')] - public function testFirstByJsonPathSucceedsWhenADocumentIsNotFound(): void - { - $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class); - $this->assertTrue($doc->isNone(), 'There should not have been a document returned'); - } -} diff --git a/tests/integration/postgresql/PatchTest.php b/tests/integration/postgresql/PatchTest.php deleted file mode 100644 index 9955441..0000000 --- a/tests/integration/postgresql/PatchTest.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test\Integration\PostgreSQL; - -use BitBadger\PDODocument\{Count, Exists, Field, Find, Patch}; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; -use Test\Integration\TestDocument; - -/** - * PostgreSQL integration tests for the Patch class - */ -#[TestDox('Patch (PostgreSQL integration)')] -class PatchTest extends TestCase -{ - /** @var string Database name for throwaway database */ - private string $dbName; - - protected function setUp(): void - { - parent::setUp(); - $this->dbName = ThrowawayDb::create(); - } - - protected function tearDown(): void - { - ThrowawayDb::destroy($this->dbName); - parent::tearDown(); - } - - #[TestDox('byId() succeeds when a document is updated')] - public function testByIdSucceedsWhenADocumentIsUpdated(): void - { - Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]); - $doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertEquals(44, $doc->get()->num_value, 'The updated document is not correct'); - } - - #[TestDox('byId() succeeds when no document is updated')] - public function testByIdSucceedsWhenNoDocumentIsUpdated(): void - { - $id = 'forty-seven'; - $this->assertFalse(Exists::byId(ThrowawayDb::TABLE, $id), 'The document should not exist'); - Patch::byId(ThrowawayDb::TABLE, $id, ['foo' => 'green']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byFields() succeeds when a document is updated')] - public function testByFieldsSucceedsWhenADocumentIsUpdated(): void - { - Patch::byFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], ['num_value' => 77]); - $after = Count::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 77)]); - $this->assertEquals(2, $after, 'There should have been 2 documents updated'); - } - - #[TestDox('byFields() succeeds when no document is updated')] - public function testByFieldsSucceedsWhenNoDocumentIsUpdated(): void - { - $fields = [Field::equal('value', 'burgundy')]; - $this->assertEquals(0, Count::byFields(ThrowawayDb::TABLE, $fields), 'There should be no matching documents'); - Patch::byFields(ThrowawayDb::TABLE, $fields, ['foo' => 'green']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byContains() succeeds when documents are updated')] - public function testByContainsSucceedsWhenDocumentsAreUpdated(): void - { - Patch::byContains(ThrowawayDb::TABLE, ['value' => 'another'], ['num_value' => 12]); - $tryDoc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'another'], TestDocument::class); - $this->assertTrue($tryDoc->isSome(), 'There should have been a document returned'); - $doc = $tryDoc->get(); - $this->assertEquals('two', $doc->id, 'An incorrect document was returned'); - $this->assertEquals(12, $doc->num_value, 'The document was not patched'); - } - - #[TestDox('byContains() succeeds when no documents are updated')] - public function testByContainsSucceedsWhenNoDocumentsAreUpdated(): void - { - $criteria = ['value' => 'updated']; - $this->assertEquals(0, Count::byContains(ThrowawayDb::TABLE, $criteria), - 'There should be no matching documents'); - Patch::byContains(ThrowawayDb::TABLE, $criteria, ['sub.foo' => 'green']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byJsonPath() succeeds when documents are updated')] - public function testByJsonPathSucceedsWhenDocumentsAreUpdated(): void - { - Patch::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', ['value' => 'blue']); - $docs = Find::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertTrue($docs->hasItems(), 'The document list should not be empty'); - foreach ($docs->items() as $item) { - $this->assertContains($item->id, ['four', 'five'], 'An incorrect document was returned'); - $this->assertEquals('blue', $item->value, 'The document was not patched'); - } - } - - #[TestDox('byJsonPath() succeeds when documents are not updated')] - public function testByJsonPathSucceedsWhenDocumentsAreNotUpdated(): void - { - $path = '$.num_value ? (@ > 100)'; - $this->assertEquals(0, Count::byJsonPath(ThrowawayDb::TABLE, $path), - 'There should be no documents matching this path'); - Patch::byJsonPath(ThrowawayDb::TABLE, $path, ['value' => 'blue']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } -} diff --git a/tests/integration/postgresql/RemoveFieldsTest.php b/tests/integration/postgresql/RemoveFieldsTest.php deleted file mode 100644 index f83a200..0000000 --- a/tests/integration/postgresql/RemoveFieldsTest.php +++ /dev/null @@ -1,140 +0,0 @@ - - * @license MIT - */ - -declare(strict_types=1); - -namespace Test\Integration\PostgreSQL; - -use BitBadger\PDODocument\{Field, Find, RemoveFields}; -use PHPUnit\Framework\Attributes\TestDox; -use PHPUnit\Framework\TestCase; -use Test\Integration\TestDocument; - -/** - * PostgreSQL integration tests for the RemoveFields class - */ -#[TestDox('Remove Fields (PostgreSQL integration)')] -class RemoveFieldsTest extends TestCase -{ - /** @var string Database name for throwaway database */ - private string $dbName; - - protected function setUp(): void - { - parent::setUp(); - $this->dbName = ThrowawayDb::create(); - } - - protected function tearDown(): void - { - ThrowawayDb::destroy($this->dbName); - parent::tearDown(); - } - - #[TestDox('byId() succeeds when fields are removed')] - public function testByIdSucceedsWhenFieldsAreRemoved(): void - { - RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']); - $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); - $this->assertTrue($tryDoc->isSome(), 'There should have been a document returned'); - $doc = $tryDoc->get(); - $this->assertEquals('', $doc->value, 'Value should have been blank (its default value)'); - $this->assertNull($doc->sub, 'Sub-document should have been null'); - } - - #[TestDox('byId() succeeds when a field is not removed')] - public function testByIdSucceedsWhenAFieldIsNotRemoved(): void - { - RemoveFields::byId(ThrowawayDb::TABLE, 'one', ['a_field_that_does_not_exist']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byId() succeeds when no document is matched')] - public function testByIdSucceedsWhenNoDocumentIsMatched(): void - { - RemoveFields::byId(ThrowawayDb::TABLE, 'fifty', ['sub']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byFields() succeeds when a field is removed')] - public function testByFieldsSucceedsWhenAFieldIsRemoved(): void - { - RemoveFields::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], ['sub']); - $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], TestDocument::class); - $this->assertTrue($doc->isSome(), 'There should have been a document returned'); - $this->assertNull($doc->get()->sub, 'Sub-document should have been null'); - } - - #[TestDox('byFields() succeeds when a field is not removed')] - public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void - { - RemoveFields::byFields(ThrowawayDb::TABLE, [Field::equal('num_value', 17)], ['nada']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byFields() succeeds when no document is matched')] - public function testByFieldsSucceedsWhenNoDocumentIsMatched(): void - { - RemoveFields::byFields(ThrowawayDb::TABLE, [Field::notEqual('missing', 'nope')], ['value']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byContains() succeeds when a field is removed')] - public function testByContainsSucceedsWhenAFieldIsRemoved(): void - { - $criteria = ['sub' => ['foo' => 'green']]; - RemoveFields::byContains(ThrowawayDb::TABLE, $criteria, ['value']); - $docs = Find::byContains(ThrowawayDb::TABLE, $criteria, TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertTrue($docs->hasItems(), 'The document list should not have been empty'); - foreach ($docs->items() as $item) { - $this->assertContains($item->id, ['two', 'four'], 'An incorrect document was returned'); - $this->assertEquals('', $item->value, 'The value field was not removed'); - } - } - - #[TestDox('byContains() succeeds when a field is not removed')] - public function testByContainsSucceedsWhenAFieldIsNotRemoved(): void - { - RemoveFields::byContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], ['invalid_field']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byContains() succeeds when no document is matched')] - public function testByContainsSucceedsWhenNoDocumentIsMatched(): void - { - RemoveFields::byContains(ThrowawayDb::TABLE, ['value' => 'substantial'], ['num_value']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byJsonPath() succeeds when a field is removed')] - public function testByJsonPathSucceedsWhenAFieldIsRemoved(): void - { - $path = '$.value ? (@ == "purple")'; - RemoveFields::byJsonPath(ThrowawayDb::TABLE, $path, ['sub']); - $docs = Find::byJsonPath(ThrowawayDb::TABLE, $path, TestDocument::class); - $this->assertNotNull($docs, 'There should have been a document list returned'); - $this->assertTrue($docs->hasItems(), 'The document list should not have been empty'); - foreach ($docs->items() as $item) { - $this->assertContains($item->id, ['four', 'five'], 'An incorrect document was returned'); - $this->assertNull($item->sub, 'The sub field was not removed'); - } - } - - #[TestDox('byJsonPath() succeeds when a field is not removed')] - public function testByJsonPathSucceedsWhenAFieldIsNotRemoved(): void - { - RemoveFields::byJsonPath(ThrowawayDb::TABLE, '$.value ? (@ == "purple")', ['submarine']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } - - #[TestDox('byJsonPath() succeeds when no document is matched')] - public function testByJsonPathSucceedsWhenNoDocumentIsMatched(): void - { - RemoveFields::byJsonPath(ThrowawayDb::TABLE, '$.value ? (@ == "mauve")', ['value']); - $this->assertTrue(true, 'The above not throwing an exception is the test'); - } -}