Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.

PHPORM-33 Add tests on Query\Builder methods #14

Merged
merged 5 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,10 @@ public function where($column, $operator = null, $value = null, $boolean = 'and'
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't comment on the if itself, but I'm wondering if the condition should read func_num_args() >= 3 - we wouldn't be removing the $ from an operator if $boolean was defined as well 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me how $boolean is used, but I see it's passed on to the parent method below. I think that's right, and this suggests another lingering bug.

I'll defer to you guys whether to fix here (if so, add a regression test that passes a fourth parameter to where() (I suppose an explicit 'and' string is fine) or report a new JIRA issue and address in a subsequent PR. A new issue probably makes more sense for visibility in the release notes.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #20


if (func_num_args() == 1 && is_string($column)) {
throw new \ArgumentCountError(sprintf('Too few arguments to function %s("%s"), 1 passed and at least 2 expected when the 1st is a string.', __METHOD__, $column));
}

return parent::where(...$params);
}

Expand Down Expand Up @@ -1378,4 +1382,28 @@ public function havingBetween($column, iterable $values, $boolean = 'and', $not
{
throw new \BadMethodCallException('This method is not supported by MongoDB');
}

/** @internal This method is not supported by MongoDB. */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good strategy to mark these as internal to have the IDE notify users already 👍

public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
{
throw new \BadMethodCallException('This method is not supported by MongoDB');
}

/** @internal This method is not supported by MongoDB. */
public function orWhereIntegerInRaw($column, $values)
{
throw new \BadMethodCallException('This method is not supported by MongoDB');
}

/** @internal This method is not supported by MongoDB. */
public function whereIntegerNotInRaw($column, $values, $boolean = 'and')
{
throw new \BadMethodCallException('This method is not supported by MongoDB');
}

/** @internal This method is not supported by MongoDB. */
public function orWhereIntegerNotInRaw($column, $values, $boolean = 'and')
{
throw new \BadMethodCallException('This method is not supported by MongoDB');
}
}
178 changes: 176 additions & 2 deletions tests/Query/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,36 @@ public static function provideQueryBuilderToMql(): iterable
*/
$date = new DateTimeImmutable('2016-07-12 15:30:00');

yield 'find' => [
yield 'select replaces previous select' => [
['find' => [[], ['projection' => ['bar' => 1]]]],
fn (Builder $builder) => $builder->select('foo')->select('bar'),
];

yield 'select array' => [
['find' => [[], ['projection' => ['foo' => 1, 'bar' => 1]]]],
fn (Builder $builder) => $builder->select(['foo', 'bar']),
];

/** @see DatabaseQueryBuilderTest::testAddingSelects */
yield 'addSelect' => [
['find' => [[], ['projection' => ['foo' => 1, 'bar' => 1, 'baz' => 1, 'boom' => 1]]]],
fn (Builder $builder) => $builder->select('foo')
->addSelect('bar')
->addSelect(['baz', 'boom'])
->addSelect('bar'),
];

yield 'select all' => [
['find' => [[], []]],
fn (Builder $builder) => $builder->select('*'),
];

yield 'find all with select' => [
['find' => [[], ['projection' => ['foo' => 1, 'bar' => 1]]]],
fn (Builder $builder) => $builder->select('foo', 'bar'),
];

yield 'find equals' => [
['find' => [['foo' => 'bar'], []]],
fn (Builder $builder) => $builder->where('foo', 'bar'),
];
Expand All @@ -66,11 +95,55 @@ public static function provideQueryBuilderToMql(): iterable
fn (Builder $builder) => $builder->where('foo', '>', $date),
];

