Skip to content
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
162 changes: 162 additions & 0 deletions spec/Builder/Binding.spec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

use BlitzPHP\Database\Builder\BaseBuilder;
use BlitzPHP\Database\Builder\BindingCollection;
use BlitzPHP\Database\Spec\Mock\MockConnection;

use function Kahlan\expect;

describe("Database / Query Builder : Bindings", function() {

beforeEach(function() {
$this->builder = new BaseBuilder(new MockConnection([]));
});

describe("BindingCollection simple", function() {
it(": BindingCollection ajout simple", function() {
$collection = new BindingCollection();
$collection->add('value1');
$collection->add(123);
$collection->add(true);
$collection->add(null);

expect($collection->count())->toBe(4);
expect($collection->getOrdered())->toBe(['value1', 123, true, null]);
});

it(": BindingCollection ajout nommé", function() {
$collection = new BindingCollection();
$collection->addNamed(':name', 'John');
$collection->addNamed(':age', 30);

expect($collection->get('where', ':name'))->toBe('John');
expect($collection->get('where', ':age'))->toBe(30);
});

it(": BindingCollection types", function() {
$collection = new BindingCollection();
$collection->add('string');
$collection->add(123);
$collection->add(true);
$collection->add(null);

$types = $collection->getTypesOrdered();
expect($types[0])->toBe(PDO::PARAM_STR);
expect($types[1])->toBe(PDO::PARAM_INT);
expect($types[2])->toBe(PDO::PARAM_BOOL);
expect($types[3])->toBe(PDO::PARAM_NULL);
});

it(": BindingCollection merge", function() {
$col1 = new BindingCollection();
$col1->add('a')->add('b');

$col2 = new BindingCollection();
$col2->add('c')->add('d');

$col1->merge($col2);

expect($col1->count())->toBe(4);
expect($col1->getOrdered())->toBe(['a', 'b', 'c', 'd']);
});

it(": BindingCollection clear", function() {
$collection = new BindingCollection();
$collection->add('test');
expect($collection->isEmpty())->toBe(false);

$collection->clear();
expect($collection->isEmpty())->toBe(true);
});

it(": Les bindings sont correctement transmis dans la requête", function() {
$builder = $this->builder->testMode()
->from('users')
->where('id', 5)
->where('name', 'John')
->whereIn('status', [1, 2, 3]);

expect($builder->bindings->count())->toBe(5);
expect($builder->bindings->getOrdered())->toBe([5, 'John', 1, 2, 3]);
});

it(": Les bindings sont réinitialisés après exécution", function() {
$builder = $this->builder->from('users')->where('id', 5);

expect($builder->bindings->isEmpty())->toBe(false);

try {
$sql = $builder->get();
} catch(Exception) {
// l'execution ne passera pas car on a pas de bd.
// on veut juste se rassuer que les bindings sont reset
expect($builder->bindings->isEmpty())->toBe(true);
}
});

it(": Les bindings sont préservés dans les sous-requêtes", function() {
$builder = $this->builder->testMode()
->from('users')
->whereIn('id', function($q) {
$q->from('profiles')
->select('user_id')
->where('active', 1)
->where('points >', 100);
});

expect($builder->bindings->count())->toBe(2);
expect($builder->bindings->getOrdered())->toBe([1, 100]);
});
});

describe("BindingCollection avec types", function() {
it(": getOrdered avec types spécifiques", function() {
$bindings = new BindingCollection();
$bindings->add('value1', 'values');
$bindings->add(5, 'where');
$bindings->add('join_cond', 'join');

expect($bindings->getOrdered(['values', 'where']))->toBe(['value1', 5]);
expect($bindings->getOrdered(['where', 'values']))->toBe([5, 'value1']);
expect($bindings->getOrdered())->toHaveLength(3);
});

it(": getOrdered ignore les types vides", function() {
$bindings = new BindingCollection();
$bindings->add('value1', 'values');

expect($bindings->getOrdered(['values', 'where', 'having']))->toBe(['value1']);
});
});

describe("BaseBuilder::getBindings", function() {
it(": UPDATE - valeurs avant where", function() {
$builder = $this->builder->table('users')
->where('id', 5)
->where('active', 1)
->set(['name' => 'John'])
->pending() // pour eviter l'execution
->update();

expect($builder->getBindings())->toBe(['John', 5, 1]);
});

it(": INSERT - seulement valeurs", function() {
$builder = $this->builder->table('users')
->set(['name' => 'John', 'age' => 30])
->pending() // pour eviter l'execution
->insert();

expect($builder->getBindings())->toBe(['John', 30]);
});

it(": SELECT - where dans l'ordre", function() {
$builder = $this->builder->table('users')
->where('id', 5)
->where('name', 'John')
->orderBy('created_at');

expect($builder->getBindings())->toBe([5, 'John']);
});
});
});
12 changes: 7 additions & 5 deletions spec/Builder/Count.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,36 @@
it(": Nombre de ligne avec condition", function() {
$builder = $this->builder->testMode()->from('jobs j')->where('id >', 3);

expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM jobs AS j WHERE id > 3');
expect($builder->getBindings())->toBe([3]);
expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM jobs AS j WHERE id > ?');
});

