diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index 303f97aa..568869a4 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -387,14 +387,84 @@ public function listBranches(string $owner, string $repositoryName): array throw new Exception("Not implemented yet"); } + /** + * Get details of a commit using commit hash + * + * @param string $owner Owner name of the repository + * @param string $repositoryName Name of the repository + * @param string $commitHash SHA of the commit + * @return array Details of the commit + */ public function getCommit(string $owner, string $repositoryName, string $commitHash): array { - throw new Exception("Not implemented yet"); + $url = "/repos/{$owner}/{$repositoryName}/git/commits/{$commitHash}"; + + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); + + $responseHeaders = $response['headers'] ?? []; + $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0; + if ($responseHeadersStatusCode >= 400) { + throw new Exception("Commit not found or inaccessible"); + } + + $responseBody = $response['body'] ?? []; + $responseBodyCommit = $responseBody['commit'] ?? []; + $responseBodyCommitAuthor = $responseBodyCommit['author'] ?? []; + $responseBodyAuthor = $responseBody['author'] ?? []; + + return [ + 'commitAuthor' => $responseBodyCommitAuthor['name'] ?? 'Unknown', + 'commitMessage' => $responseBodyCommit['message'] ?? 'No message', + 'commitAuthorAvatar' => $responseBodyAuthor['avatar_url'] ?? '', + 'commitAuthorUrl' => $responseBodyAuthor['html_url'] ?? '', + 'commitHash' => $responseBody['sha'] ?? '', + 'commitUrl' => $responseBody['html_url'] ?? '', + ]; } + /** + * Get latest commit of a branch + * + * @param string $owner Owner name of the repository + * @param string $repositoryName Name of the repository + * @param string $branch Name of the branch + * @return array Details of the commit + */ public function getLatestCommit(string $owner, string $repositoryName, string $branch): array { - throw new Exception("Not implemented yet"); + $query = http_build_query([ + 'sha' => $branch, + 'limit' => 1, + ]); + $url = "/repos/{$owner}/{$repositoryName}/commits?{$query}"; + + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); + + $responseHeaders = $response['headers'] ?? []; + $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0; + if ($responseHeadersStatusCode >= 400) { + throw new Exception("Latest commit response failed with status code {$responseHeadersStatusCode}"); + } + + $responseBody = $response['body'] ?? []; + + if (empty($responseBody[0] ?? [])) { + throw new Exception("Latest commit response is missing required information."); + } + + $responseBodyFirst = $responseBody[0]; + $responseBodyFirstCommit = $responseBodyFirst['commit'] ?? []; + $responseBodyFirstCommitAuthor = $responseBodyFirstCommit['author'] ?? []; + $responseBodyFirstAuthor = $responseBodyFirst['author'] ?? []; + + return [ + 'commitAuthor' => $responseBodyFirstCommitAuthor['name'] ?? 'Unknown', + 'commitMessage' => $responseBodyFirstCommit['message'] ?? 'No message', + 'commitHash' => $responseBodyFirst['sha'] ?? '', + 'commitUrl' => $responseBodyFirst['html_url'] ?? '', + 'commitAuthorAvatar' => $responseBodyFirstAuthor['avatar_url'] ?? '', + 'commitAuthorUrl' => $responseBodyFirstAuthor['html_url'] ?? '', + ]; } public function updateCommitStatus(string $repositoryName, string $commitHash, string $owner, string $state, string $description = '', string $target_url = '', string $context = ''): void diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 18f30c33..f21a4c96 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -365,12 +365,87 @@ public function testUpdateComment(): void public function testGetCommit(): void { - $this->markTestSkipped('Will be implemented in follow-up PR'); + $repositoryName = 'test-get-commit-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + $customMessage = 'Test commit message'; + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit', $customMessage); + + $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); + $commitHash = $latestCommit['commitHash']; + + $result = $this->vcsAdapter->getCommit(self::$owner, $repositoryName, $commitHash); + + $this->assertIsArray($result); + $this->assertArrayHasKey('commitHash', $result); + $this->assertArrayHasKey('commitMessage', $result); + $this->assertArrayHasKey('commitAuthor', $result); + $this->assertArrayHasKey('commitUrl', $result); + $this->assertArrayHasKey('commitAuthorAvatar', $result); + $this->assertArrayHasKey('commitAuthorUrl', $result); + + $this->assertSame($commitHash, $result['commitHash']); + $this->assertStringContainsString($customMessage, $result['commitMessage']); + $this->assertNotEmpty($result['commitAuthor']); + $this->assertNotEmpty($result['commitUrl']); + + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); } public function testGetLatestCommit(): void { - $this->markTestSkipped('Will be implemented in follow-up PR'); + $repositoryName = 'test-get-latest-commit-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + $firstMessage = 'First commit'; + $secondMessage = 'Second commit'; + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test', $firstMessage); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test content', $secondMessage); + + $result = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); + + $this->assertIsArray($result); + $this->assertArrayHasKey('commitHash', $result); + $this->assertArrayHasKey('commitMessage', $result); + $this->assertArrayHasKey('commitAuthor', $result); + $this->assertArrayHasKey('commitUrl', $result); + $this->assertArrayHasKey('commitAuthorAvatar', $result); + $this->assertArrayHasKey('commitAuthorUrl', $result); + + $this->assertNotEmpty($result['commitHash']); + $this->assertStringContainsString($secondMessage, $result['commitMessage']); + $this->assertNotEmpty($result['commitAuthor']); + $this->assertNotEmpty($result['commitUrl']); + + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } + + public function testGetCommitWithInvalidSha(): void + { + $repositoryName = 'test-get-commit-invalid-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); + + try { + $this->expectException(\Exception::class); + $this->vcsAdapter->getCommit(self::$owner, $repositoryName, 'invalid-sha-12345'); + } finally { + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } + } + + public function testGetLatestCommitWithInvalidBranch(): void + { + $repositoryName = 'test-get-latest-commit-invalid-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); + + try { + $this->expectException(\Exception::class); + $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'non-existing-branch'); + } finally { + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } } public function testGetEvent(): void