Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
92 changes: 87 additions & 5 deletions src/VCS/Adapter/Git/Gitea.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,19 +347,82 @@ public function deleteRepository(string $owner, string $repositoryName): bool
return true;
}

/**
* Create a pull request
*
* @param string $owner Owner of the repository
* @param string $repositoryName Name of the repository
* @param string $title PR title
* @param string $head Source branch
* @param string $base Target branch
* @param string $body PR description (optional)
* @return array<mixed> Created PR details
*/
public function createPullRequest(string $owner, string $repositoryName, string $title, string $head, string $base, string $body = ''): array
Comment thread
Meldiron marked this conversation as resolved.
{
$url = "/repos/{$owner}/{$repositoryName}/pulls";

$payload = [
'title' => $title,
'head' => $head,
'base' => $base,
];

if (!empty($body)) {
$payload['body'] = $body;
}

$response = $this->call(
self::METHOD_POST,
$url,
['Authorization' => "token $this->accessToken"],
$payload
);

$responseBody = $response['body'] ?? [];

return $responseBody;
Comment thread
jaysomani marked this conversation as resolved.
}

public function createComment(string $owner, string $repositoryName, int $pullRequestNumber, string $comment): string
{
throw new Exception("Not implemented yet");
$url = "/repos/{$owner}/{$repositoryName}/issues/{$pullRequestNumber}/comments";

$response = $this->call(self::METHOD_POST, $url, ['Authorization' => "token $this->accessToken"], ['body' => $comment]);

$responseBody = $response['body'] ?? [];

if (!array_key_exists('id', $responseBody)) {
throw new Exception("Comment creation response is missing comment ID.");
}

return (string) ($responseBody['id'] ?? '');
}
Comment thread
jaysomani marked this conversation as resolved.

public function getComment(string $owner, string $repositoryName, string $commentId): string
{
throw new Exception("Not implemented yet");
$url = "/repos/{$owner}/{$repositoryName}/issues/comments/{$commentId}";

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);

$responseBody = $response['body'] ?? [];

return $responseBody['body'] ?? '';
}

public function updateComment(string $owner, string $repositoryName, int $commentId, string $comment): string
{
throw new Exception("Not implemented yet");
$url = "/repos/{$owner}/{$repositoryName}/issues/comments/{$commentId}";

$response = $this->call(self::METHOD_PATCH, $url, ['Authorization' => "token $this->accessToken"], ['body' => $comment]);

$responseBody = $response['body'] ?? [];

if (!array_key_exists('id', $responseBody)) {
throw new Exception("Comment update response is missing comment ID.");
}

return (string) ($responseBody['id'] ?? '');
}
Comment thread
jaysomani marked this conversation as resolved.

public function getUser(string $username): array
Expand All @@ -374,12 +437,31 @@ public function getOwnerName(string $installationId): string

public function getPullRequest(string $owner, string $repositoryName, int $pullRequestNumber): array
{
throw new Exception("Not implemented yet");
$url = "/repos/{$owner}/{$repositoryName}/pulls/{$pullRequestNumber}";

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);

return $response['body'] ?? [];
}

public function getPullRequestFromBranch(string $owner, string $repositoryName, string $branch): array
{
throw new Exception("Not implemented yet");
$url = "/repos/{$owner}/{$repositoryName}/pulls?state=open&sort=recentupdate&limit=1";
Comment thread
jaysomani marked this conversation as resolved.
Outdated

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);

Comment thread
jaysomani marked this conversation as resolved.
$responseBody = $response['body'] ?? [];

// Filter by head branch (source branch of the PR)
foreach ($responseBody as $pr) {
$prHead = $pr['head'] ?? [];
$prHeadRef = $prHead['ref'] ?? '';
Comment thread
jaysomani marked this conversation as resolved.
Outdated
if ($prHeadRef === $branch) {
return $pr;
}
}

return [];
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
Comment thread
jaysomani marked this conversation as resolved.

