diff --git a/tests/Integration/DocumentTestCase.php b/tests/Integration/DocumentTestCase.php new file mode 100644 index 0000000..c19181a --- /dev/null +++ b/tests/Integration/DocumentTestCase.php @@ -0,0 +1,38 @@ + + * @license MIT + * @see https://github.com/Zaid-Ajaj/ThrowawayDb The origin concept + */ + +declare(strict_types=1); + +namespace Test\Integration; + +use PHPUnit\Framework\TestCase; + +/** + * Base test case class for document integration tests + */ +class DocumentTestCase extends TestCase +{ + /** + * Clear the output buffer + */ + public function clearBuffer(): void + { + ob_clean(); + ob_start(); + } + + /** + * Get the contents of the output buffer and end buffering + * + * @return string The contents of the output buffer + */ + public function getBufferContents(): string { + $contents = ob_get_contents(); + ob_end_clean(); + return $contents; + } +} diff --git a/tests/Integration/PgIntegrationTest.php b/tests/Integration/PgIntegrationTest.php index 5e53482..51d162d 100644 --- a/tests/Integration/PgIntegrationTest.php +++ b/tests/Integration/PgIntegrationTest.php @@ -11,13 +11,12 @@ namespace Test\Integration; use BitBadger\PDODocument\{Configuration, Custom, Delete, DocumentException, Field}; use BitBadger\PDODocument\Mapper\ExistsMapper; -use PHPUnit\Framework\TestCase; use Test\Integration\PostgreSQL\ThrowawayDb; /** * Integration Test Class wrapper for PostgreSQL integration tests */ -class PgIntegrationTest extends TestCase +class PgIntegrationTest extends DocumentTestCase { /** @var string Database name for throwaway database */ static private string $dbName = ''; diff --git a/tests/Integration/PostgreSQL/CustomTest.php b/tests/Integration/PostgreSQL/CustomTest.php index 2660616..382c179 100644 --- a/tests/Integration/PostgreSQL/CustomTest.php +++ b/tests/Integration/PostgreSQL/CustomTest.php @@ -78,20 +78,14 @@ describe('::jsonArray()', function () { describe('::outputJsonArray()', function () { test('outputs non-empty array when data found', function () { - ob_clean(); - ob_start(); + $this->clearBuffer(); Custom::outputJsonArray(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'sub' IS NOT NULL", []); - $json = ob_get_contents(); - ob_end_clean(); - expect($json)->toContain('[{', '},{', '}]'); + expect($this->getBufferContents())->toContain('[{', '},{', '}]'); }); test('outputs empty array when no data found', function () { - ob_clean(); - ob_start(); + $this->clearBuffer(); Custom::outputJsonArray(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'nothing' = '7'", []); - $json = ob_get_contents(); - ob_end_clean(); - expect($json)->toBe('[]'); + expect($this->getBufferContents())->toBe('[]'); }); }); diff --git a/tests/Integration/PostgreSQL/JsonTest.php b/tests/Integration/PostgreSQL/JsonTest.php index 29e338f..4187115 100644 --- a/tests/Integration/PostgreSQL/JsonTest.php +++ b/tests/Integration/PostgreSQL/JsonTest.php @@ -7,11 +7,16 @@ declare(strict_types=1); use BitBadger\PDODocument\{Custom, Delete, Document, Field, FieldMatch, Json}; -use Test\Integration\{ArrayDocument, NumDocument, TestDocument}; +use Test\Integration\ArrayDocument; use Test\Integration\PostgreSQL\ThrowawayDb; pest()->group('integration', 'postgresql'); +// NOTE: PostgreSQL's `JSONB` type stores JSON in a binary representation which is more easily indexed and stored. One +// side effect of this is that the document returned may not have fields in the same order as the document which +// was originally stored. The ID will always be first, though, so these tests check for presence of IDs, sorting +// by IDs, etc., instead of checking the entire JSON string that should be either returned or output. + /** * Expect document ordering by verifying the index of IDs against others * @@ -27,14 +32,21 @@ function expect_doc_order(string $json, array $ids): void } } +/** + * Expect to find one of several document IDs in the given JSON + * @param string $json The JSON string to be searched + * @param string ...$ids One or more IDs to be searched + */ +function expect_any(string $json, string... $ids): void +{ + expect(array_any($ids, fn($it) => strpos($json, '"id": "' . $it . '",') >= 0)) + ->toBeTrue('Could not find any of IDs [' . implode(', ', $ids) . "] in $json"); +} + describe('::all()', function () { test('retrieves data', function () { expect(Json::all(ThrowawayDb::TABLE)) - ->toContain('{"id": "one",') - ->toContain('{"id": "two",') - ->toContain('{"id": "three",') - ->toContain('{"id": "four",') - ->toContain('{"id": "five",'); + ->toContain('{"id": "one",', '{"id": "two",', '{"id": "three",', '{"id": "four",', '{"id": "five",'); }); test('sorts data ascending', function () { expect_doc_order(Json::all(ThrowawayDb::TABLE, [Field::named('id')]), ['five', 'four', 'one', 'three', 'two']); @@ -56,164 +68,339 @@ describe('::all()', function () { describe('::byId()', function () { test('retrieves a document via string ID', function () { - expect(Json::byId(ThrowawayDb::TABLE, 'two'))->toStartWith('{')->toContain('"id": "two",')->toEndWith('}'); + expect(Json::byId(ThrowawayDb::TABLE, 'two'))->toStartWith('{"id": "two",')->toEndWith('}'); }); test('retrieves a document via numeric ID', function () { Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absent')]); Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); - expect(Json::byId(ThrowawayDb::TABLE, 18))->toStartWith('{')->toContain('"id": 18,')->toEndWith('}'); + expect(Json::byId(ThrowawayDb::TABLE, 18))->toStartWith('{"id": 18,')->toEndWith('}'); }); test('returns "{}" when a document is not found', function () { expect(Json::byId(ThrowawayDb::TABLE, 'seventy-five'))->toBe('{}'); }); }); -//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 () { -// $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'another')], TestDocument::class); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->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->value->id); -// }); -// test('retrieves a document for multiple ordered results', function () { -// $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], TestDocument::class, -// orderBy: [Field::named('n:num_value DESC')]); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->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 () { -// $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'], TestDocument::class); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->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->value->id); -// }); -// test('retrieves a document for multiple ordered results', function () { -// $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class, -// [Field::named('sub.bar NULLS FIRST')]); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->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 () { -// $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)', TestDocument::class); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->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->value->id); -// }); -// test('retrieves a document for multiple ordered results', function () { -// $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class, -// [Field::named('id DESC')]); -// expect($doc)->isSome->toBeTrue()->and($doc->value)->id->toBe('four'); -// }); -// test('returns None when no documents match', function () { -// expect(Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class)) -// ->isNone->toBeTrue(); -// }); -//}); +describe('::byFields()', function () { + test('retrieves matching documents', function () { + expect( + Json::byFields(ThrowawayDb::TABLE, [Field::in('value', ['blue', 'purple']), Field::exists('sub')], + FieldMatch::All)) + ->toStartWith('[{"id": "four",')->toEndWith('}]'); + }); + test('retrieves ordered matching documents', function () { + expect_doc_order( + Json::byFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], FieldMatch::All, + [Field::named('id')]), + ['five', 'four']); + }); + test('retrieves documents matching a numeric IN clause', function () { + expect(Json::byFields(ThrowawayDb::TABLE, [Field::in('num_value', [2, 4, 6, 8])])) + ->toStartWith('[{"id": "three",')->toEndWith('}]'); + }); + test('returns an empty array when no matching documents are found', function () { + expect(Json::byFields(ThrowawayDb::TABLE, [Field::greater('num_value', 100)]))->toBe('[]'); + }); + 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); + expect(Json::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['c'])])) + ->toStartWith('[')->toContain('{"id": "first",')->toContain('{"id": "second",')->toEndWith(']'); + }); + test('returns an empty array 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(Json::byFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['j'])])) + ->toBe('[]'); + }); +}); + +describe('::byContains()', function () { + test('retrieves matching documents', function () { + expect(Json::byContains(ThrowawayDb::TABLE, ['value' => 'purple'])) + ->toStartWith('[')->toContain('{"id": "four",', '{"id": "five",')->toEndWith(']'); + }); + test('retrieves ordered matching documents', function () { + expect_doc_order(Json::byContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], [Field::named('value')]), + ['two', 'four']); + }); + test('returns an empty array when no documents match', function () { + expect(Json::byContains(ThrowawayDb::TABLE, ['value' => 'indigo']))->toBe('[]'); + }); +}); + +describe('::byJsonPath()', function () { + test('retrieves matching documents', function () { + expect(Json::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)')) + ->toStartWith('[')->toContain('{"id": "four",', '{"id": "five",')->toEndWith(']'); + }); + test('retrieves ordered matching documents', function () { + expect_doc_order(Json::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', [Field::named('id')]), + ['five', 'four']); + }); + test('returns an empty array when no documents match', function () { + expect(Json::byJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)'))->toBe('[]'); + }); +}); + +describe('::firstByFields()', function () { + test('retrieves a matching document', function () { + expect(Json::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'another')])) + ->toStartWith('{"id": "two",')->toEndWith('}'); + }); + test('retrieves a document for multiple results', function () { + $doc = Json::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')]); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'two', 'four'); + }); + test('retrieves a document for multiple ordered results', function () { + expect( + Json::firstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], + orderBy: [Field::named('n:num_value DESC')])) + ->toStartWith('{"id": "four",')->toEndWith('}'); + }); + test('returns "{}" when no documents match', function () { + expect(Json::firstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'absent')]))->toBe('{}'); + }); +}); + +describe('::firstByContains()', function () { + test('retrieves a matching document', function () { + expect(Json::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'])) + ->toStartWith('{"id": "one",')->toEndWith('}'); + }); + test('retrieves a document for multiple results', function () { + $doc = Json::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple']); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'four', 'five'); + }); + test('retrieves a document for multiple ordered results', function () { + expect(Json::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], [Field::named('sub.bar NULLS FIRST')])) + ->toStartWith('{"id": "five",')->toEndWith('}'); + }); + test('returns "{}" when no documents match', function () { + expect(Json::firstByContains(ThrowawayDb::TABLE, ['value' => 'indigo']))->toBe('{}'); + }); +}); + +describe('::firstByJsonPath()', function () { + test('retrieves a matching document', function () { + expect(Json::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)')) + ->toStartWith('{"id": "two",')->toEndWith('}'); + }); + test('retrieves a document for multiple results', function () { + $doc = Json::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)'); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'four', 'five'); + }); + test('retrieves a document for multiple ordered results', function () { + expect(Json::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', [Field::named('id DESC')])) + ->toStartWith('{"id": "four",')->toEndWith('}'); + }); + test('returns "{}" when no documents match', function () { + expect(Json::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)'))->toBe('{}'); + }); +}); + +describe('::outputAll()', function () { + test('outputs data', function () { + $this->clearBuffer(); + Json::outputAll(ThrowawayDb::TABLE); + expect($this->getBufferContents()) + ->toContain('{"id": "one",', '{"id": "two",', '{"id": "three",', '{"id": "four",', '{"id": "five",'); + }); + test('sorts data ascending', function () { + $this->clearBuffer(); + Json::outputAll(ThrowawayDb::TABLE, [Field::named('id')]); + expect_doc_order($this->getBufferContents(), ['five', 'four', 'one', 'three', 'two']); + }); + test('sorts data descending', function () { + $this->clearBuffer(); + Json::outputAll(ThrowawayDb::TABLE, [Field::named('id DESC')]); + expect_doc_order($this->getBufferContents(), ['two', 'three', 'one', 'four', 'five']); + }); + test('sorts data numerically', function () { + $this->clearBuffer(); + Json::outputAll(ThrowawayDb::TABLE, [Field::named('sub.foo NULLS LAST'), Field::named('n:num_value')]); + expect_doc_order($this->getBufferContents(), ['two', 'four', 'one', 'three', 'five']); + }); + test('outputs empty results', function () { + $this->clearBuffer(); + Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); + Json::outputAll(ThrowawayDb::TABLE); + expect($this->getBufferContents())->toBe('[]'); + }); +}); + +describe('::outputById()', function () { + test('outputs a document via string ID', function () { + $this->clearBuffer(); + Json::outputById(ThrowawayDb::TABLE, 'two'); + expect($this->getBufferContents())->toStartWith('{"id": "two",')->toEndWith('}'); + }); + test('outputs a document via numeric ID', function () { + $this->clearBuffer(); + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absent')]); + Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); + Json::outputById(ThrowawayDb::TABLE, 18); + expect($this->getBufferContents())->toStartWith('{"id": 18,')->toEndWith('}'); + }); + test('outputs "{}" when a document is not found', function () { + $this->clearBuffer(); + Json::outputById(ThrowawayDb::TABLE, 'seventy-five'); + expect($this->getBufferContents())->toBe('{}'); + }); +}); + +describe('::outputByFields()', function () { + test('outputs matching documents', function () { + $this->clearBuffer(); + Json::outputByFields(ThrowawayDb::TABLE, [Field::in('value', ['blue', 'purple']), Field::exists('sub')], + FieldMatch::All); + expect($this->getBufferContents())->toStartWith('[{"id": "four",')->toEndWith('}]'); + }); + test('outputs ordered matching documents', function () { + $this->clearBuffer(); + Json::outputByFields(ThrowawayDb::TABLE, [Field::equal('value', 'purple')], FieldMatch::All, + [Field::named('id')]); + expect_doc_order($this->getBufferContents(), ['five', 'four']); + }); + test('outputs documents matching a numeric IN clause', function () { + $this->clearBuffer(); + Json::outputByFields(ThrowawayDb::TABLE, [Field::in('num_value', [2, 4, 6, 8])]); + expect($this->getBufferContents())->toStartWith('[{"id": "three",')->toEndWith('}]'); + }); + test('outputs an empty array when no matching documents are found', function () { + $this->clearBuffer(); + Json::outputByFields(ThrowawayDb::TABLE, [Field::greater('num_value', 100)]); + expect($this->getBufferContents())->toBe('[]'); + }); + test('outputs documents matching an inArray condition', function () { + $this->clearBuffer(); + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); + foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); + Json::outputByFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['c'])]); + expect($this->getBufferContents()) + ->toStartWith('[')->toContain('{"id": "first",', '{"id": "second",')->toEndWith(']'); + }); + test('outputs an empty array when no documents match an inArray condition', function () { + $this->clearBuffer(); + Delete::byFields(ThrowawayDb::TABLE, [Field::notExists('absentField')]); + foreach (ArrayDocument::testDocuments() as $doc) Document::insert(ThrowawayDb::TABLE, $doc); + Json::outputByFields(ThrowawayDb::TABLE, [Field::inArray('values', ThrowawayDb::TABLE, ['j'])]); + expect($this->getBufferContents())->toBe('[]'); + }); +}); + +describe('::outputByContains()', function () { + test('outputs matching documents', function () { + $this->clearBuffer(); + Json::outputByContains(ThrowawayDb::TABLE, ['value' => 'purple']); + expect($this->getBufferContents()) + ->toStartWith('[')->toContain('{"id": "four",', '{"id": "five",')->toEndWith(']'); + }); + test('outputs ordered matching documents', function () { + $this->clearBuffer(); + Json::outputByContains(ThrowawayDb::TABLE, ['sub' => ['foo' => 'green']], [Field::named('value')]); + expect_doc_order($this->getBufferContents(), ['two', 'four']); + }); + test('outputs an empty array when no documents match', function () { + $this->clearBuffer(); + Json::outputByContains(ThrowawayDb::TABLE, ['value' => 'indigo']); + expect($this->getBufferContents())->toBe('[]'); + }); +}); + +describe('::outputByJsonPath()', function () { + test('outputs matching documents', function () { + $this->clearBuffer(); + Json::outputByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)'); + expect($this->getBufferContents()) + ->toStartWith('[')->toContain('{"id": "four",', '{"id": "five",')->toEndWith(']'); + }); + test('outputs ordered matching documents', function () { + $this->clearBuffer(); + Json::outputByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', [Field::named('id')]); + expect_doc_order($this->getBufferContents(), ['five', 'four']); + }); + test('outputs an empty array when no documents match', function () { + $this->clearBuffer(); + Json::outputByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)'); + expect($this->getBufferContents())->toBe('[]'); + }); +}); + +describe('::outputFirstByFields()', function () { + test('outputs a matching document', function () { + $this->clearBuffer(); + Json::outputFirstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'another')]); + expect($this->getBufferContents())->toStartWith('{"id": "two",')->toEndWith('}'); + }); + test('outputs a document for multiple results', function () { + $this->clearBuffer(); + Json::outputFirstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')]); + $doc = $this->getBufferContents(); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'two', 'four'); + }); + test('outputs a document for multiple ordered results', function () { + $this->clearBuffer(); + Json::outputFirstByFields(ThrowawayDb::TABLE, [Field::equal('sub.foo', 'green')], + orderBy: [Field::named('n:num_value DESC')]); + expect($this->getBufferContents())->toStartWith('{"id": "four",')->toEndWith('}'); + }); + test('outputs "{}" when no documents match', function () { + $this->clearBuffer(); + Json::outputFirstByFields(ThrowawayDb::TABLE, [Field::equal('value', 'absent')]); + expect($this->getBufferContents())->toBe('{}'); + }); +}); + +describe('::outputFirstByContains()', function () { + test('outputs a matching document', function () { + $this->clearBuffer(); + Json::outputFirstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!']); + expect($this->getBufferContents())->toStartWith('{"id": "one",')->toEndWith('}'); + }); + test('outputs a document for multiple results', function () { + $this->clearBuffer(); + Json::outputFirstByContains(ThrowawayDb::TABLE, ['value' => 'purple']); + $doc = $this->getBufferContents(); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'four', 'five'); + }); + test('outputs a document for multiple ordered results', function () { + $this->clearBuffer(); + Json::outputFirstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], [Field::named('sub.bar NULLS FIRST')]); + expect($this->getBufferContents())->toStartWith('{"id": "five",')->toEndWith('}'); + }); + test('outputs "{}" when no documents match', function () { + $this->clearBuffer(); + Json::outputFirstByContains(ThrowawayDb::TABLE, ['value' => 'indigo']); + expect($this->getBufferContents())->toBe('{}'); + }); +}); + +describe('::outputFirstByJsonPath()', function () { + test('outputs a matching document', function () { + $this->clearBuffer(); + Json::outputFirstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)'); + expect($this->getBufferContents())->toStartWith('{"id": "two",')->toEndWith('}'); + }); + test('outputs a document for multiple results', function () { + $this->clearBuffer(); + Json::outputFirstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)'); + $doc = $this->getBufferContents(); + expect($doc)->toStartWith('{')->toEndWith('}'); + expect_any($doc, 'four', 'five'); + }); + test('outputs a document for multiple ordered results', function () { + $this->clearBuffer(); + Json::outputFirstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', [Field::named('id DESC')]); + expect($this->getBufferContents())->toStartWith('{"id": "four",')->toEndWith('}'); + }); + test('outputs "{}" when no documents match', function () { + $this->clearBuffer(); + Json::outputFirstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)'); + expect($this->getBufferContents())->toBe('{}'); + }); +}); diff --git a/tests/Integration/SQLite/CustomTest.php b/tests/Integration/SQLite/CustomTest.php index dd434cf..da824d9 100644 --- a/tests/Integration/SQLite/CustomTest.php +++ b/tests/Integration/SQLite/CustomTest.php @@ -76,20 +76,14 @@ describe('::jsonArray()', function () { describe('::outputJsonArray()', function () { test('outputs non-empty array when data found', function () { - ob_clean(); - ob_start(); + $this->clearBuffer(); Custom::outputJsonArray(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'sub' IS NOT NULL", []); - $json = ob_get_contents(); - ob_end_clean(); - expect($json)->toContain('[{', '},{', '}]'); + expect($this->getBufferContents())->toContain('[{', '},{', '}]'); }); test('outputs empty array when no data found', function () { - ob_clean(); - ob_start(); + $this->clearBuffer(); Custom::outputJsonArray(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'nothing' = '7'", []); - $json = ob_get_contents(); - ob_end_clean(); - expect($json)->toBe('[]'); + expect($this->getBufferContents())->toBe('[]'); }); }); diff --git a/tests/Integration/SQLiteIntegrationTest.php b/tests/Integration/SQLiteIntegrationTest.php index 7330f32..290c84f 100644 --- a/tests/Integration/SQLiteIntegrationTest.php +++ b/tests/Integration/SQLiteIntegrationTest.php @@ -11,13 +11,12 @@ namespace Test\Integration; use BitBadger\PDODocument\{Configuration, Custom, Delete, DocumentException, Field}; use BitBadger\PDODocument\Mapper\ExistsMapper; -use PHPUnit\Framework\TestCase; use Test\Integration\SQLite\ThrowawayDb; /** * Integration Test Class wrapper for SQLite integration tests */ -class SQLiteIntegrationTest extends TestCase +class SQLiteIntegrationTest extends DocumentTestCase { /** @var string Database name for throwaway database */ static private string $dbName = '';