From a3ad158dfecc71e18e8f53df4cc4693f50918bf4 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Wed, 25 Sep 2024 20:03:17 -0400 Subject: [PATCH] Add in/inArray tests - Make nameFields void (modifies array by ref) --- src/Count.php | 6 ++-- src/Delete.php | 5 ++- src/Exists.php | 6 ++-- src/Find.php | 12 +++---- src/Op.php | 2 +- src/Parameters.php | 6 ++-- src/Patch.php | 6 ++-- src/RemoveFields.php | 6 ++-- tests/integration/ArrayDocument.php | 35 ++++++++++++++++++ tests/integration/postgresql/FindTest.php | 43 ++++++++++++++++++++--- tests/integration/sqlite/FindTest.php | 42 ++++++++++++++++++++-- tests/unit/FieldTest.php | 2 +- tests/unit/OpTest.php | 2 +- tests/unit/ParametersTest.php | 4 +-- 14 files changed, 140 insertions(+), 37 deletions(-) create mode 100644 tests/integration/ArrayDocument.php diff --git a/src/Count.php b/src/Count.php index 27e43f4..44df8f6 100644 --- a/src/Count.php +++ b/src/Count.php @@ -38,9 +38,9 @@ class Count */ public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): int { - $namedFields = Parameters::nameFields($fields); - return Custom::scalar(Query\Count::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, []), new CountMapper()); + Parameters::nameFields($fields); + return Custom::scalar(Query\Count::byFields($tableName, $fields, $match), Parameters::addFields($fields, []), + new CountMapper()); } /** diff --git a/src/Delete.php b/src/Delete.php index 003f05a..554a5ba 100644 --- a/src/Delete.php +++ b/src/Delete.php @@ -35,9 +35,8 @@ class Delete */ public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): void { - $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\Delete::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, [])); + Parameters::nameFields($fields); + Custom::nonQuery(Query\Delete::byFields($tableName, $fields, $match), Parameters::addFields($fields, [])); } /** diff --git a/src/Exists.php b/src/Exists.php index dc7f6f3..1b2d4c8 100644 --- a/src/Exists.php +++ b/src/Exists.php @@ -39,9 +39,9 @@ class Exists */ public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): bool { - $namedFields = Parameters::nameFields($fields); - return Custom::scalar(Query\Exists::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, []), new ExistsMapper()); + Parameters::nameFields($fields); + return Custom::scalar(Query\Exists::byFields($tableName, $fields, $match), Parameters::addFields($fields, []), + new ExistsMapper()); } /** diff --git a/src/Find.php b/src/Find.php index f42e42b..a8157f8 100644 --- a/src/Find.php +++ b/src/Find.php @@ -60,9 +60,9 @@ class Find public static function byFields(string $tableName, array $fields, string $className, ?FieldMatch $match = null): DocumentList { - $namedFields = Parameters::nameFields($fields); - return Custom::list(Query\Find::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, []), new DocumentMapper($className)); + Parameters::nameFields($fields); + return Custom::list(Query\Find::byFields($tableName, $fields, $match), Parameters::addFields($fields, []), + new DocumentMapper($className)); } /** @@ -110,9 +110,9 @@ class Find public static function firstByFields(string $tableName, array $fields, string $className, ?FieldMatch $match = null): Option { - $namedFields = Parameters::nameFields($fields); - return Custom::single(Query\Find::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, []), new DocumentMapper($className)); + Parameters::nameFields($fields); + return Custom::single(Query\Find::byFields($tableName, $fields, $match), + Parameters::addFields($fields, []), new DocumentMapper($className)); } /** diff --git a/src/Op.php b/src/Op.php index 1956581..97c1c8e 100644 --- a/src/Op.php +++ b/src/Op.php @@ -52,7 +52,7 @@ enum Op Op::NotEqual => "<>", Op::Between => "BETWEEN", Op::In => "IN", - Op::InArray => "?|", + Op::InArray => "??|", // The actual operator is ?|, but needs to be escaped by doubling Op::Exists => "IS NOT NULL", Op::NotExists => "IS NULL", }; diff --git a/src/Parameters.php b/src/Parameters.php index f30e046..5ffa4f6 100644 --- a/src/Parameters.php +++ b/src/Parameters.php @@ -59,15 +59,13 @@ class Parameters /** * Fill in parameter names for any fields missing one * - * @param Field[] $fields The fields for the query - * @return Field[] The fields, all with non-blank parameter names + * @param Field[] $fields The fields for the query (entries with no names will be modified) */ - public static function nameFields(array $fields): array + public static function nameFields(array &$fields): void { array_walk($fields, function (Field $field, int $idx) { if (empty($field->paramName)) $field->paramName =":field$idx"; }); - return $fields; } /** diff --git a/src/Patch.php b/src/Patch.php index a28506a..ab918e9 100644 --- a/src/Patch.php +++ b/src/Patch.php @@ -39,9 +39,9 @@ class Patch public static function byFields(string $tableName, array $fields, array|object $patch, ?FieldMatch $match = null): void { - $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $match), - Parameters::addFields($namedFields, Parameters::json(':data', $patch))); + Parameters::nameFields($fields); + Custom::nonQuery(Query\Patch::byFields($tableName, $fields, $match), + Parameters::addFields($fields, Parameters::json(':data', $patch))); } /** diff --git a/src/RemoveFields.php b/src/RemoveFields.php index 5651904..38b0257 100644 --- a/src/RemoveFields.php +++ b/src/RemoveFields.php @@ -41,9 +41,9 @@ class RemoveFields ?FieldMatch $match = null): void { $nameParams = Parameters::fieldNames(':name', $fieldNames); - $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $match), - Parameters::addFields($namedFields, $nameParams)); + Parameters::nameFields($fields); + Custom::nonQuery(Query\RemoveFields::byFields($tableName, $fields, $nameParams, $match), + Parameters::addFields($fields, $nameParams)); } /** diff --git a/tests/integration/ArrayDocument.php b/tests/integration/ArrayDocument.php new file mode 100644 index 0000000..59e5320 --- /dev/null +++ b/tests/integration/ArrayDocument.php @@ -0,0 +1,35 @@ + + * @license MIT + */ + +declare(strict_types=1); + +namespace Test\Integration; + +/** + * A document with an array of values + */ +class ArrayDocument +{ + /** + * @param string $id The ID of the document + * @param string[] $values The values for the document + */ + public function __construct(public string $id = '', public array $values = []) { } + + /** + * A set of documents used for integration tests + * + * @return ArrayDocument[] Test documents for InArray tests + */ + public static function testDocuments(): array + { + return [ + new ArrayDocument('first', ['a', 'b', 'c']), + new ArrayDocument('second', ['c', 'd', 'e']), + new ArrayDocument('third', ['x', 'y', 'z']) + ]; + } +} diff --git a/tests/integration/postgresql/FindTest.php b/tests/integration/postgresql/FindTest.php index 7353f03..8a124e1 100644 --- a/tests/integration/postgresql/FindTest.php +++ b/tests/integration/postgresql/FindTest.php @@ -8,10 +8,10 @@ declare(strict_types=1); namespace Test\Integration\PostgreSQL; -use BitBadger\PDODocument\{Custom, Delete, Document, Field, Find}; +use BitBadger\PDODocument\{Custom, Delete, Document, Field, FieldMatch, Find}; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; -use Test\Integration\{NumDocument, TestDocument}; +use Test\Integration\{ArrayDocument, NumDocument, TestDocument}; /** * PostgreSQL integration tests for the Find class @@ -81,11 +81,22 @@ class FindTest extends TestCase #[TestDox('byFields() succeeds when documents are found')] public function testByFieldsSucceedsWhenDocumentsAreFound(): void { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::greater('num_value', 15)], TestDocument::class); + $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(2, $count, 'There should have been 2 documents in the list'); + $this->assertEquals(1, $count, 'There should have been 1 document in the list'); + } + + #[TestDox('byFields() succeeds when documents are found using IN with numeric field')] + public function testByFieldsSucceedsWhenDocumentsAreFoundUsingInWithNumericField() + { + $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')] @@ -96,6 +107,30 @@ class FindTest extends TestCase $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 { diff --git a/tests/integration/sqlite/FindTest.php b/tests/integration/sqlite/FindTest.php index a948cd4..754610d 100644 --- a/tests/integration/sqlite/FindTest.php +++ b/tests/integration/sqlite/FindTest.php @@ -8,9 +8,10 @@ declare(strict_types=1); namespace Test\Integration\SQLite; -use BitBadger\PDODocument\{Custom, Document, DocumentException, Field, Find}; +use BitBadger\PDODocument\{Custom, Delete, Document, DocumentException, Field, FieldMatch, Find}; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; +use Test\Integration\ArrayDocument; use Test\Integration\TestDocument; /** @@ -80,11 +81,22 @@ class FindTest extends TestCase #[TestDox('byFields() succeeds when documents are found')] public function testByFieldsSucceedsWhenDocumentsAreFound(): void { - $docs = Find::byFields(ThrowawayDb::TABLE, [Field::greater('num_value', 15)], TestDocument::class); + $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(2, $count, 'There should have been 2 documents in the list'); + $this->assertEquals(1, $count, 'There should have been 1 document in the list'); + } + + #[TestDox('byFields() succeeds when documents are found using IN with numeric field')] + public function testByFieldsSucceedsWhenDocumentsAreFoundUsingInWithNumericField() + { + $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')] @@ -95,6 +107,30 @@ class FindTest extends TestCase $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() fails')] public function testByContainsFails(): void { diff --git a/tests/unit/FieldTest.php b/tests/unit/FieldTest.php index a8c78ee..6ab1fe6 100644 --- a/tests/unit/FieldTest.php +++ b/tests/unit/FieldTest.php @@ -349,7 +349,7 @@ class FieldTest extends TestCase Configuration::overrideMode(Mode::PgSQL); try { $field = Field::inArray('even', 'tbl', [2, 4, 6, 8], ':it'); - $this->assertEquals("data->'even' ?| ARRAY[:it_0, :it_1, :it_2, :it_3]", $field->toWhere(), + $this->assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]", $field->toWhere(), 'WHERE fragment not generated correctly'); } finally { Configuration::overrideMode(null); diff --git a/tests/unit/OpTest.php b/tests/unit/OpTest.php index baff4df..380d24f 100644 --- a/tests/unit/OpTest.php +++ b/tests/unit/OpTest.php @@ -69,7 +69,7 @@ class OpTest extends TestCase #[TestDox('toSQL() succeeds for InArray')] public function testToSQLSucceedsForInArray(): void { - $this->assertEquals('?|', Op::InArray->toSQL(), 'InArray SQL operator incorrect'); + $this->assertEquals('??|', Op::InArray->toSQL(), 'InArray SQL operator incorrect'); } #[TestDox('toSQL() succeeds for Exists')] diff --git a/tests/unit/ParametersTest.php b/tests/unit/ParametersTest.php index 1761adc..68da6d9 100644 --- a/tests/unit/ParametersTest.php +++ b/tests/unit/ParametersTest.php @@ -84,8 +84,8 @@ class ParametersTest extends TestCase #[TestDox('nameFields() succeeds')] public function testNameFieldsSucceeds(): void { - $named = Parameters::nameFields( - [Field::equal('it', 17), Field::equal('also', 22, ':also'), Field::equal('other', 24)]); + $named = [Field::equal('it', 17), Field::equal('also', 22, ':also'), Field::equal('other', 24)]; + Parameters::nameFields($named); $this->assertCount(3, $named, 'There should be 3 parameters in the array'); $this->assertEquals(':field0', $named[0]->paramName, 'Parameter 1 not named correctly'); $this->assertEquals(':also', $named[1]->paramName, 'Parameter 2 not named correctly');