it(": Nombre de ligne avec regroupement", function() {
$builder = $this->builder->testMode()->from('jobs j')->where('id >', 3)->groupBy('id');

expect($builder->bindings->getValues())->toBe([3]);
expect($builder->getBindings())->toBe([3]);
expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM (SELECT * FROM jobs AS j WHERE id > ? GROUP BY id) AS count_table');
});

it(": Compter tous les résultats avec GroupBy et le prefixe de la base de données", function() {
$this->builder->db()->setPrefix('db_');
$builder = $this->builder->testMode()->select('j.*')->from('jobs j')->where('id >', 3)->groupBy('id');

expect($builder->bindings->getValues())->toBe([3]);
expect($builder->getBindings())->toBe([3]);
expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM (SELECT j.* FROM db_jobs AS j WHERE id > ? GROUP BY id) AS count_table');
});

it(": Compter tous les résultats avec GroupBy et Having", function() {
$builder = $this->builder->testMode()->from('jobs j')->where('id >', 3)->groupBy('id')->having('1=1');

expect($builder->bindings->getValues())->toBe([3, 1]);
expect($builder->getBindings())->toBe([3, 1]);
expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM (SELECT * FROM jobs AS j WHERE id > ? GROUP BY id HAVING 1 = ?) AS count_table');
});

it(": Compter tous les résultats avec Having uniquement", function() {
$builder = $this->builder->testMode()->from('jobs j')->where('id >', 3)->having('1=1');

expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM jobs AS j WHERE id > 3 HAVING 1 = 1');
expect($builder->getBindings())->toBe([3, 1]);
expect($builder->count())->toBe('SELECT COUNT(*) AS count_value FROM jobs AS j WHERE id > ? HAVING 1 = ?');
});
});
2 changes: 1 addition & 1 deletion spec/Builder/Delete.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

it(": Suppression avec limite", function() {
$builder = $this->builder->testMode()->from('jobs')->where('id', 1)->limit(10);
expect($builder->bindings->getValues())->toBe([1]);
expect($builder->getBindings())->toBe([1]);
expect($builder->delete())->toBe('DELETE FROM jobs WHERE id = 1 LIMIT 10');
});
});
66 changes: 66 additions & 0 deletions spec/Builder/Json.spec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

use BlitzPHP\Database\Builder\BaseBuilder;
use BlitzPHP\Database\Spec\Mock\MockConnection;

