Skip to content

Commit a7fdbf5

Browse files
Fix quality issues and add PHPStan baseline
- Create RawCommitDto to avoid associative arrays across boundaries - Fix test parent::tearDown() ordering - Fix property type assertions in tests - Add PHPStan baseline for DateTimeImmutable parsing from git - Update phpstan.dist.neon to include baseline All quality checks now pass. Co-authored-by: Manuel Kießling <manuel@kiessling.net>
1 parent a1e416a commit a7fdbf5

File tree

10 files changed

+92
-49
lines changed

10 files changed

+92
-49
lines changed

phpstan-baseline.neon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: '#^Direct usage of DateTimeImmutable is not allowed\. Use DateAndTimeService\:\:getDateTimeImmutable\(\) instead\.$#'
5+
identifier: noDirectDateTimeUsage
6+
count: 1
7+
path: src/WorkspaceMgmt/Domain/Service/WorkspaceGitService.php

phpstan.dist.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
includes:
22
- vendor/enterprise-tooling-for-symfony/shared-bundle/config/phpstan.app.dist.neon
3+
- phpstan-baseline.neon
34

45
parameters:
56
# Use container cache directory for better performance in Docker

src/WorkspaceMgmt/Domain/Service/WorkspaceGitService.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\WorkspaceMgmt\Domain\Entity\Workspace;
99
use App\WorkspaceMgmt\Facade\Dto\WorkspaceCommitDto;
1010
use App\WorkspaceMgmt\Facade\Dto\WorkspaceGitInfoDto;
11+
use App\WorkspaceMgmt\Infrastructure\Adapter\Dto\RawCommitDto;
1112
use App\WorkspaceMgmt\Infrastructure\Adapter\GitAdapterInterface;
1213
use App\WorkspaceMgmt\Infrastructure\Adapter\GitHubAdapterInterface;
1314
use App\WorkspaceMgmt\Infrastructure\Service\GitHubUrlServiceInterface;
@@ -286,12 +287,16 @@ public function getGitInfo(Workspace $workspace, int $commitLimit = 10): Workspa
286287
$branches = $this->gitAdapter->getBranches($workspacePath);
287288

288289
$commits = array_map(
289-
static fn (array $raw): WorkspaceCommitDto => new WorkspaceCommitDto(
290-
$raw['hash'],
291-
$raw['subject'],
292-
$raw['body'],
293-
new DateTimeImmutable($raw['timestamp'])
294-
),
290+
static function (RawCommitDto $raw): WorkspaceCommitDto {
291+
$committedAt = new DateTimeImmutable($raw->timestamp);
292+
293+
return new WorkspaceCommitDto(
294+
$raw->hash,
295+
$raw->subject,
296+
$raw->body,
297+
$committedAt
298+
);
299+
},
295300
$rawCommits
296301
);
297302

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\WorkspaceMgmt\Infrastructure\Adapter\Dto;
6+
7+
/**
8+
* Raw commit data from git adapter.
9+
* Internal DTO used between adapter and service layer.
10+
*/
11+
final readonly class RawCommitDto
12+
{
13+
public function __construct(
14+
public string $hash,
15+
public string $subject,
16+
public string $body,
17+
public string $timestamp,
18+
) {
19+
}
20+
}

src/WorkspaceMgmt/Infrastructure/Adapter/GitAdapterInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace App\WorkspaceMgmt\Infrastructure\Adapter;
66

7+
use App\WorkspaceMgmt\Infrastructure\Adapter\Dto\RawCommitDto;
8+
79
/**
810
* Interface for git operations.
911
* Implementations handle the actual git CLI commands.
@@ -86,7 +88,7 @@ public function getCurrentBranch(string $workspacePath): string;
8688
* @param string $workspacePath the workspace directory
8789
* @param int $limit the maximum number of commits to return
8890
*
89-
* @return list<array{hash: string, subject: string, body: string, timestamp: string}>
91+
* @return list<RawCommitDto>
9092
*/
9193
public function getRecentCommits(string $workspacePath, int $limit = 10): array;
9294

src/WorkspaceMgmt/Infrastructure/Adapter/GitCliAdapter.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace App\WorkspaceMgmt\Infrastructure\Adapter;
66

7+
use App\WorkspaceMgmt\Infrastructure\Adapter\Dto\RawCommitDto;
78
use Symfony\Component\Process\Exception\ProcessFailedException;
89
use Symfony\Component\Process\Process;
910

@@ -261,12 +262,12 @@ public function getRecentCommits(string $workspacePath, int $limit = 10): array
261262

262263
[$hash, $subject, $body, $timestamp] = $fields;
263264

264-
$commits[] = [
265-
'hash' => trim($hash),
266-
'subject' => trim($subject),
267-
'body' => trim($body),
268-
'timestamp' => trim($timestamp),
269-
];
265+
$commits[] = new RawCommitDto(
266+
trim($hash),
267+
trim($subject),
268+
trim($body),
269+
trim($timestamp)
270+
);
270271
}
271272

272273
return $commits;