public function listBranches(string $owner, string $repositoryName): array
Expand Down
205 changes: 202 additions & 3 deletions tests/VCS/Adapter/GiteaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,111 @@ public function testCreatePrivateRepository(): void
$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testCommentWorkflow(): void
{
$repositoryName = 'test-comment-workflow-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);

$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');
$this->vcsAdapter->createBranch(self::$owner, $repositoryName, 'comment-test', 'main');
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test');

$pr = $this->vcsAdapter->createPullRequest(
self::$owner,
$repositoryName,
'Comment Test PR',
'comment-test',
'main'
);

$prNumber = $pr['number'] ?? 0;
$this->assertGreaterThan(0, $prNumber);

$originalComment = 'This is a test comment';
$commentId = $this->vcsAdapter->createComment(self::$owner, $repositoryName, $prNumber, $originalComment);

$this->assertNotEmpty($commentId);
$this->assertIsString($commentId);

// Test getComment
$retrievedComment = $this->vcsAdapter->getComment(self::$owner, $repositoryName, $commentId);

$this->assertSame($originalComment, $retrievedComment);
$this->assertIsString($commentId);
$this->assertNotEmpty($commentId);

// Test updateComment
$updatedCommentText = 'This comment has been updated';
$updatedCommentId = $this->vcsAdapter->updateComment(self::$owner, $repositoryName, (int)$commentId, $updatedCommentText);

$this->assertSame($commentId, $updatedCommentId);

// Verify the update
$finalComment = $this->vcsAdapter->getComment(self::$owner, $repositoryName, $commentId);
$this->assertSame($updatedCommentText, $finalComment);

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testGetComment(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
$repositoryName = 'test-get-comment-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);

$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');
$this->vcsAdapter->createBranch(self::$owner, $repositoryName, 'test-branch', 'main');
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test');

// Create PR
$pr = $this->vcsAdapter->createPullRequest(
self::$owner,
$repositoryName,
'Test PR',
'test-branch',
'main'
);

$prNumber = $pr['number'] ?? 0;

// Create a comment
$commentId = $this->vcsAdapter->createComment(self::$owner, $repositoryName, $prNumber, 'Test comment');

Comment thread
jaysomani marked this conversation as resolved.
// Test getComment
$result = $this->vcsAdapter->getComment(self::$owner, $repositoryName, $commentId);

$this->assertIsString($result);
$this->assertSame('Test comment', $result);

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testCreateCommentInvalidPR(): void
{
$repositoryName = 'test-comment-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->createComment(self::$owner, $repositoryName, 99999, 'Test comment');
} finally {
$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}
}

public function testGetCommentInvalidId(): void
{
$repositoryName = 'test-get-comment-invalid-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');

// Getting invalid comment should return empty string
$result = $this->vcsAdapter->getComment(self::$owner, $repositoryName, '99999999');

$this->assertIsString($result);
// May be empty or throw exception depending on API

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testGetRepositoryTreeWithSlashInBranchName(): void
Expand Down Expand Up @@ -340,7 +442,59 @@ public function testListRepositoryContentsNonExistingPath(): void

public function testGetPullRequest(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
$repositoryName = 'test-get-pull-request-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);

// Create initial file on main branch
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');

// Create feature branch and add file
$this->vcsAdapter->createBranch(self::$owner, $repositoryName, 'feature-branch', 'main');
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'feature.txt', 'feature content');

// Create pull request
$pr = $this->vcsAdapter->createPullRequest(
self::$owner,
$repositoryName,
'Test PR',
'feature-branch',
'main',
'Test PR description'
);

$prNumber = $pr['number'] ?? 0;
$this->assertGreaterThan(0, $prNumber);

// Now test getPullRequest
$result = $this->vcsAdapter->getPullRequest(self::$owner, $repositoryName, $prNumber);

$this->assertIsArray($result);
$this->assertArrayHasKey('number', $result);
$this->assertArrayHasKey('title', $result);
$this->assertArrayHasKey('state', $result);
$this->assertArrayHasKey('head', $result);
$this->assertArrayHasKey('base', $result);

$this->assertSame($prNumber, $result['number']);
$this->assertSame('Test PR', $result['title']);
$this->assertSame('open', $result['state']);

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testGetPullRequestWithInvalidNumber(): void
{
$repositoryName = 'test-get-pull-request-invalid-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');

// Try to get non-existent PR
$result = $this->vcsAdapter->getPullRequest(self::$owner, $repositoryName, 99999);

// Should return empty or have error handling
$this->assertIsArray($result);

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
Comment thread
jaysomani marked this conversation as resolved.
Outdated
Comment thread
jaysomani marked this conversation as resolved.
Outdated
}

public function testGenerateCloneCommand(): void
Expand Down Expand Up @@ -505,7 +659,52 @@ public function testGetOwnerName(): void

public function testGetPullRequestFromBranch(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
$repositoryName = 'test-get-pr-from-branch-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);

$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');
$this->vcsAdapter->createBranch(self::$owner, $repositoryName, 'my-feature', 'main');
$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'feature.txt', 'content');

// Create PR
$pr = $this->vcsAdapter->createPullRequest(
self::$owner,
$repositoryName,
'Feature PR',
'my-feature',
'main'
);

$this->assertArrayHasKey('number', $pr);

// Test getPullRequestFromBranch
$result = $this->vcsAdapter->getPullRequestFromBranch(self::$owner, $repositoryName, 'my-feature');

$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertArrayHasKey('head', $result);

$resultHead = $result['head'] ?? [];
$this->assertSame('my-feature', $resultHead['ref'] ?? '');

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testGetPullRequestFromBranchNoPR(): void
{
$repositoryName = 'test-get-pr-no-pr-' . \uniqid();
$this->vcsAdapter->createRepository(self::$owner, $repositoryName, false);

$this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test');
$this->vcsAdapter->createBranch(self::$owner, $repositoryName, 'lonely-branch', 'main');

// Don't create a PR - just test the method
$result = $this->vcsAdapter->getPullRequestFromBranch(self::$owner, $repositoryName, 'lonely-branch');

$this->assertIsArray($result);
$this->assertEmpty($result);

$this->vcsAdapter->deleteRepository(self::$owner, $repositoryName);
}

public function testCreateComment(): void
Expand Down