diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index 8c563fa12..410325bd6 100755 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -155,7 +155,7 @@ public function setConfig(array $values): DataResponse { $result = []; if (isset($values['token'])) { - if ($values['token'] && $values['token'] !== '') { + if ($values['token']) { $result = $this->storeUserInfo(); } else { $this->clearUserInfo(); @@ -192,7 +192,7 @@ private function setIntegrationConfig(array $values): array { ]; // if values contains a key that is not in the allowedKeys array, // return a response with status code 400 and an error message - foreach ($values as $key => $value) { + foreach (array_keys($values) as $key) { if (!in_array($key, $allowedKeys)) { throw new InvalidArgumentException('Invalid key'); } @@ -247,7 +247,7 @@ private function setIntegrationConfig(array $values): array { if ($key === 'setup_project_folder' || $key === 'setup_app_password') { continue; } - $this->config->setAppValue(Application::APP_ID, $key, trim($value)); + $this->config->setAppValue(Application::APP_ID, $key, trim((string)$value)); } // if the OpenProject OAuth URL has changed @@ -266,7 +266,7 @@ private function setIntegrationConfig(array $values): array { Application::APP_ID, 'nc_oauth_client_id', '' ); $this->oauthService->setClientRedirectUri( - (int)$oauthClientInternalId, $values['openproject_instance_url'] + (int)$oauthClientInternalId, (string)$values['openproject_instance_url'] ); } } diff --git a/lib/Controller/DirectUploadController.php b/lib/Controller/DirectUploadController.php index 0bc2bbfd3..d77653fcf 100644 --- a/lib/Controller/DirectUploadController.php +++ b/lib/Controller/DirectUploadController.php @@ -181,7 +181,6 @@ public function directUpload(string $token):DataResponse { throw new NotFoundException('invalid token'); } $tokenInfo = $this->directUploadService->getTokenInfo($token); - $fileId = null; $directUploadFile = $this->request->getUploadedFile('file'); if (empty($directUploadFile)) { throw new OpenprojectFileNotUploadedException( diff --git a/lib/Migration/Version2001Date20221213083550.php b/lib/Migration/Version2001Date20221213083550.php index c09ff6f86..097c78aca 100644 --- a/lib/Migration/Version2001Date20221213083550.php +++ b/lib/Migration/Version2001Date20221213083550.php @@ -50,7 +50,7 @@ public function __construct(IConfig $config) { /** * @param IOutput $output * @param Closure(): ISchemaWrapper $schemaClosure - * @param array $options + * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { diff --git a/lib/Migration/Version2310Date20230116153411.php b/lib/Migration/Version2310Date20230116153411.php index 98f73d4ec..3e6a44af6 100644 --- a/lib/Migration/Version2310Date20230116153411.php +++ b/lib/Migration/Version2310Date20230116153411.php @@ -37,7 +37,7 @@ class Version2310Date20230116153411 extends SimpleMigrationStep { /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options + * @param array $options * @return null|ISchemaWrapper * @throws SchemaException */ diff --git a/lib/Migration/Version2400Date20230504144300.php b/lib/Migration/Version2400Date20230504144300.php index 784ff218e..b1f73ab79 100644 --- a/lib/Migration/Version2400Date20230504144300.php +++ b/lib/Migration/Version2400Date20230504144300.php @@ -50,7 +50,7 @@ public function __construct(IConfig $config) { /** * @param IOutput $output * @param Closure(): ISchemaWrapper $schemaClosure - * @param array $options + * @param array $options * @return null|ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { diff --git a/lib/Migration/Version2640Date20240628114301.php b/lib/Migration/Version2640Date20240628114301.php index 2712c2165..3ca71b7c2 100644 --- a/lib/Migration/Version2640Date20240628114301.php +++ b/lib/Migration/Version2640Date20240628114301.php @@ -55,7 +55,7 @@ public function __construct( /** * @param IOutput $output * @param Closure(): ISchemaWrapper $schemaClosure - * @param array $options + * @param array $options * @return null|ISchemaWrapper * @throws DoesNotExistException * @throws Exception diff --git a/lib/Service/OpenProjectAPIService.php b/lib/Service/OpenProjectAPIService.php index ea1d23fcb..5ed9f83eb 100644 --- a/lib/Service/OpenProjectAPIService.php +++ b/lib/Service/OpenProjectAPIService.php @@ -238,7 +238,6 @@ public function searchWorkPackage( bool $onlyLinkableWorkPackages = true, int $workPackageId = null ): array { - $resultsById = []; $filters = []; // search by description @@ -604,7 +603,7 @@ public static function validateIntegrationSetupInformation(?array $values, bool throw new InvalidArgumentException('invalid data'); } } else { - foreach ($values as $key => $value) { + foreach (array_keys($values) as $key) { if (!in_array($key, $opKeys)) { throw new InvalidArgumentException('invalid key'); } @@ -761,7 +760,7 @@ public function linkWorkPackageToFile( 'workpackageId', 'fileinfo' ]; - foreach ($values as $key => $value) { + foreach (array_keys($values) as $key) { if (!in_array($key, $allowedKeys)) { throw new InvalidArgumentException('invalid key'); } diff --git a/lib/Settings/AdminSection.php b/lib/Settings/AdminSection.php index 680d4b5b8..ec25528be 100644 --- a/lib/Settings/AdminSection.php +++ b/lib/Settings/AdminSection.php @@ -49,9 +49,9 @@ public function getPriority(): int { } /** - * @return ?string The relative path to a an icon describing the section + * @return string The relative path to a an icon describing the section */ - public function getIcon(): ?string { + public function getIcon(): string { return $this->urlGenerator->imagePath('integration_openproject', 'app-dark.svg'); } } diff --git a/lib/Settings/PersonalSection.php b/lib/Settings/PersonalSection.php index 7b7c30efc..9ab1915dc 100644 --- a/lib/Settings/PersonalSection.php +++ b/lib/Settings/PersonalSection.php @@ -49,9 +49,9 @@ public function getPriority(): int { } /** - * @return ?string The relative path to a an icon describing the section + * @return string The relative path to a an icon describing the section */ - public function getIcon(): ?string { + public function getIcon(): string { return $this->urlGenerator->imagePath('integration_openproject', 'app-dark.svg'); } } diff --git a/psalm.xml b/psalm.xml index 926a320fc..383521f40 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,12 +1,13 @@ @@ -17,7 +18,8 @@ - + + diff --git a/tests/acceptance/features/bootstrap/DirectUploadContext.php b/tests/acceptance/features/bootstrap/DirectUploadContext.php index 0c1313018..e428ec6a5 100644 --- a/tests/acceptance/features/bootstrap/DirectUploadContext.php +++ b/tests/acceptance/features/bootstrap/DirectUploadContext.php @@ -1,6 +1,7 @@ getEnvironment(); // Get all the contexts you need in this context - $this->featureContext = $environment->getContext('FeatureContext'); + if ($environment instanceof InitializedContextEnvironment) { + $this->featureContext = $environment->getContext('FeatureContext'); + } } } diff --git a/tests/acceptance/features/bootstrap/FeatureContext.php b/tests/acceptance/features/bootstrap/FeatureContext.php index 4f95fa2b7..095286871 100644 --- a/tests/acceptance/features/bootstrap/FeatureContext.php +++ b/tests/acceptance/features/bootstrap/FeatureContext.php @@ -1,6 +1,7 @@ + * @var array */ private array $createdFiles = []; @@ -127,21 +128,21 @@ private function createUserWithRetry(string $user, array $userAttributes): void $isUserCreated = true; break; } elseif ($response->getStatusCode() === 400 && getenv('CI')) { - var_dump("Error: " . $response->getBody()->getContents()); - var_dump('Creating user ' . $user . ' failed!'); - var_dump('Deleting the file system of ' . $user . ' and retrying the user creation again...'); + echo("Error: " . $response->getBody()->getContents()); + echo('Creating user ' . $user . ' failed!'); + echo('Deleting the file system of ' . $user . ' and retrying the user creation again...'); exec( "docker exec nextcloud /bin/bash -c 'rm -rf data/$user'", $output, $command_result_code ); if ($command_result_code === 0) { - var_dump('File system for user ' . $user . ' has been deleted successfully!'); + echo('File system for user ' . $user . ' has been deleted successfully!'); } } else { // in case of any other error we just log the response - var_dump("Status Code: " . $response->getStatusCode()); - var_dump("Error: " . $response->getBody()->getContents()); + echo("Status Code: " . $response->getStatusCode()); + echo("Error: " . $response->getBody()->getContents()); } sleep(2); $retryCreate++; @@ -161,7 +162,7 @@ public function userHasBeenCreated(string $user, string $displayName = null):voi $userAttributes['displayName'] = $displayName; } $this->createUserWithRetry($user, $userAttributes); - $userid = \strtolower((string)$user); + $userid = \strtolower($user); $this->createdUsers[$userid] = $userAttributes; $this->response = $this->makeDavRequest( $user, @@ -552,9 +553,9 @@ public function theDataOfTheOCSResponseShouldMatch( PyStringNode $schemaString ): void { $responseAsJson = json_decode($this->response->getBody()->getContents()); - $responseAsJson = $responseAsJson->ocs->data; + $_responseAsJson = $responseAsJson->ocs->data; JsonAssertions::assertJsonDocumentMatchesSchema( - $responseAsJson, + $_responseAsJson, $this->getJSONSchema($schemaString) ); } @@ -568,9 +569,9 @@ public function theDataOfTheOCSResponseShouldMatch( public function theDataOfTheResponseShouldMatch( PyStringNode $schemaString ): void { - $responseAsJson = json_decode($this->response->getBody()->getContents()); + $_responseAsJson = json_decode($this->response->getBody()->getContents()); JsonAssertions::assertJsonDocumentMatchesSchema( - $responseAsJson, + $_responseAsJson, $this->getJSONSchema($schemaString) ); } @@ -579,7 +580,7 @@ public function uploadFileWithContent( string $user, ?string $content, string $destination - ): int { + ): ?string { $this->response = $this->makeDavRequest( $user, $this->regularUserPassword, @@ -613,18 +614,15 @@ public function theFollowingHeadersShouldBeSet(TableNode $table):void { $expectedHeaderValue = $header['value']; $returnedHeader = $this->response->getHeader($headerName); - $headerValue = $returnedHeader; - if (\is_array($returnedHeader)) { - if (empty($returnedHeader)) { - throw new Exception( - \sprintf( - "Missing expected header '%s'", - $headerName - ) - ); - } - $headerValue = $returnedHeader[0]; + if (empty($returnedHeader)) { + throw new Exception( + \sprintf( + "Missing expected header '%s'", + $headerName + ) + ); } + $headerValue = $returnedHeader[0]; Assert::assertEquals( $expectedHeaderValue, @@ -712,7 +710,7 @@ public function propfindFileOrFolder(string $user, string $path): ResponseInterf ); } - public function getIdOfFileOrFolder(string $user, string $path): int { + public function getIdOfFileOrFolder(string $user, string $path): string { $propfindResponse = $this->propfindFileOrFolder($user, $path); // Ensure PROPFIND returned status 207 $this->theHTTPStatusCodeShouldBe(207, "", $propfindResponse); @@ -721,7 +719,10 @@ public function getIdOfFileOrFolder(string $user, string $path): int { 'oc', 'http://owncloud.org/ns' ); - return (int)(string)$responseXmlObject->xpath('//oc:fileid')[0]; + $fileId = $responseXmlObject->xpath('//oc:fileid')[0]; + Assert::assertNotNull($fileId, __METHOD__ . " file $path user $user not found (the file may not exist)"); + + return (string) $fileId; } public function fileOrFolderExists(string $user, string $path): bool { @@ -1091,6 +1092,11 @@ public function sendRequestsToAppEndpoint( $fullUrl = $this->getBaseUrl(); $fullUrl .= "index.php/apps/integration_openproject/" . $endpoint; + // Handle PyStringNode + if ($data instanceof PyStringNode) { + $data = (string)$data; + } + // don't set content-type for multipart requests if (is_array($data) && $headers === null) { $options['multipart'] = $data; @@ -1158,8 +1164,10 @@ public function before(BeforeScenarioScope $scope):void { $environment = $scope->getEnvironment(); // Get all the contexts you need in this context - $this->sharingContext = $environment->getContext('SharingContext'); - $this->directUploadContext = $environment->getContext('DirectUploadContext'); + if ($environment instanceof InitializedContextEnvironment) { + $this->sharingContext = $environment->getContext('SharingContext'); + $this->directUploadContext = $environment->getContext('DirectUploadContext'); + } } /** diff --git a/tests/acceptance/features/bootstrap/FilesVersionsContext.php b/tests/acceptance/features/bootstrap/FilesVersionsContext.php index ee533885d..b294fd4ce 100644 --- a/tests/acceptance/features/bootstrap/FilesVersionsContext.php +++ b/tests/acceptance/features/bootstrap/FilesVersionsContext.php @@ -1,6 +1,7 @@ featureContext->getIdOfFileOrFolder($user, $path); - Assert::assertNotNull($fileId, __METHOD__ . " file $path user $user not found (the file may not exist)"); $this->theVersionFolderOfFileIdShouldContainElements($user, $fileId, $count); } @@ -36,7 +36,7 @@ public function theVersionFolderOfFileShouldContainElements( * assert file versions count * * @param string $user - * @param int $fileId + * @param string $fileId * @param int $count * * @return void @@ -44,7 +44,7 @@ public function theVersionFolderOfFileShouldContainElements( */ public function theVersionFolderOfFileIdShouldContainElements( string $user, - int $fileId, + string $fileId, int $count ):void { $responseXml = $this->listVersionFolder($user, $fileId); @@ -60,7 +60,7 @@ public function theVersionFolderOfFileIdShouldContainElements( * returns the result parsed into an SimpleXMLElement * * @param string $user - * @param int $fileId + * @param string $fileId * * @return SimpleXMLElement * @throws GuzzleException @@ -68,7 +68,7 @@ public function theVersionFolderOfFileIdShouldContainElements( */ public function listVersionFolder( string $user, - int $fileId + string $fileId ):SimpleXMLElement { $password = $this->featureContext->getRegularUserPassword(); $fullUrl = $this->featureContext->sanitizeUrl( @@ -110,6 +110,8 @@ public function before(BeforeScenarioScope $scope):void { $environment = $scope->getEnvironment(); // Get all the contexts you need in this context - $this->featureContext = $environment->getContext('FeatureContext'); + if ($environment instanceof InitializedContextEnvironment) { + $this->featureContext = $environment->getContext('FeatureContext'); + } } } diff --git a/tests/acceptance/features/bootstrap/GroupfoldersContext.php b/tests/acceptance/features/bootstrap/GroupfoldersContext.php index 9dad408f7..c112ae72c 100644 --- a/tests/acceptance/features/bootstrap/GroupfoldersContext.php +++ b/tests/acceptance/features/bootstrap/GroupfoldersContext.php @@ -1,6 +1,7 @@ getEnvironment(); // Get all the contexts you need in this context - $this->featureContext = $environment->getContext('FeatureContext'); + if ($environment instanceof InitializedContextEnvironment) { + $this->featureContext = $environment->getContext('FeatureContext'); + } } /** diff --git a/tests/acceptance/features/bootstrap/SharingContext.php b/tests/acceptance/features/bootstrap/SharingContext.php index e138a723e..e206f3179 100644 --- a/tests/acceptance/features/bootstrap/SharingContext.php +++ b/tests/acceptance/features/bootstrap/SharingContext.php @@ -1,6 +1,7 @@ getEnvironment(); // Get all the contexts you need in this context - $this->featureContext = $environment->getContext('FeatureContext'); + if ($environment instanceof InitializedContextEnvironment) { + $this->featureContext = $environment->getContext('FeatureContext'); + } } } diff --git a/tests/lib/Controller/ConfigControllerTest.php b/tests/lib/Controller/ConfigControllerTest.php index 6c14ceef6..634e4a661 100644 --- a/tests/lib/Controller/ConfigControllerTest.php +++ b/tests/lib/Controller/ConfigControllerTest.php @@ -450,9 +450,7 @@ public function testOauthNoAccessTokenInResponse($oauthResponse, $expectedErrorM ['testUser', 'integration_openproject', 'oauth_connection_result', 'error'], ['testUser', 'integration_openproject', 'oauth_connection_error_message', $expectedErrorMessage], ); - /** - * @var ConfigController - */ + $configController = new ConfigController( 'integration_openproject', $this->createMock(IRequest::class), @@ -838,7 +836,7 @@ public function checkForUsersCountBeforeTest($expectedCount = 1): IUserManager { $actualCount = 1; $userManager = \OC::$server->getUserManager(); $count = 0; - $function = function (IUser $user) use (&$count) { + $function = function () use (&$count) { $count++; return null; }; diff --git a/tests/lib/Controller/DirectUploadControllerTest.php b/tests/lib/Controller/DirectUploadControllerTest.php index df6fc04dc..b020412a2 100644 --- a/tests/lib/Controller/DirectUploadControllerTest.php +++ b/tests/lib/Controller/DirectUploadControllerTest.php @@ -181,7 +181,7 @@ public function testDirectUploadFileNotUploaded(string $tmpName, int $error):voi } /** - * @return array> + * @return array> */ public function newFileExceptionsDataProvider() { return [ diff --git a/tests/lib/Controller/FilesControllerTest.php b/tests/lib/Controller/FilesControllerTest.php index 89f66f84b..5c72182e2 100644 --- a/tests/lib/Controller/FilesControllerTest.php +++ b/tests/lib/Controller/FilesControllerTest.php @@ -883,7 +883,7 @@ public function getFilesControllerMock( MockObject $folderMock, MockObject $mountCacheMock = null, MockObject $davUtilsMock = null - ): FilesController { + ): FilesController|MockObject { $storageMock = $this->getMockBuilder('\OCP\Files\IRootFolder')->getMock(); $storageMock->method('getUserFolder')->willReturn($folderMock); diff --git a/tests/lib/Controller/OpenProjectAPIControllerTest.php b/tests/lib/Controller/OpenProjectAPIControllerTest.php index 3e7179a2a..8b1f1c066 100644 --- a/tests/lib/Controller/OpenProjectAPIControllerTest.php +++ b/tests/lib/Controller/OpenProjectAPIControllerTest.php @@ -31,10 +31,10 @@ use Psr\Log\LoggerInterface; class OpenProjectAPIControllerTest extends TestCase { - /** @var IConfig $configMock */ + /** @var IConfig */ private $configMock; - /** @var IRequest $requestMock */ + /** @var IRequest */ private $requestMock; /** @@ -79,9 +79,10 @@ public function setUpMocks(): void { /** * @param string $token + * @psalm-suppress UndefinedInterfaceMethod * @return void */ - public function getUserValueMock($token = '123') { + public function getUserValueMock(string $token = '123'): void { $this->configMock ->method('getUserValue') ->withConsecutive( @@ -93,7 +94,7 @@ public function getUserValueMock($token = '123') { /** * @return void */ - public function testGetNotifications() { + public function testGetNotifications(): void { $this->getUserValueMock(); $service = $this->getMockBuilder(OpenProjectAPIService::class) ->disableOriginalConstructor() diff --git a/tests/lib/Reference/WorkPackageReferenceProviderTest.php b/tests/lib/Reference/WorkPackageReferenceProviderTest.php index c7cfa16d3..1e8c89fc8 100644 --- a/tests/lib/Reference/WorkPackageReferenceProviderTest.php +++ b/tests/lib/Reference/WorkPackageReferenceProviderTest.php @@ -53,7 +53,7 @@ public function getWorkReferenceProviderMock( $refrenceMangager = null, $openProjectAPIService = null, $userId = null - ): WorkPackageReferenceProvider { + ): WorkPackageReferenceProvider|MockObject { if ($configMock === null) { $configMock = $this->createMock(IConfig::class); } diff --git a/tests/lib/Service/OpenProjectAPIServiceTest.php b/tests/lib/Service/OpenProjectAPIServiceTest.php index 50f0d5906..584c45109 100644 --- a/tests/lib/Service/OpenProjectAPIServiceTest.php +++ b/tests/lib/Service/OpenProjectAPIServiceTest.php @@ -635,7 +635,6 @@ private function getOpenProjectAPIService( ) { $certificateManager = $this->getMockBuilder('\OCP\ICertificateManager')->getMock(); $certificateManager->method('getAbsoluteBundlePath')->willReturn('/'); - $ocClient = null; $client = new GuzzleClient(); $clientConfigMock = $this->getMockBuilder(IConfig::class)->getMock(); $clientConfigMock @@ -781,7 +780,7 @@ private function getServiceMock( $db = null, $iLogFactory = null, $iURLGenerator = null - ): OpenProjectAPIService { + ): OpenProjectAPIService|MockObject { $onlyMethods[] = 'getBaseUrl'; if ($rootMock === null) { $rootMock = $this->createMock(IRootFolder::class); @@ -3003,10 +3002,9 @@ public function testMarkAllNotificationsOfANotExistingWorkPackageAsReadPact(): v ->with($consumerRequest) ->willRespondWith($providerResponse); - $service = $this->getOpenProjectAPIService(); $this->expectException(OpenprojectErrorException::class); - $result = $service->markAllNotificationsOfWorkPackageAsRead( + $service->markAllNotificationsOfWorkPackageAsRead( 789, 'testUser' ); @@ -3892,7 +3890,7 @@ public function auditLogDataProvider(): array { * @param int $logLevel * @param string $pathToAuditLog * @param array $logCondition - * @param string $isAdminAuditAppInstalled + * @param bool $isAdminAuditAppInstalled * @param bool $expectedResult * * @return void diff --git a/tests/stub.phpstub b/tests/stub/doctrine_cacheItem.phpstub similarity index 100% rename from tests/stub.phpstub rename to tests/stub/doctrine_cacheItem.phpstub diff --git a/tests/stub/timejob_joblist.phpstub b/tests/stub/timejob_joblist.phpstub new file mode 100644 index 000000000..e138652b0 --- /dev/null +++ b/tests/stub/timejob_joblist.phpstub @@ -0,0 +1,61 @@ +// The 'TimedJob' class uses 'JobList' in 'execute()'', but the `IJob` interface expects `IJobList`. +// so we got the following error: +// +// ERROR: MoreSpecificImplementedParamType - ../../lib/public/BackgroundJob/TimedJob.php:92:32 - +// Argument 1 of OCP\BackgroundJob\TimedJob::execute has the more specific type 'OC\BackgroundJob\JobList', +// expecting 'OCP\BackgroundJob\IJobList' as defined by OCP\BackgroundJob\IJob::execute (see https://psalm.dev/140) +// final public function execute($jobList, ILogger $logger = null) { +// +// The stub changes the type to `IJobList` to match the interface and prevent the error, +// without changing the original code. + + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\BackgroundJob; + +use OC\BackgroundJob\JobList; +use OCP\ILogger; + +class TimedJob extends Job { + + public function setInterval(int $seconds) + { + } + + public function isTimeSensitive(): bool + { + } + + public function setTimeSensitivity(int $sensitivity): void + { + } + + final public function execute($jobList, ILogger $logger = null) + { + } + + final public function start(IJobList $jobList): void + { + } +}