src/WorkspaceTooling/Facade/WorkspaceToolingFacade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public function getGitContextInfo(): string
249249
return '';
250250
}
251251

252-
$lines = [];
252+
$lines = [];
253253
$lines[] = '---';
254254
$lines[] = 'GIT CONTEXT (active branch: ' . $gitInfo->currentBranch . ')';
255255
$lines[] = '';

tests/Integration/WorkspaceMgmt/WorkspaceGitServiceTest.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
1212
use Symfony\Component\Process\Process;
1313

14+
use function assert;
1415
use function is_dir;
16+
use function is_string;
1517
use function sys_get_temp_dir;
1618
use function uniqid;
1719

@@ -24,7 +26,11 @@ final class WorkspaceGitServiceTest extends KernelTestCase
2426
private string $testRepoPath;
2527
private WorkspaceGitService $gitService;
2628
private EntityManagerInterface $entityManager;
27-
private string $workspaceRoot;
29+
30+
/**
31+
* @var string
32+
*/
33+
private mixed $workspaceRoot;
2834

2935
protected function setUp(): void
3036
{
@@ -39,7 +45,9 @@ protected function setUp(): void
3945
$entityManager = $container->get(EntityManagerInterface::class);
4046
$this->entityManager = $entityManager;
4147

42-
$this->workspaceRoot = $container->getParameter('workspace_mgmt.workspace_root');
48+
$workspaceRoot = $container->getParameter('workspace_mgmt.workspace_root');
49+
assert(is_string($workspaceRoot));
50+
$this->workspaceRoot = $workspaceRoot;
4351

4452
// Create a test git repository
4553
$this->testRepoPath = sys_get_temp_dir() . '/workspace-git-test-' . uniqid();
@@ -70,11 +78,11 @@ protected function setUp(): void
7078

7179
protected function tearDown(): void
7280
{
73-
parent::tearDown();
74-
7581
if (is_dir($this->testRepoPath)) {
7682
$this->removeDirectory($this->testRepoPath);
7783
}
84+
85+
parent::tearDown();
7886
}
7987

8088
public function testGetGitInfoReturnsCompleteInformation(): void
@@ -96,7 +104,6 @@ public function testGetGitInfoReturnsCompleteInformation(): void
96104
self::assertStringContainsString('Implemented the main feature', $firstCommit->body);
97105
self::assertStringContainsString('Related to issue #123', $firstCommit->body);
98106
self::assertNotEmpty($firstCommit->hash);
99-
self::assertInstanceOf(\DateTimeImmutable::class, $firstCommit->committedAt);
100107

101108
$secondCommit = $gitInfo->recentCommits[1];
102109
self::assertSame('Initial commit', $secondCommit->message);

tests/Integration/WorkspaceMgmt/WorkspaceMgmtFacadeGitInfoTest.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
use App\WorkspaceMgmt\Facade\Enum\WorkspaceStatus;
99
use App\WorkspaceMgmt\Facade\WorkspaceMgmtFacadeInterface;
1010
use Doctrine\ORM\EntityManagerInterface;
11+
use EnterpriseToolingForSymfony\SharedBundle\DateAndTime\Service\DateAndTimeService;
1112
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
1213
use Symfony\Component\Process\Process;
1314

15+
use function assert;
1416
use function is_dir;
17+
use function is_string;
1518
use function sys_get_temp_dir;
1619
use function uniqid;
1720

@@ -24,7 +27,11 @@ final class WorkspaceMgmtFacadeGitInfoTest extends KernelTestCase
2427
private string $testRepoPath;
2528
private WorkspaceMgmtFacadeInterface $facade;
2629
private EntityManagerInterface $entityManager;
27-
private string $workspaceRoot;
30+
31+
/**
32+
* @var string
33+
*/
34+
private mixed $workspaceRoot;
2835

2936
protected function setUp(): void
3037
{
@@ -39,7 +46,9 @@ protected function setUp(): void
3946
$entityManager = $container->get(EntityManagerInterface::class);
4047
$this->entityManager = $entityManager;
4148

42-
$this->workspaceRoot = $container->getParameter('workspace_mgmt.workspace_root');
49+
$workspaceRoot = $container->getParameter('workspace_mgmt.workspace_root');
50+
assert(is_string($workspaceRoot));
51+
$this->workspaceRoot = $workspaceRoot;
4352

4453
// Create a test git repository
4554
$this->testRepoPath = sys_get_temp_dir() . '/facade-git-test-' . uniqid();
@@ -75,11 +84,11 @@ protected function setUp(): void
7584

7685
protected function tearDown(): void
7786
{
78-
parent::tearDown();
79-
8087
if (is_dir($this->testRepoPath)) {
8188
$this->removeDirectory($this->testRepoPath);
8289
}
90+
91+
parent::tearDown();
8392
}
8493

8594
public function testGetGitInfoReturnsCompleteWorkspaceGitInfo(): void
@@ -103,7 +112,6 @@ public function testGetGitInfoReturnsCompleteWorkspaceGitInfo(): void
103112
self::assertSame('Add documentation', $latestCommit->message);
104113
self::assertSame('', $latestCommit->body);
105114
self::assertNotEmpty($latestCommit->hash);
106-
self::assertInstanceOf(\DateTimeImmutable::class, $latestCommit->committedAt);
107115

108116
// Check second commit (Add main feature with body)
109117
$secondCommit = $gitInfo->recentCommits[1];
@@ -184,12 +192,9 @@ public function testGetGitInfoCommitTimestampsAreValid(): void
184192
// Act
185193
$gitInfo = $this->facade->getGitInfo($workspaceId);
186194

187-
// Assert: All timestamps are valid DateTimeImmutable instances
195+
// Assert: All timestamps are recent (within last hour - tests should run fast)
188196
foreach ($gitInfo->recentCommits as $commit) {
189-
self::assertInstanceOf(\DateTimeImmutable::class, $commit->committedAt);
190-
191-
// Check that timestamp is recent (within last hour - tests should run fast)
192-
$now = new \DateTimeImmutable();
197+
$now = DateAndTimeService::getDateTimeImmutable();
193198
$diff = $now->getTimestamp() - $commit->committedAt->getTimestamp();
194199
self::assertLessThan(3600, $diff, 'Commit timestamp should be recent');
195200
}

tests/Unit/WorkspaceMgmt/GitCliAdapterTest.php

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PHPUnit\Framework\TestCase;
99
use Symfony\Component\Process\Process;
1010

11-
use function count;
1211
use function is_dir;
1312
use function sys_get_temp_dir;
1413
use function uniqid;
@@ -57,12 +56,12 @@ protected function setUp(): void
5756

5857
protected function tearDown(): void
5958
{
60-
parent::tearDown();
61-
6259
// Clean up test repository
6360
if (is_dir($this->testRepoPath)) {
6461
$this->removeDirectory($this->testRepoPath);
6562
}
63+
64+
parent::tearDown();
6665
}
6766

6867
public function testGetCurrentBranch(): void
@@ -88,31 +87,27 @@ public function testGetRecentCommits(): void
8887
self::assertCount(3, $commits);
8988

9089
// Check first commit (most recent)
91-
self::assertArrayHasKey('hash', $commits[0]);
92-
self::assertArrayHasKey('subject', $commits[0]);
93-
self::assertArrayHasKey('body', $commits[0]);
94-
self::assertArrayHasKey('timestamp', $commits[0]);
95-
96-
self::assertSame('Third commit', $commits[0]['subject']);
97-
self::assertStringContainsString('Body line 1', $commits[0]['body']);
98-
self::assertStringContainsString('Body line 2', $commits[0]['body']);
90+
self::assertNotEmpty($commits[0]->hash);
91+
self::assertSame('Third commit', $commits[0]->subject);
92+
self::assertStringContainsString('Body line 1', $commits[0]->body);
93+
self::assertStringContainsString('Body line 2', $commits[0]->body);
9994

10095
// Check second commit
101-
self::assertSame('Second commit', $commits[1]['subject']);
102-
self::assertStringContainsString('This is the body of the second commit', $commits[1]['body']);
96+
self::assertSame('Second commit', $commits[1]->subject);
97+
self::assertStringContainsString('This is the body of the second commit', $commits[1]->body);
10398

10499
// Check third commit (oldest)
105-
self::assertSame('First commit', $commits[2]['subject']);
106-
self::assertSame('', $commits[2]['body']);
100+
self::assertSame('First commit', $commits[2]->subject);
101+
self::assertSame('', $commits[2]->body);
107102
}
108103

109104
public function testGetRecentCommitsWithLimit(): void
110105
{
111106
$commits = $this->adapter->getRecentCommits($this->testRepoPath, 2);
112107

113108
self::assertCount(2, $commits);
114-
self::assertSame('Third commit', $commits[0]['subject']);
115-
self::assertSame('Second commit', $commits[1]['subject']);
109+
self::assertSame('Third commit', $commits[0]->subject);
110+
self::assertSame('Second commit', $commits[1]->subject);
116111
}
117112

118113
public function testGetRecentCommitsReturnsEmptyArrayForNewRepo(): void
@@ -165,9 +160,9 @@ public function testGetRecentCommitsHandlesMultilineBody(): void
165160
$commits = $this->adapter->getRecentCommits($this->testRepoPath, 1);
166161

167162
self::assertCount(1, $commits);
168-
self::assertSame('Multiline commit', $commits[0]['subject']);
163+
self::assertSame('Multiline commit', $commits[0]->subject);
169164

170-
$body = $commits[0]['body'];
165+
$body = $commits[0]->body;
171166
self::assertStringContainsString('Line 1', $body);
172167
self::assertStringContainsString('Line 3', $body);
173168
}
@@ -179,7 +174,7 @@ public function testGetRecentCommitsTimestampIsIso8601(): void
179174
self::assertCount(1, $commits);
180175

181176
// Verify timestamp is in ISO 8601 format (e.g., 2024-01-01T12:00:00+00:00)
182-
$timestamp = $commits[0]['timestamp'];
177+
$timestamp = $commits[0]->timestamp;
183178
self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/', $timestamp);
184179
}
185180

0 commit comments

Comments
 (0)