diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 5b1f84b..6541411 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -25,6 +25,9 @@ on: - templates/** - tests/** +permissions: + contents: read + env: APP_NAME: integration_github @@ -86,6 +89,7 @@ jobs: CI_USER_LOGIN: ${{ secrets.CI_USER_LOGIN }} CI_USER_PASSWORD: ${{ secrets.CI_USER_PASSWORD }} CI_TOTP_SECRET: ${{ secrets.CI_TOTP_SECRET }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: composer run test:integration - name: Upload Nextcloud log on failure diff --git a/tests/integration/GitHubCodeReferenceIntegrationTest.php b/tests/integration/GitHubCodeReferenceIntegrationTest.php index 02ee4c3..77412d9 100644 --- a/tests/integration/GitHubCodeReferenceIntegrationTest.php +++ b/tests/integration/GitHubCodeReferenceIntegrationTest.php @@ -9,34 +9,40 @@ namespace OCA\Github\Tests\Integration; +require_once __DIR__ . '/WorkflowTokenTrait.php'; + use OCA\Github\Reference\GithubCodeReferenceProvider; use OCA\Github\Service\SecretService; use OCP\Collaboration\Reference\IReference; use OCP\Server; -use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\Group; use Test\TestCase; #[Group('DB')] class GitHubCodeReferenceIntegrationTest extends TestCase { + use WorkflowTokenTrait; + private GithubCodeReferenceProvider $referenceProvider; private SecretService $secretService; protected function setUp(): void { parent::setUp(); + $this->useWorkflowToken(); $this->referenceProvider = Server::get(GithubCodeReferenceProvider::class); $this->secretService = Server::get(SecretService::class); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testResolveSingleLineCodeReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + protected function tearDown(): void { + $this->restorePreviousToken(); + parent::tearDown(); + } + + public function testResolveSingleLineCodeReference(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -79,11 +85,7 @@ private function assertCodeRichObjectStructure(array $richObject): void { $this->assertStringContainsString('github.com', $richObject['link'], 'link should contain github.com'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testResolveMultiLineCodeReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testResolveMultiLineCodeReference(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1-L5'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -103,10 +105,7 @@ public function testResolveMultiLineCodeReference(array $oauthData): void { $this->assertCount($expectedLineCount, $richObject['lines'], 'lines array should have correct count'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testMatchReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - + public function testMatchReference(): void { $validSingleLine = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $validMultiLine = 'https://github.com/nextcloud/server/blob/abc123/lib/base.php#L1-L10'; $invalidNoLine = 'https://github.com/nextcloud/server/blob/master/lib/base.php'; @@ -118,11 +117,7 @@ public function testMatchReference(array $oauthData): void { $this->assertFalse($this->referenceProvider->matchReference($invalidWrongUrl), 'Should not match non-blob URL'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testReferenceTitle(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testReferenceTitle(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -133,11 +128,7 @@ public function testReferenceTitle(array $oauthData): void { $this->assertStringContainsString('permalink', strtolower($title), 'Title should mention permalink'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testReferenceDescription(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testReferenceDescription(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -151,11 +142,7 @@ public function testReferenceDescription(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testShortRefFormat(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testShortRefFormat(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -174,11 +161,7 @@ public function testShortRefFormat(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testVcsCodePermalinkStructure(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testVcsCodePermalinkStructure(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -204,11 +187,7 @@ public function testVcsCodePermalinkStructure(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testCodeLinesContent(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testCodeLinesContent(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1-L3'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -226,11 +205,7 @@ public function testCodeLinesContent(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testFilePathExtraction(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testFilePathExtraction(): void { $referenceUrl = 'https://github.com/nextcloud/server/blob/master/lib/base.php#L1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); diff --git a/tests/integration/GitHubIssuePrReferenceIntegrationTest.php b/tests/integration/GitHubIssuePrReferenceIntegrationTest.php index ac9393c..d347aec 100644 --- a/tests/integration/GitHubIssuePrReferenceIntegrationTest.php +++ b/tests/integration/GitHubIssuePrReferenceIntegrationTest.php @@ -9,34 +9,40 @@ namespace OCA\Github\Tests\Integration; +require_once __DIR__ . '/WorkflowTokenTrait.php'; + use OCA\Github\Reference\GithubIssuePrReferenceProvider; use OCA\Github\Service\SecretService; use OCP\Collaboration\Reference\IReference; use OCP\Server; -use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\Group; use Test\TestCase; #[Group('DB')] class GitHubIssuePrReferenceIntegrationTest extends TestCase { + use WorkflowTokenTrait; + private GithubIssuePrReferenceProvider $referenceProvider; private SecretService $secretService; protected function setUp(): void { parent::setUp(); + $this->useWorkflowToken(); $this->referenceProvider = Server::get(GithubIssuePrReferenceProvider::class); $this->secretService = Server::get(SecretService::class); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testResolveIssueReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + protected function tearDown(): void { + $this->restorePreviousToken(); + parent::tearDown(); + } + + public function testResolveIssueReference(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $referenceUrl = 'https://github.com/nextcloud/server/issues/1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -96,14 +102,11 @@ private function assertIssueRichObjectStructure(array $richObject): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testResolvePullRequestReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testResolvePullRequestReference(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $referenceUrl = 'https://github.com/nextcloud/server/pull/1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -156,14 +159,11 @@ private function assertPullRequestRichObjectStructure(array $richObject): void { $this->assertValidDateString($richObject['created_at'], 'created_at should be a valid date string'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testResolveIssueWithCommentReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testResolveIssueWithCommentReference(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $referenceUrl = 'https://github.com/nextcloud/server/issues/1#issuecomment-223229268'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -193,10 +193,7 @@ private function assertCommentStructure(array $comment): void { $this->assertValidDateString($comment['updated_at'], 'Comment updated_at should be a valid date string'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testMatchReference(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - + public function testMatchReference(): void { $validIssueUrl = 'https://github.com/nextcloud/server/issues/123'; $validPrUrl = 'https://github.com/nextcloud/server/pull/456'; $invalidUrl = 'https://github.com/nextcloud/server'; @@ -206,11 +203,7 @@ public function testMatchReference(array $oauthData): void { $this->assertFalse($this->referenceProvider->matchReference($invalidUrl), 'Should not match repo URL'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testReferenceTitle(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testReferenceTitle(): void { $referenceUrl = 'https://github.com/nextcloud/server/issues/1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -222,11 +215,7 @@ public function testReferenceTitle(array $oauthData): void { $this->assertStringContainsString('nextcloud/server', $title, 'Title should contain repo name'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testReferenceMilestone(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testReferenceMilestone(): void { $referenceUrl = 'https://github.com/nextcloud/server/issues/1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); @@ -240,11 +229,7 @@ public function testReferenceMilestone(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testReferenceReactions(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - + public function testReferenceReactions(): void { $referenceUrl = 'https://github.com/nextcloud/server/issues/1'; $reference = $this->referenceProvider->resolveReference($referenceUrl); diff --git a/tests/integration/GitHubSearchIntegrationTest.php b/tests/integration/GitHubSearchIntegrationTest.php index 433efd8..c95be74 100644 --- a/tests/integration/GitHubSearchIntegrationTest.php +++ b/tests/integration/GitHubSearchIntegrationTest.php @@ -9,33 +9,39 @@ namespace OCA\Github\Tests\Integration; +require_once __DIR__ . '/WorkflowTokenTrait.php'; + use OCA\Github\Service\GithubAPIService; use OCA\Github\Service\SecretService; use OCP\Server; -use PHPUnit\Framework\Attributes\DependsExternal; use PHPUnit\Framework\Attributes\Group; use Test\TestCase; #[Group('DB')] class GitHubSearchIntegrationTest extends TestCase { + use WorkflowTokenTrait; + private GithubAPIService $githubAPIService; private SecretService $secretService; protected function setUp(): void { parent::setUp(); + $this->useWorkflowToken(); $this->githubAPIService = Server::get(GithubAPIService::class); $this->secretService = Server::get(SecretService::class); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testSearchRepositoriesStructure(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test, got: ' . gettype($oauthData)); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + protected function tearDown(): void { + $this->restorePreviousToken(); + parent::tearDown(); + } + + public function testSearchRepositoriesStructure(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $result = $this->githubAPIService->searchRepositories($userId, 'nextcloud', 0, 5); $this->assertArrayNotHasKey('error', $result, 'GitHub API returned error: ' . ($result['error'] ?? 'unknown')); @@ -62,11 +68,8 @@ private function assertRepositoryStructure(array $repo): void { $this->assertStringContainsString('github.com', $repo['html_url'], 'html_url should point to github.com'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testSearchRepositoriesPagination(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testSearchRepositoriesPagination(): void { + $userId = $this->userId; $result1 = $this->githubAPIService->searchRepositories($userId, 'nextcloud', 0, 3); $this->assertArrayNotHasKey('error', $result1, 'GitHub API returned error'); @@ -83,14 +86,11 @@ public function testSearchRepositoriesPagination(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testSearchIssuesStructure(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test, got: ' . gettype($oauthData)); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testSearchIssuesStructure(): void { + $userId = $this->userId; $token = $this->secretService->getEncryptedUserValue($userId, 'token'); - $this->assertNotSame('', $token, 'Token should be stored after OAuth flow'); + $this->assertNotSame('', $token, 'The workflow token should be stored for the test user'); $result = $this->githubAPIService->searchIssues($userId, 'nextcloud is:open', 0, 5); $this->assertArrayNotHasKey('error', $result, 'GitHub API returned error: ' . ($result['error'] ?? 'unknown')); @@ -130,11 +130,8 @@ private function assertValidRepositoryUrl(string $url): void { $this->assertCount(2, $parts, 'Repository path should have owner/repo format'); } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testSearchIssuesPullRequestDetection(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testSearchIssuesPullRequestDetection(): void { + $userId = $this->userId; $result = $this->githubAPIService->searchIssues($userId, 'nextcloud is:pr is:open', 0, 10); $this->assertArrayNotHasKey('error', $result, 'GitHub API returned error'); @@ -148,11 +145,8 @@ public function testSearchIssuesPullRequestDetection(array $oauthData): void { } } - #[DependsExternal(GithubOauthIntegrationTest::class, 'testOAuthLogin')] - public function testSearchIssuesIssuesOnly(array $oauthData): void { - $this->assertIsArray($oauthData, 'oauthData should be an array from OAuth test'); - $this->assertArrayHasKey('userId', $oauthData, 'oauthData must contain userId'); - $userId = $oauthData['userId']; + public function testSearchIssuesIssuesOnly(): void { + $userId = $this->userId; $result = $this->githubAPIService->searchIssues($userId, 'nextcloud is:issue is:open', 0, 10); $this->assertArrayNotHasKey('error', $result, 'GitHub API returned error'); diff --git a/tests/integration/WorkflowTokenTrait.php b/tests/integration/WorkflowTokenTrait.php new file mode 100644 index 0000000..23a192f --- /dev/null +++ b/tests/integration/WorkflowTokenTrait.php @@ -0,0 +1,73 @@ +markTestSkipped('GITHUB_TOKEN not set'); + } + + $userManager = Server::get(IUserManager::class); + $user = $userManager->get(self::WORKFLOW_TOKEN_USER_ID) + ?? $userManager->createUser(self::WORKFLOW_TOKEN_USER_ID, 'test-password'); + self::loginAsUser($user->getUID()); + $this->userId = $user->getUID(); + + $config = Server::get(IConfig::class); + $secretService = Server::get(SecretService::class); + $this->previousToken = $secretService->getEncryptedUserValue($this->userId, 'token'); + $this->previousTokenType = $config->getUserValue($this->userId, Application::APP_ID, 'token_type'); + + $secretService->setEncryptedUserValue($this->userId, 'token', $token); + $config->setUserValue($this->userId, Application::APP_ID, 'token_type', 'personal'); + } + + private function restorePreviousToken(): void { + if (!isset($this->userId)) { + return; + } + + $config = Server::get(IConfig::class); + if ($this->previousToken === '') { + $config->deleteUserValue($this->userId, Application::APP_ID, 'token'); + } else { + Server::get(SecretService::class)->setEncryptedUserValue($this->userId, 'token', $this->previousToken); + } + if ($this->previousTokenType === '') { + $config->deleteUserValue($this->userId, Application::APP_ID, 'token_type'); + } else { + $config->setUserValue($this->userId, Application::APP_ID, 'token_type', $this->previousTokenType); + } + } +}