yield 'find in array' => [
/** @see DatabaseQueryBuilderTest::testBasicWhereIns */
yield 'whereIn' => [
['find' => [['foo' => ['$in' => ['bar', 'baz']]], []]],
fn (Builder $builder) => $builder->whereIn('foo', ['bar', 'baz']),
];

// Nested array are not flattened like in the Eloquent builder. MongoDB can compare objects.
$array = [['issue' => 45582], ['id' => 2], [3]];
yield 'whereIn nested array' => [
['find' => [['id' => ['$in' => $array]], []]],
fn (Builder $builder) => $builder->whereIn('id', $array),
];

yield 'orWhereIn' => [
['find' => [
['$or' => [
['id' => 1],
['id' => ['$in' => [1, 2, 3]]],
]],
[], // options
]],
fn (Builder $builder) => $builder->where('id', '=', 1)
->orWhereIn('id', [1, 2, 3]),
];

/** @see DatabaseQueryBuilderTest::testBasicWhereNotIns */
yield 'whereNotIn' => [
['find' => [['id' => ['$nin' => [1, 2, 3]]], []]],
fn (Builder $builder) => $builder->whereNotIn('id', [1, 2, 3]),
];

yield 'orWhereNotIn' => [
['find' => [
['$or' => [
['id' => 1],
['id' => ['$nin' => [1, 2, 3]]],
]],
[], // options
]],
fn (Builder $builder) => $builder->where('id', '=', 1)
->orWhereNotIn('id', [1, 2, 3]),
];

/** @see DatabaseQueryBuilderTest::testEmptyWhereIns */
yield 'whereIn empty array' => [
['find' => [['id' => ['$in' => []]], []]],
fn (Builder $builder) => $builder->whereIn('id', []),
];

yield 'find limit offset select' => [
['find' => [[], ['limit' => 10, 'skip' => 5, 'projection' => ['foo' => 1, 'bar' => 1]]]],
fn (Builder $builder) => $builder->limit(10)->offset(5)->select('foo', 'bar'),
Expand Down Expand Up @@ -266,6 +339,43 @@ public static function provideQueryBuilderToMql(): iterable
fn (Builder $builder) => $builder->forPage(3, 20),
];

/** @see DatabaseQueryBuilderTest::testLimitsAndOffsets() */
yield 'offset limit' => [
['find' => [[], ['skip' => 5, 'limit' => 10]]],
fn (Builder $builder) => $builder->offset(5)->limit(10),
];

yield 'offset limit zero (unset)' => [
['find' => [[], []]],
fn (Builder $builder) => $builder
->offset(0)->limit(0),
];

yield 'offset limit zero (reset)' => [
['find' => [[], []]],
fn (Builder $builder) => $builder
->offset(5)->limit(10)
->offset(0)->limit(0),
];

yield 'offset limit negative (unset)' => [
['find' => [[], []]],
fn (Builder $builder) => $builder
->offset(-5)->limit(-10),
];

yield 'offset limit null (reset)' => [
['find' => [[], []]],
fn (Builder $builder) => $builder
->offset(5)->limit(10)
->offset(null)->limit(null),
];

yield 'skip take (aliases)' => [
['find' => [[], ['skip' => 5, 'limit' => 10]]],
fn (Builder $builder) => $builder->skip(5)->limit(10),
];

/** @see DatabaseQueryBuilderTest::testOrderBys() */
yield 'orderBy multiple columns' => [
['find' => [[], ['sort' => ['email' => 1, 'age' => -1]]]],
Expand Down Expand Up @@ -452,11 +562,57 @@ function (Builder $builder) {
->orWhereNotBetween('id', collect([3, 4])),
];

/** @see DatabaseQueryBuilderTest::testBasicSelectDistinct */
yield 'distinct' => [
['distinct' => ['foo', [], []]],
fn (Builder $builder) => $builder->distinct('foo'),
];

yield 'select distinct' => [
['distinct' => ['foo', [], []]],
fn (Builder $builder) => $builder->select('foo', 'bar')
->distinct(),
];

/** @see DatabaseQueryBuilderTest::testBasicSelectDistinctOnColumns */
yield 'select distinct on' => [
['distinct' => ['foo', [], []]],
fn (Builder $builder) => $builder->distinct('foo')
->select('foo', 'bar'),
];

/** @see DatabaseQueryBuilderTest::testLatest() */
yield 'latest' => [
['find' => [[], ['sort' => ['created_at' => -1]]]],
fn (Builder $builder) => $builder->latest(),
];

yield 'latest limit' => [
['find' => [[], ['sort' => ['created_at' => -1], 'limit' => 1]]],
fn (Builder $builder) => $builder->latest()->limit(1),
];

yield 'latest custom field' => [
['find' => [[], ['sort' => ['updated_at' => -1]]]],
fn (Builder $builder) => $builder->latest('updated_at'),
];

/** @see DatabaseQueryBuilderTest::testOldest() */
yield 'oldest' => [
['find' => [[], ['sort' => ['created_at' => 1]]]],
fn (Builder $builder) => $builder->oldest(),
];

yield 'oldest limit' => [
['find' => [[], ['sort' => ['created_at' => 1], 'limit' => 1]]],
fn (Builder $builder) => $builder->oldest()->limit(1),
];

yield 'oldest custom field' => [
['find' => [[], ['sort' => ['updated_at' => 1]]]],
fn (Builder $builder) => $builder->oldest('updated_at'),
];

yield 'groupBy' => [
['aggregate' => [
[['$group' => ['_id' => ['foo' => '$foo'], 'foo' => ['$last' => '$foo']]]],
Expand Down Expand Up @@ -516,6 +672,12 @@ public static function provideExceptions(): iterable
'Between $values must be a list with exactly two elements: [min, max]',
fn (Builder $builder) => $builder->whereBetween('id', ['min' => 1, 'max' => 2]),
];

yield 'find with single string argument' => [
\ArgumentCountError::class,
'Too few arguments to function Jenssegers\Mongodb\Query\Builder::where("foo"), 1 passed and at least 2 expected when the 1st is a string',
fn (Builder $builder) => $builder->where('foo'),
];
}

/** @dataProvider getEloquentMethodsNotSupported */
Expand Down Expand Up @@ -562,6 +724,18 @@ public static function getEloquentMethodsNotSupported()
yield 'having' => [fn (Builder $builder) => $builder->having('baz', '=', 1)];
yield 'havingBetween' => [fn (Builder $builder) => $builder->havingBetween('last_login_date', ['2018-11-16', '2018-12-16'])];
yield 'orHavingRaw' => [fn (Builder $builder) => $builder->orHavingRaw('user_foo < user_bar')];

/** @see DatabaseQueryBuilderTest::testWhereIntegerInRaw */
yield 'whereIntegerInRaw' => [fn (Builder $builder) => $builder->whereIntegerInRaw('id', ['1a', 2])];

/** @see DatabaseQueryBuilderTest::testOrWhereIntegerInRaw */
yield 'orWhereIntegerInRaw' => [fn (Builder $builder) => $builder->orWhereIntegerInRaw('id', ['1a', 2])];

/** @see DatabaseQueryBuilderTest::testWhereIntegerNotInRaw */
yield 'whereIntegerNotInRaw' => [fn (Builder $builder) => $builder->whereIntegerNotInRaw('id', ['1a', 2])];

/** @see DatabaseQueryBuilderTest::testOrWhereIntegerNotInRaw */
yield 'orWhereIntegerNotInRaw' => [fn (Builder $builder) => $builder->orWhereIntegerNotInRaw('id', ['1a', 2])];
}

private static function getBuilder(): Builder
Expand Down
2 changes: 1 addition & 1 deletion tests/TransactionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ public function testTransaction(): void
$count = User::count();
$this->assertEquals(2, $count);

$this->assertTrue(User::where('alcaeus')->exists());
$this->assertTrue(User::where('name', 'alcaeus')->exists());
Copy link
Owner Author

@GromNaN GromNaN Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being too permissive didn't allow to detect this incorrect query.

- { "alcaeus": null }
+ { "name": "alcaeus" }

The new exception made it surface.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to stop using my name in tests. It makes blaming me for my mistakes too easy 😅

$this->assertTrue(User::where(['name' => 'klinson'])->where('age', 21)->exists());
}

Expand Down