describe("Database / Query Builder : JSON", function() {

beforeEach(function() {
$this->builder = new BaseBuilder(new MockConnection([]));
});

it(": whereJsonContains", function() {
$builder = $this->builder->testMode()
->from('users')
->whereJsonContains('preferences->languages', 'fr');

expect($builder->toSql())->toBe(
"SELECT * FROM users WHERE JSON_CONTAINS(preferences->languages, ?)"
);
expect($builder->getBindings())->toBe(['fr']);
});

it(": whereJsonDoesntContain", function() {
$builder = $this->builder->testMode()
->from('users')
->whereJsonDoesntContain('preferences->tags', 'premium');

expect($builder->toSql())->toBe(
"SELECT * FROM users WHERE NOT JSON_CONTAINS(preferences->tags, ?)"
);
expect($builder->getBindings())->toBe(['premium']);
});

it(": whereJsonContainsKey", function() {
$builder = $this->builder->testMode()
->from('users')
->whereJsonContainsKey('settings->notifications');

expect($builder->sql())->toBe(
"SELECT * FROM users WHERE JSON_CONTAINS_PATH(settings->notifications, 'one', ?) = 1"
);
});

it(": whereJsonLength", function() {
$builder = $this->builder->testMode()
->from('users')
->whereJsonLength('preferences->items', '>', 5);

expect($builder->toSql())->toBe(
"SELECT * FROM users WHERE JSON_LENGTH(preferences->items) > ?"
);
expect($builder->getBindings())->toBe([5]);
});

it(": orWhereJsonContains", function() {
$builder = $this->builder->testMode()
->from('users')
->where('active', 1)
->orWhereJsonContains('preferences->languages', 'en');

expect($builder->toSql())->toBe(
"SELECT * FROM users WHERE active = ? OR JSON_CONTAINS(preferences->languages, ?)"
);
expect($builder->getBindings())->toBe([1, 'en']);
});
});
99 changes: 99 additions & 0 deletions spec/Builder/Union.spec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

use BlitzPHP\Database\Builder\BaseBuilder;
use BlitzPHP\Database\Spec\Mock\MockConnection;

describe("Database / Query Builder : UNION", function() {

beforeEach(function() {
$this->builder = new BaseBuilder(new MockConnection([]));
});

it(": UNION simple", function() {
$builder = $this->builder->testMode()
->from('users')
->select('name, email')
->where('active', 1)
->union(function($q) {
$q->from('deleted_users')
->select('name, email')
->where('restored', 0);
});

expect($builder->toSql())->toBe(
"SELECT name, email FROM users WHERE active = ? " .
"UNION SELECT name, email FROM deleted_users WHERE restored = ?"
);
expect($builder->getBindings())->toBe([1, 0]);
});

it(": UNION ALL", function() {
$builder = $this->builder->testMode()
->from('orders_2023')
->select('id, total')
->unionAll(function($q) {
$q->from('orders_2024')
->select('id, total');
});

expect($builder->sql())->toBe(
"SELECT id, total FROM orders_2023 " .
"UNION ALL SELECT id, total FROM orders_2024"
);
});

it(": UNION multiples", function() {
$builder = $this->builder->testMode()
->from('q1')
->select('data')
->union(function($q) { $q->from('q2')->select('data'); })
->union(function($q) { $q->from('q3')->select('data'); });

expect($builder->sql())->toBe(
"SELECT data FROM q1 " .
"UNION SELECT data FROM q2 " .
"UNION SELECT data FROM q3"
);
});

it(": UNION avec ORDER BY et LIMIT", function() {
$builder = $this->builder->testMode()
->from('products')
->select('name, price')
->union(function($q) {
$q->from('archived_products')
->select('name, price');
})
->orderBy('price', 'DESC')
->limit(10);

expect($builder->sql())->toBe(
"SELECT name, price FROM products " .
"UNION SELECT name, price FROM archived_products " .
"ORDER BY price DESC LIMIT 10"
);
});

it(": UNION avec sous-requête complexe", function() {
$subquery = (new BaseBuilder(new MockConnection([])))
->from('logs')
->select('user_id, COUNT(*) as count')
->groupBy('user_id')
->having('count >', 5);

$builder = $this->builder->testMode()
->from('users')
->select('id, name')
->union(function($q) use ($subquery) {
$q->fromSubquery($subquery, 'active_logs')
->select('user_id as id, count as name');
});

expect($builder->toSql())->toBe(
"SELECT id, name FROM users " .
"UNION SELECT user_id AS id, count AS name FROM " .
"(SELECT user_id, COUNT(*) as count FROM logs GROUP BY user_id HAVING count > ?) AS active_logs"
);
expect($builder->getBindings())->toBe([5]);
});
});
Loading