Skip to content

Commit 4d481bb

Browse files
committed
Add readonly properties, fix deprecations, improve cov and CI
Mark Mapper::$db and Db::$connection as readonly. Fix 3 deprecation warnings by adding explicit typed properties to TestFetchingClassArgs for PDO FETCH_CLASS compatibility. Add 10 tests covering Db, Sql, and Mapper edge cases to reach 98.66% line coverage. Add composer coverage script. Add code-coverage CI job with Codecov upload and PHPCS to the static-analysis CI job.
1 parent 566d7a4 commit 4d481bb

8 files changed

Lines changed: 123 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,23 @@ jobs:
1717
- uses: ramsey/composer-install@v3
1818
- run: composer phpunit
1919

20+
code-coverage:
21+
name: Code Coverage
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v6
25+
- uses: shivammathur/setup-php@v2
26+
with:
27+
php-version: '8.5'
28+
coverage: pcov
29+
- uses: ramsey/composer-install@v3
30+
- name: Generate coverage report
31+
run: vendor/bin/phpunit --coverage-clover=coverage.xml
32+
- name: Upload to Codecov
33+
uses: codecov/codecov-action@v5
34+
with:
35+
token: ${{ secrets.CODECOV_TOKEN }}
36+
2037
static-analysis:
2138
name: Static Analysis
2239
runs-on: ubuntu-latest
@@ -26,4 +43,5 @@ jobs:
2643
with:
2744
php-version: '8.5'
2845
- uses: ramsey/composer-install@v3
46+
- run: composer phpcs
2947
- run: composer phpstan

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"phpcs": "vendor/bin/phpcs",
4646
"phpstan": "vendor/bin/phpstan analyze",
4747
"phpunit": "vendor/bin/phpunit",
48+
"coverage": "vendor/bin/phpunit --coverage-text",
4849
"qa": [
4950
"@phpcs",
5051
"@phpstan",

src/Db.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class Db
1919

2020
protected Sql $protoSql;
2121

22-
public function __construct(protected PDO $connection, Sql|null $sqlPrototype = null)
22+
public function __construct(protected readonly PDO $connection, Sql|null $sqlPrototype = null)
2323
{
2424
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
2525
$this->protoSql = $sqlPrototype ?: new Sql();

src/Mapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ final class Mapper extends AbstractMapper implements
4343
c\Mixable,
4444
c\Typable
4545
{
46-
protected Db $db;
46+
protected readonly Db $db;
4747

4848
public string $entityNamespace = '\\';
4949

tests/DbTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,33 @@ public function testRawSqlWithParams(): void
118118
$this->assertEquals(10, $line->testa);
119119
}
120120

121+
public function testExecReturnsTrueOnSuccess(): void
122+
{
123+
$result = $this->object->insertInto('unit', ['testa' => 40, 'testb' => 'jkl'])
124+
->values(['testa' => 40, 'testb' => 'jkl'])
125+
->exec();
126+
$this->assertTrue($result);
127+
}
128+
129+
public function testGetConnectionReturnsPdoInstance(): void
130+
{
131+
$connection = $this->object->getConnection();
132+
$this->assertInstanceOf(PDO::class, $connection);
133+
}
134+
135+
public function testFetchAllWithCallback(): void
136+
{
137+
$all = $this->object->select('*')->from('unit')->fetchAll(
138+
static function ($row) {
139+
$row->extra = 'callback';
140+
141+
return $row;
142+
},
143+
);
144+
$this->assertCount(3, $all);
145+
$this->assertEquals('callback', $all[0]->extra);
146+
}
147+
121148
protected function tearDown(): void
122149
{
123150
unset($this->object);

tests/MapperTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,45 @@ public function testShouldNotExecuteEntityConstructorWhenDisabled(): void
11441144
);
11451145
}
11461146

1147+
public function testFetchWithStringConditionUsingColumnExpression(): void
1148+
{
1149+
$mapper = $this->mapper;
1150+
$comments = $mapper->comment(['comment.id > 0'])->fetchAll();
1151+
$this->assertCount(2, $comments);
1152+
}
1153+
1154+
public function testPersistNewEntityWithNoAutoIncrementId(): void
1155+
{
1156+
$conn = $this->createStub(PDO::class);
1157+
$conn->method('getAttribute')
1158+
->willReturn('sqlite');
1159+
$stmt = $this->createStub(PDOStatement::class);
1160+
$stmt->method('execute')
1161+
->willReturn(true);
1162+
$conn->method('prepare')
1163+
->willReturn($stmt);
1164+
$conn->method('lastInsertId')
1165+
->willReturn('0');
1166+
$conn->method('beginTransaction')
1167+
->willReturn(true);
1168+
$conn->method('commit')
1169+
->willReturn(true);
1170+
$mapper = new Mapper($conn);
1171+
$obj = new stdClass();
1172+
$obj->id = null;
1173+
$obj->name = 'test';
1174+
$mapper->foo->persist($obj);
1175+
$mapper->flush();
1176+
$this->assertNull($obj->id);
1177+
}
1178+
1179+
public function testFetchReturnsDbInstance(): void
1180+
{
1181+
$db = new Db($this->conn);
1182+
$mapper = new Mapper($db);
1183+
$this->assertInstanceOf(Db::class, $mapper->getDb());
1184+
}
1185+
11471186
private function query(string $sql): PDOStatement
11481187
{
11491188
$stmt = $this->conn->query($sql);

tests/SqlTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,4 +507,34 @@ public function testInsertWithSelectSubquery(): void
507507
);
508508
$this->assertEquals(array_values($data), $this->object->getParams());
509509
}
510+
511+
public function testEncloseWithStringWrapsInParentheses(): void
512+
{
513+
$result = Sql::enclose('SELECT 1');
514+
$this->assertEquals('(SELECT 1) ', $result);
515+
}
516+
517+
public function testEncloseWithEmptyStringReturnsEmpty(): void
518+
{
519+
$result = Sql::enclose('');
520+
$this->assertEquals('', $result);
521+
}
522+
523+
public function testEncloseWithSqlObjectWrapsQuery(): void
524+
{
525+
$sql = new Sql('SELECT 1');
526+
$result = Sql::enclose($sql);
527+
$this->assertInstanceOf(Sql::class, $result);
528+
$this->assertEquals('(SELECT 1)', (string) $result);
529+
}
530+
531+
public function testBuildOperationWithLeadingUnderscore(): void
532+
{
533+
$sql = (string) $this->object->select('*')->from('table')
534+
->where('column > 1')->_innerSelect('f1')->from('t2')->_();
535+
$this->assertEquals(
536+
'SELECT * FROM table WHERE column > 1 (INNER SELECT f1 FROM t2)',
537+
$sql,
538+
);
539+
}
510540
}

tests/Stubs/TestFetchingClassArgs.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
class TestFetchingClassArgs
88
{
9+
public int|null $testa = null;
10+
11+
public string|null $testb = null;
12+
13+
public int|null $testez = null;
14+
915
public function __construct(public string|null $testd = null)
1016
{
1117
}

0 commit comments

Comments
 (0)