Skip to content

Commit a5405ac

Browse files
test(support): add db() helper tests with FakeContainer, FakeConnection and FakeDatabaseManager
1 parent fac11d3 commit a5405ac

10 files changed

Lines changed: 522 additions & 1 deletion

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
All significant changes to this project will be documented in this file.
44

5+
## [1.2.0] – 2025-11-24
6+
7+
### Added
8+
9+
- Added unit tests for the global `db()` helper, including verification of passing a connection name and returning a `Connection` instance.
10+
- Added the `FakeConnection` and `FakeDatabaseManager` classes to tests to isolate behavior and enable stable testing without a real database.
11+
- Added the `reset()` method to `SupportFakeContainer` to clean up the container between tests.
12+
- Improved testing infrastructure: tests are now fully isolated and independent of the Annabel framework.
13+
514
## [1.1.0] – 2025-11-16
615

716
### Added

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"codemonster-ru/env": "^2.0",
2121
"codemonster-ru/router": "^2.0",
2222
"codemonster-ru/session": "^1.0",
23-
"codemonster-ru/dumper": "^1.0"
23+
"codemonster-ru/dumper": "^1.0",
24+
"codemonster-ru/database": "^1.0"
2425
},
2526
"require-dev": {
2627
"phpunit/phpunit": "^9.6 || ^10.5 || ^11.0 || ^12.0",
@@ -32,6 +33,7 @@
3233
},
3334
"files": [
3435
"src/helpers/config.php",
36+
"src/helpers/db.php",
3537
"src/helpers/env.php",
3638
"src/helpers/view.php",
3739
"src/helpers/router.php",

src/helpers/db.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
use Codemonster\Database\DatabaseManager;
4+
5+
if (!function_exists('db')) {
6+
function db(?string $name = null)
7+
{
8+
return app(DatabaseManager::class)->connection($name);
9+
}
10+
}

tests/Fakes/FakeConnection.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
namespace Tests\Fakes;
4+
5+
use Codemonster\Database\Contracts\ConnectionInterface;
6+
use Codemonster\Database\Exceptions\QueryException;
7+
use Codemonster\Database\Query\QueryBuilder;
8+
use PDO;
9+
10+
class FakeConnection implements ConnectionInterface
11+
{
12+
public ?string $lastSql = null;
13+
public array $lastBindings = [];
14+
15+
public array $selectResult = [];
16+
public ?array $selectOneResult = null;
17+
18+
public array $queries = [];
19+
20+
public function select(string $query, array $params = []): array
21+
{
22+
$this->lastSql = $query;
23+
$this->lastBindings = $params;
24+
25+
return $this->selectResult;
26+
}
27+
28+
public function selectOne(string $query, array $params = []): ?array
29+
{
30+
$this->lastSql = $query;
31+
$this->lastBindings = $params;
32+
33+
return $this->selectOneResult;
34+
}
35+
36+
public function insert(string $query, array $params = []): bool
37+
{
38+
throw new QueryException('Not implemented in FakeConnection::insert');
39+
}
40+
41+
public function update(string $query, array $params = []): int
42+
{
43+
throw new QueryException('Not implemented in FakeConnection::update');
44+
}
45+
46+
public function delete(string $query, array $params = []): int
47+
{
48+
throw new QueryException('Not implemented in FakeConnection::delete');
49+
}
50+
51+
public function statement(string $query, array $params = []): bool
52+
{
53+
throw new QueryException('Not implemented in FakeConnection::statement');
54+
}
55+
56+
public function getPdo(): PDO
57+
{
58+
return new PDO('sqlite::memory:');
59+
}
60+
61+
public function table(string $table): QueryBuilder
62+
{
63+
return new QueryBuilder($this, $table);
64+
}
65+
66+
public function beginTransaction(): bool
67+
{
68+
$this->queries[] = ['beginTransaction'];
69+
70+
return true;
71+
}
72+
73+
public function commit(): bool
74+
{
75+
$this->queries[] = ['commit'];
76+
77+
return true;
78+
}
79+
80+
public function rollBack(): bool
81+
{
82+
$this->queries[] = ['rollBack'];
83+
84+
return true;
85+
}
86+
87+
public function transaction(callable $callback): mixed
88+
{
89+
$this->beginTransaction();
90+
91+
try {
92+
$result = $callback($this);
93+
94+
$this->commit();
95+
96+
return $result;
97+
} catch (\Throwable $e) {
98+
$this->rollBack();
99+
100+
throw $e;
101+
}
102+
}
103+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Tests\Fakes;
4+
5+
use Codemonster\Database\DatabaseManager;
6+
use Codemonster\Database\Contracts\ConnectionInterface;
7+
8+
class FakeDatabaseManager extends DatabaseManager
9+
{
10+
protected ConnectionInterface $connection;
11+
12+
public function __construct(ConnectionInterface $connection)
13+
{
14+
parent::__construct([
15+
'default' => 'fake',
16+
'connections' => [],
17+
]);
18+
19+
$this->connection = $connection;
20+
}
21+
22+
public function connection(?string $name = null): ConnectionInterface
23+
{
24+
return $this->connection;
25+
}
26+
}

tests/Helpers/DbHelperTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Tests\Helpers;
4+
5+
use Codemonster\Database\DatabaseManager;
6+
use PHPUnit\Framework\TestCase;
7+
use Tests\SupportFakeContainer;
8+
use Tests\Fakes\FakeConnection;
9+
use Tests\Fakes\FakeDatabaseManager;
10+
11+
class DbHelperTest extends TestCase
12+
{
13+
protected SupportFakeContainer $container;
14+
15+
protected function setUp(): void
16+
{
17+
$this->container = app();
18+
$this->container->reset();
19+
}
20+
21+
public function test_db_returns_connection()
22+
{
23+
$connection = new FakeConnection();
24+
25+
$manager = new FakeDatabaseManager($connection);
26+
27+
$this->container->singleton(DatabaseManager::class, fn() => $manager);
28+
29+
$db = db();
30+
31+
$this->assertSame(
32+
$connection,
33+
$db,
34+
'db() must return a FakeConnection instance from FakeDatabaseManager'
35+
);
36+
}
37+
38+
public function test_db_passes_connection_name()
39+
{
40+
$connection = new FakeConnection();
41+
42+
$manager = new class($connection) extends FakeDatabaseManager {
43+
public ?string $lastName = null;
44+
45+
public function connection(?string $name = null): \Codemonster\Database\Contracts\ConnectionInterface
46+
{
47+
$this->lastName = $name;
48+
49+
return $this->connection;
50+
}
51+
};
52+
53+
$this->container->singleton(DatabaseManager::class, fn() => $manager);
54+
55+
$db = db('secondary');
56+
57+
$this->assertSame('secondary', $manager->lastName);
58+
$this->assertSame($connection, $db);
59+
}
60+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Tests\Query;
4+
5+
use Codemonster\Database\Query\QueryBuilder;
6+
use PHPUnit\Framework\TestCase;
7+
use Tests\Fakes\FakeConnection;
8+
9+
class QueryBuilderCrudTest extends TestCase
10+
{
11+
protected FakeConnection $connection;
12+
13+
protected function setUp(): void
14+
{
15+
$this->connection = new FakeConnection();
16+
}
17+
18+
/* -----------------------------------------------------------------
19+
| INSERT
20+
| -----------------------------------------------------------------
21+
*/
22+
23+
public function testInsertGeneratesCorrectSqlAndBindings()
24+
{
25+
$builder = new QueryBuilder($this->connection, 'users');
26+
27+
$result = $builder->insert([
28+
'name' => 'Vasya',
29+
'email' => 'k@example.com',
30+
]);
31+
32+
$this->assertTrue($result);
33+
$this->assertSame(
34+
'INSERT INTO `users` (`name`, `email`) VALUES (?, ?)',
35+
$this->connection->lastSql
36+
);
37+
$this->assertSame(
38+
['Vasya', 'k@example.com'],
39+
$this->connection->lastBindings
40+
);
41+
}
42+
43+
public function testInsertGetIdReturnsId()
44+
{
45+
$builder = new QueryBuilder($this->connection, 'ideas');
46+
47+
$this->connection->getPdo()->exec('CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)');
48+
49+
$id = $builder->insertGetId(['title' => 'New idea']);
50+
51+
$this->assertSame(
52+
'INSERT INTO `ideas` (`title`) VALUES (?)',
53+
$this->connection->lastSql
54+
);
55+
$this->assertSame(['New idea'], $this->connection->lastBindings);
56+
$this->assertTrue(is_int($id), 'insertGetId must return a number (ID)');
57+
}
58+
59+
/* -----------------------------------------------------------------
60+
| UPDATE
61+
| -----------------------------------------------------------------
62+
*/
63+
64+
public function testUpdateGeneratesSqlAndBindings()
65+
{
66+
$builder = new QueryBuilder($this->connection, 'users');
67+
68+
$updated = $builder
69+
->where('id', 10)
70+
->update([
71+
'name' => 'Updated',
72+
'active' => 0,
73+
]);
74+
75+
$this->assertSame(1, $updated);
76+
$this->assertSame(
77+
'UPDATE `users` SET `name` = ?, `active` = ? WHERE `id` = ?',
78+
$this->connection->lastSql
79+
);
80+
$this->assertSame(
81+
['Updated', 0, 10],
82+
$this->connection->lastBindings
83+
);
84+
}
85+
86+
/* -----------------------------------------------------------------
87+
| DELETE
88+
| -----------------------------------------------------------------
89+
*/
90+
91+
public function testDeleteGeneratesSqlAndBindings()
92+
{
93+
$builder = new QueryBuilder($this->connection, 'logs');
94+
95+
$deleted = $builder
96+
->where('id', 5)
97+
->delete();
98+
99+
$this->assertSame(1, $deleted);
100+
$this->assertSame(
101+
'DELETE FROM `logs` WHERE `id` = ?',
102+
$this->connection->lastSql
103+
);
104+
$this->assertSame([5], $this->connection->lastBindings);
105+
}
106+
}

0 commit comments

Comments
 (0)