From 3893c1e6251f89460730c730acbfd0a4569e6f00 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 22 Jul 2015 22:33:00 +0200 Subject: [PATCH 01/50] Makes links in the CHANGELOG be actual links. --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c386634..0ee9369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and [Semantic Versioning](http://semver.org/) conventions. ### Security --> -## v0.2.0 - 2015-07-21 - Improvements and UnitTests +## [v0.2.0] - 2015-07-21 - Improvements and UnitTests ### Added - Adds automated checks (a.k.a. unit-tests) for the Adapter, Client and Settings classes. @@ -23,7 +23,7 @@ and [Semantic Versioning](http://semver.org/) conventions. - Makes the PHPUnit configuration more strict - Renames the Client class to "Api" -## v0.1.0 - 2015-07-18 - Read functionality +## [v0.1.0] - 2015-07-18 - Read functionality ### Added - Read functionality and Github API authentication have been implemented. @@ -36,7 +36,7 @@ Contributing guidelines, Composer file stating dependencies, MIT License, README file and this CHANGELOG file. [unreleased]: https://github.com/potherca/flystystem-github/compare/v0.2.0...HEAD -[0.2.0]: https://github.com/potherca/flystystem-github/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/potherca/flystystem-github/compare/v0.0.0...v0.1.0 +[v0.2.0]: https://github.com/potherca/flystystem-github/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/potherca/flystystem-github/compare/v0.0.0...v0.1.0 [keep-a-changelog]: http://keepachangelog.com/ [Semantic Versioning]: http://semver.org/ From 836d37ed793b8bac529385803b9f0618bc67f1c1 Mon Sep 17 00:00:00 2001 From: Arjan Date: Thu, 23 Jul 2015 07:25:35 +0200 Subject: [PATCH 02/50] Improves check for valid repository names - Adds test for repository names with a single slash that is also a trailing slash - Adds test for repository names with two consecutive slashes - Updates check that name is valid by not allowing names with a single trailing slash --- src/Settings.php | 4 ++-- tests/SettingsTest.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Settings.php b/src/Settings.php index 8ea14b1..c10a080 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -101,9 +101,9 @@ final public function __construct( private function isValidRepositoryName($repository) { if (is_string($repository) === false - || strpos($repository, '/') === false - || strpos($repository, '/') === 0 || substr_count($repository, '/') !== 1 + || substr($repository, 0, 1) === '/' + || substr($repository, -1, 1) === '/' ) { $message = sprintf( self::ERROR_INVALID_REPOSITORY_NAME, diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php index bd46a24..305fd21 100644 --- a/tests/SettingsTest.php +++ b/tests/SettingsTest.php @@ -226,6 +226,8 @@ final public function provideInvalidRepositoryNames() [array()], ['foo'], ['/foo'], + ['foo/'], + ['foo//bar'], ['foo/bar/'], ['/foo/bar/'], ['foo/bar/baz'], From 2256c6718cd7a875b91b7af61827b89ec551aacb Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Thu, 5 Nov 2015 17:38:41 +0100 Subject: [PATCH 03/50] Adds code for CodeCov to Travis config. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 093a889..90567ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,5 +19,6 @@ install: script: - vendor/bin/phpunit --configuration build/phpunit.xml && cat build/testdox.txt build/coverage.txt -after_script: +after_success: + - bash <(curl -s https://codecov.io/bash) - php vendor/bin/coveralls -v From d06fc92bb7745f3ca82ff4f6d7e6b0a6111e679d Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Feb 2016 19:11:06 +0100 Subject: [PATCH 04/50] Makes dynamic PHPUnit calls static for static methods. --- tests/ApiTest.php | 72 +++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index f57ecf0..28fc59e 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -53,10 +53,8 @@ final public function testApiShouldComplainWhenInstantiatedWithoutClient() Client::class ); - $this->setExpectedException( - \PHPUnit_Framework_Error::class, - $message - ); + $this->expectException(\PHPUnit_Framework_Error::class); + $this->expectExceptionMessage($message); /** @noinspection PhpParamsInspection */ new Api(); @@ -74,10 +72,8 @@ final public function testApiShouldComplainWhenInstantiatedWithoutSettings() SettingsInterface::class ); - $this->setExpectedException( - \PHPUnit_Framework_Error::class, - $message - ); + $this->expectException(\PHPUnit_Framework_Error::class); + $this->expectExceptionMessage($message); /** @noinspection PhpParamsInspection */ new Api($this->getMockClient()); @@ -111,7 +107,7 @@ final public function testApiShouldUseValuesFromSettingsWhenAskingClientForFileC $actual = $api->getFileContents(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -142,7 +138,7 @@ final public function testApiShouldUseValuesFromSettingsWhenAskingClientIfFileEx $actual = $api->exists(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -158,7 +154,7 @@ final public function testApiShouldUseValuesFromSettingsWhenAskingClientForLastU $actual = $api->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -174,8 +170,9 @@ final public function testApiShouldUseValuesFromSettingsWhenAskingClientForCreat $actual = $api->getCreatedTimestamp(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } + /** * @covers ::getMetaData */ @@ -204,7 +201,7 @@ final public function testApiShouldUseValuesFromSettingsWhenAskingClientForFileI $actual = $api->getMetaData(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -216,13 +213,13 @@ final public function testApiShouldAccountForFileNotExistingWhenAskingInfoForFil $expected = false; - $this->mockClient->expects($this->exactly(1)) + $this->mockClient->expects(self::exactly(1)) ->method('api') ->willThrowException(new RuntimeException(Api::ERROR_NOT_FOUND)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -232,17 +229,18 @@ final public function testApiShouldPassOtherRuntimeExceptionsWhenAskingInfoForFi { $api = $this->api; - $this->setExpectedException(RuntimeException::class, self::MOCK_FILE_CONTENTS); + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage(self::MOCK_FILE_CONTENTS); $expected = false; - $this->mockClient->expects($this->exactly(1)) + $this->mockClient->expects(self::exactly(1)) ->method('api') ->willThrowException(new RuntimeException(self::MOCK_FILE_CONTENTS)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -252,17 +250,18 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn { $api = $this->api; - $this->setExpectedException(\RuntimeException::class, Api::ERROR_NOT_FOUND); + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage(Api::ERROR_NOT_FOUND); $expected = false; - $this->mockClient->expects($this->exactly(1)) + $this->mockClient->expects(self::exactly(1)) ->method('api') ->willThrowException(new \RuntimeException(Api::ERROR_NOT_FOUND)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -275,7 +274,7 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn * @param bool $recursive * @param bool $truncated */ - final public function testApiShouldRetrieveExpectedMetadataWhenAskedTogetRecursiveMetadata( + final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursiveMetadata( $path, $expected, $recursive, @@ -303,7 +302,7 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedTogetRecursi $actual = $api->getRecursiveMetadata($path, $recursive); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -317,11 +316,11 @@ final public function testApiShouldUseFileExtensionToGuessMimeTypeWhenExtensionI $expected = 'image/png'; - $this->mockClient->expects($this->never())->method('api'); + $this->mockClient->expects(self::never())->method('api'); $actual = $api->guessMimeType(self::MOCK_FILE_PATH.'.png'); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -363,7 +362,7 @@ final public function testApiShouldUseFileContentsToGuessMimeTypeWhenExtensionUn $actual = $api->guessMimeType(self::MOCK_FILE_PATH); - $this->assertEquals($expected, $actual); + self::assertEquals($expected, $actual); } /** @@ -391,7 +390,7 @@ final public function testApiShouldUseCredentialsWhenTheyHaveBeenGiven() '' ); - $this->mockClient->expects($this->exactly(1)) + $this->mockClient->expects(self::exactly(1)) ->method('authenticate') ; @@ -425,8 +424,13 @@ private function getMockSettings() * @param mixed $apiOutput * @param string $repositoryClass */ - private function prepareMockApi($method, $apiName, $apiParameters, $apiOutput, $repositoryClass = Contents::class) - { + private function prepareMockApi( + $method, + $apiName, + $apiParameters, + $apiOutput, + $repositoryClass = Contents::class + ) { $parts = explode('\\', $repositoryClass); $repositoryName = strtolower(array_pop($parts)); @@ -441,21 +445,21 @@ private function prepareMockApi($method, $apiName, $apiParameters, $apiOutput, $ ->getMock() ; - $mockRepository->expects($this->exactly(1)) + $mockRepository->expects(self::exactly(1)) ->method($method) ->withAnyParameters() ->willReturnCallback(function () use ($apiParameters, $apiOutput) { - $this->assertEquals($apiParameters, func_get_args()); + self::assertEquals($apiParameters, func_get_args()); return $apiOutput; }) ; - $mockApi->expects($this->exactly(1)) + $mockApi->expects(self::exactly(1)) ->method($repositoryName) ->willReturn($mockRepository) ; - $this->mockClient->expects($this->exactly(1)) + $this->mockClient->expects(self::exactly(1)) ->method('api') ->with($apiName) ->willReturn($mockApi) @@ -468,7 +472,7 @@ private function prepareMockApi($method, $apiName, $apiParameters, $apiOutput, $ private function prepareMockSettings(array $expectations) { foreach ($expectations as $methodName => $returnValue) { - $this->mockSettings->expects($this->exactly(1)) + $this->mockSettings->expects(self::exactly(1)) ->method($methodName) ->willReturn($returnValue) ; From 85a82289279887630b8e5b07bbab0c3241fe6434 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Feb 2016 19:11:42 +0100 Subject: [PATCH 05/50] Updates PHPUnit to 5.2. --- build/phpunit.xml | 10 ++++++++-- composer.json | 2 +- phpunit.xml.dist | 9 +++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/build/phpunit.xml b/build/phpunit.xml index df9f636..5b7a9c4 100644 --- a/build/phpunit.xml +++ b/build/phpunit.xml @@ -1,14 +1,20 @@ Date: Fri, 19 Feb 2016 19:12:01 +0100 Subject: [PATCH 06/50] Fixes broken links in CHANGELOG.md. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee9369..f3210a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,8 +35,8 @@ and [Semantic Versioning](http://semver.org/) conventions. Contributing guidelines, Composer file stating dependencies, MIT License, README file and this CHANGELOG file. -[unreleased]: https://github.com/potherca/flystystem-github/compare/v0.2.0...HEAD -[v0.2.0]: https://github.com/potherca/flystystem-github/compare/v0.1.0...v0.2.0 -[v0.1.0]: https://github.com/potherca/flystystem-github/compare/v0.0.0...v0.1.0 +[unreleased]: https://github.com/potherca/flysystem-github/compare/v0.2.0...HEAD +[v0.2.0]: https://github.com/potherca/flysystem-github/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/potherca/flysystem-github/compare/v0.0.0...v0.1.0 [keep-a-changelog]: http://keepachangelog.com/ [Semantic Versioning]: http://semver.org/ From e4205f87e8eaf2d3f8b673b7b8330a67114d0fe7 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Feb 2016 20:22:26 +0100 Subject: [PATCH 07/50] Adds named keys to data provider in ApiTest. --- tests/ApiTest.php | 145 ++++++++++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 28fc59e..92ff6f5 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -269,17 +269,9 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn * * @dataProvider provideExpectedMetadata * - * @param string $path - * @param array $expected - * @param bool $recursive - * @param bool $truncated + * @param array $data */ - final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursiveMetadata( - $path, - $expected, - $recursive, - $truncated - ) { + final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursiveMetadata($data) { $api = $this->api; $mockVendor = 'vendor'; @@ -295,14 +287,14 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi $this->prepareMockApi( 'show', $api::API_GIT_DATA, - [$mockVendor, $mockPackage, $mockReference, $recursive], - $this->getMockApiTreeResponse($truncated, $api), + [$mockVendor, $mockPackage, $mockReference, $data['recursive']], + $this->getMockApiTreeResponse($data['truncated'], $api), Trees::class ); - $actual = $api->getRecursiveMetadata($path, $recursive); + $actual = $api->getRecursiveMetadata($data['path'], $data['recursive']); - self::assertEquals($expected, $actual); + self::assertEquals($data['expected'], $actual); } /** @@ -565,9 +557,9 @@ private function prepareFixturesForTimeStamp() final public function provideExpectedMetadata() { return [ - 'Filepath, not recursive, not truncated' => [ - self::MOCK_FILE_PATH, - [ + 'Filepath, not recursive, not truncated' => [[ + 'path' => self::MOCK_FILE_PATH, + 'expected' => [ [ 'path' => '/path/to/mock/file', 'mode' => 100644, @@ -592,12 +584,12 @@ final public function provideExpectedMetadata() 'visibility' => 'public' ] ], - false, - false - ], - 'Filepath, recursive, not truncated' => [ - self::MOCK_FILE_PATH, - [ + 'recursive' => false, + 'truncated' => false + ]], + 'Filepath, recursive, not truncated' => [[ + 'path' => self::MOCK_FILE_PATH, + 'expected' => [ [ 'path' => '/path/to/mock/file', 'mode' => 100644, @@ -633,12 +625,12 @@ final public function provideExpectedMetadata() 'visibility' => 'public' ] ], - true, - false - ], - 'Filepath, not recursive, truncated' => [ - self::MOCK_FILE_PATH, - [ + 'recursive' => true, + 'truncated' => false + ]], + 'Filepath, not recursive, truncated' => [[ + 'path' => self::MOCK_FILE_PATH, + 'expected' => [ [ 'path' => '/path/to/mock/file', 'mode' => 100644, @@ -663,12 +655,53 @@ final public function provideExpectedMetadata() 'visibility' => 'public' ] ], - false, - true - ], - 'No Filepath, recursive, not truncated' => [ - '', - [ + 'recursive' => false, + 'truncated' => true + ]], + 'Filepath, recursive, truncated' => [[ + 'path' => self::MOCK_FILE_PATH, + 'expected' => [ + [ + 'path' => '/path/to/mock/file', + 'mode' => 100644, + 'type' => 'dir', + 'size' => 57, + 'name' => '/path/to/mock/file', + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => 'public' + ], + [ + 'path' => '/path/to/mock/fileFoo', + 'basename' => '/path/to/mock/fileFoo', + 'mode' => 100644, + 'type' => 'file', + 'size' => 57, + 'name' => '/path/to/mock/fileFoo', + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => 'public' + ], + [ + 'path' => '/path/to/mock/file/Bar', + 'mode' => 100644, + 'type' => 'file', + 'size' => 57, + 'name' => '/path/to/mock/file/Bar', + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => 'public' + ] + ], + 'recursive' => true, + 'truncated' => true + ]], + 'No Filepath, recursive, not truncated' => [[ + 'path' => '', + 'expected' => [ [ 'path' => '/path/to/mock/file', 'mode' => 100644, @@ -715,12 +748,12 @@ final public function provideExpectedMetadata() 'visibility' => 'public' ] ], - true, - false - ], - 'No Filepath, recursive, truncated' => [ - '', - [ + 'recursive' => true, + 'truncated' => false + ]], + 'No Filepath, recursive, truncated' => [[ + 'path' => '', + 'expected' => [ [ 'path' => '/path/to/mock/file', 'mode' => 100644, @@ -767,12 +800,12 @@ final public function provideExpectedMetadata() 'visibility' => 'public' ] ], - true, - true - ], - 'No Filepath, not recursive, truncated' => [ - '', - [ + 'recursive' => true, + 'truncated' => true + ]], + 'No Filepath, not recursive, truncated' => [[ + 'path' => '', + 'expected' => [ [ 'name' => null, 'visibility' => null, @@ -781,12 +814,12 @@ final public function provideExpectedMetadata() 'timestamp' => null ] ], - false, - true - ], - 'No Filepath, not recursive, not truncated' => [ - '', - [ + 'recursive' => false, + 'truncated' => true + ]], + 'No Filepath, not recursive, not truncated' => [[ + 'path' => '', + 'expected' => [ [ 'name' => null, 'visibility' => null, @@ -795,9 +828,9 @@ final public function provideExpectedMetadata() 'timestamp' => null, ] ], - false, - false - ], + 'recursive' => false, + 'truncated' => false + ]], ]; } } From 3090c5ab15d98b4c71db655ab1e24c3cbdfccaef Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Thu, 24 Mar 2016 16:00:49 -0400 Subject: [PATCH 08/50] Changes required PHP version to 5.4 for production as 5.5 syntax only applies to testing. --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index aa5c813..2078e24 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,13 @@ } ], "require": { - "php" : ">=5.5", + "php" : ">=5.4", "knplabs/github-api": "^1.4", "league/flysystem": "^1.0" }, "require-dev": { - "phpunit/phpunit" : "^5.2.0", + "php" : ">=5.5", + "phpunit/phpunit" : "^4.7.7", "satooshi/php-coveralls": "^0.6.1", "scrutinizer/ocular": "^1.1", "whatthejeff/nyancat-phpunit-resultprinter": "^1.2" From 6a67a476b8a277aaf3753cda7fa7b79316c8318f Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 16:32:41 +0200 Subject: [PATCH 09/50] Fixes calls in ApiTest to be PHPUnit 4 compatible. --- tests/ApiTest.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 92ff6f5..c9f70ce 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -53,8 +53,7 @@ final public function testApiShouldComplainWhenInstantiatedWithoutClient() Client::class ); - $this->expectException(\PHPUnit_Framework_Error::class); - $this->expectExceptionMessage($message); + $this->setExpectedException(\PHPUnit_Framework_Error::class, $message); /** @noinspection PhpParamsInspection */ new Api(); @@ -72,8 +71,7 @@ final public function testApiShouldComplainWhenInstantiatedWithoutSettings() SettingsInterface::class ); - $this->expectException(\PHPUnit_Framework_Error::class); - $this->expectExceptionMessage($message); + $this->setExpectedException(\PHPUnit_Framework_Error::class, $message); /** @noinspection PhpParamsInspection */ new Api($this->getMockClient()); @@ -229,8 +227,7 @@ final public function testApiShouldPassOtherRuntimeExceptionsWhenAskingInfoForFi { $api = $this->api; - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage(self::MOCK_FILE_CONTENTS); + $this->setExpectedException(RuntimeException::class, self::MOCK_FILE_CONTENTS); $expected = false; @@ -250,8 +247,7 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn { $api = $this->api; - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage(Api::ERROR_NOT_FOUND); + $this->setExpectedException(\RuntimeException::class, Api::ERROR_NOT_FOUND); $expected = false; From 1f81a3f2daeb52161b32bb35190d72f95981d264 Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Thu, 24 Mar 2016 11:11:38 -0400 Subject: [PATCH 10/50] Tree filter assumes incoming path ends in a slash, so add it if it's missing --- src/Api.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index c5fbbef..42f7903 100644 --- a/src/Api.php +++ b/src/Api.php @@ -255,7 +255,8 @@ private function authenticate() */ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) { - if(empty($path) === false) { + if (empty($path) === false) { + $path = rtrim($path, '/') . '/'; $metadata = array_filter($tree, function ($entry) use ($path, $recursive) { $match = false; From 06c605919da878483f06e47c62dbc3d02fdf5022 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 19:56:34 +0200 Subject: [PATCH 11/50] Changes Api::extractMetaDataFromTreeInfo() to be more readable. --- src/Api.php | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Api.php b/src/Api.php index 42f7903..8fc9579 100644 --- a/src/Api.php +++ b/src/Api.php @@ -197,6 +197,8 @@ final public function getRecursiveMetadata($path, $recursive) $recursive ); + $path = rtrim($path, '/') . '/'; + $treeMetadata = $this->extractMetaDataFromTreeInfo($info[self::KEY_TREE], $path, $recursive); return $this->normalizeTreeMetadata($treeMetadata); @@ -255,29 +257,24 @@ private function authenticate() */ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) { - if (empty($path) === false) { - $path = rtrim($path, '/') . '/'; - $metadata = array_filter($tree, function ($entry) use ($path, $recursive) { - $match = false; - - if (strpos($entry[self::KEY_PATH], $path) === 0) { - if ($recursive === true) { - $match = true; - } else { - $length = strlen($path); - $match = (strpos($entry[self::KEY_PATH], '/', $length) === false); - } + $matchPath = substr($path, 0, -1); + $length = strlen($matchPath) - 1; + + $metadata = array_filter($tree, function ($entry) use ($matchPath, $recursive, $length) { + $match = false; + + $entryPath = $entry[self::KEY_PATH]; + + if ($matchPath === '' || strpos($entryPath, $matchPath) === 0) { + if ($recursive === true) { + $match = true; + } else { + $match = ($matchPath !== '' && strpos($entryPath, '/', $length) === false); } + } - return $match; - }); - } elseif ($recursive === false) { - $metadata = array_filter($tree, function ($entry) use ($path) { - return (strpos($entry[self::KEY_PATH], '/', strlen($path)) === false); - }); - } else { - $metadata = $tree; - } + return $match; + }); return $metadata; } From 1582a510147cf2479e58b6c8391cbcd90d45c6dd Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Feb 2016 19:11:42 +0100 Subject: [PATCH 12/50] Updates various development dependencies. --- composer.json | 8 ++++---- phpunit.xml.dist | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 2078e24..1dfbc83 100644 --- a/composer.json +++ b/composer.json @@ -26,10 +26,10 @@ }, "require-dev": { "php" : ">=5.5", - "phpunit/phpunit" : "^4.7.7", - "satooshi/php-coveralls": "^0.6.1", - "scrutinizer/ocular": "^1.1", - "whatthejeff/nyancat-phpunit-resultprinter": "^1.2" + "phpunit/phpunit" : "~4.8", + "satooshi/php-coveralls": "~0.6", + "scrutinizer/ocular": "~1.1", + "whatthejeff/nyancat-phpunit-resultprinter": "~1.2" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7924aeb..e41215d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,11 +1,13 @@ Date: Sun, 3 Apr 2016 21:54:39 +0200 Subject: [PATCH 13/50] Adds @throws annotations to Api class. --- src/Api.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Api.php b/src/Api.php index 8fc9579..d67c9b1 100644 --- a/src/Api.php +++ b/src/Api.php @@ -46,7 +46,10 @@ class Api implements ApiInterface //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @param string $name + * * @return \Github\Api\ApiInterface + * + * @throws \Github\Exception\InvalidArgumentException */ private function getApi($name) { @@ -56,6 +59,8 @@ private function getApi($name) /** * @return GitData + * + * @throws \Github\Exception\InvalidArgumentException */ private function getGitDataApi() { @@ -64,6 +69,8 @@ private function getGitDataApi() /** * @return Repo + * + * @throws \Github\Exception\InvalidArgumentException */ private function getRepositoryApi() { @@ -72,6 +79,8 @@ private function getRepositoryApi() /** * @return \Github\Api\Repository\Contents + * + * @throws \Github\Exception\InvalidArgumentException */ private function getRepositoryContent() { @@ -93,6 +102,8 @@ final public function __construct(Client $client, SettingsInterface $settings) * @param string $path * * @return bool + * + * @throws \Github\Exception\InvalidArgumentException */ final public function exists($path) { @@ -108,6 +119,7 @@ final public function exists($path) * @param $path * * @return null|string + * @throws \Github\Exception\InvalidArgumentException * * @throws \Github\Exception\ErrorException */ @@ -125,6 +137,8 @@ final public function getFileContents($path) * @param string $path * * @return array + * + * @throws \Github\Exception\InvalidArgumentException */ final public function getLastUpdatedTimestamp($path) { @@ -141,6 +155,8 @@ final public function getLastUpdatedTimestamp($path) * @param string $path * * @return array + * + * @throws \Github\Exception\InvalidArgumentException */ final public function getCreatedTimestamp($path) { @@ -157,6 +173,9 @@ final public function getCreatedTimestamp($path) * @param string $path * * @return array|bool + * + * @throws \Github\Exception\InvalidArgumentException + * @throws \Github\Exception\RuntimeException */ final public function getMetaData($path) { @@ -183,6 +202,7 @@ final public function getMetaData($path) * @param bool $recursive * * @return array + * @throws \Github\Exception\InvalidArgumentException */ final public function getRecursiveMetadata($path, $recursive) { @@ -208,6 +228,9 @@ final public function getRecursiveMetadata($path, $recursive) * @param string $path * * @return null|string + * + * @throws \Github\Exception\ErrorException + * @throws \Github\Exception\InvalidArgumentException */ final public function guessMimeType($path) { @@ -226,6 +249,7 @@ final public function guessMimeType($path) ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * + * @throws \Github\Exception\InvalidArgumentException If no authentication method was given */ private function authenticate() { @@ -321,6 +345,8 @@ private function normalizeTreeMetadata($metadata) * @param $path * * @return array + * + * @throws \Github\Exception\InvalidArgumentException */ private function commitsForFile($path) { From 681d262ed1dba7746f6271635c6297292fcdfc7b Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 3 Apr 2016 21:56:10 +0200 Subject: [PATCH 14/50] Splits ternary operator in Api::guessVisibility across multiple lines. --- src/Api.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index d67c9b1..6d69b07 100644 --- a/src/Api.php +++ b/src/Api.php @@ -309,7 +309,10 @@ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) */ private function guessVisibility($permissions) { - return $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE; + return ($permissions & 0044) + ? AdapterInterface::VISIBILITY_PUBLIC + : AdapterInterface::VISIBILITY_PRIVATE + ; } /** From 37e290c412f08b1b64bc9250771858c87b287abe Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 3 Apr 2016 21:57:48 +0200 Subject: [PATCH 15/50] Refactores various `isset` calls in Api::guessVisibility to be more DRY. --- src/Api.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Api.php b/src/Api.php index 6d69b07..c40a45b 100644 --- a/src/Api.php +++ b/src/Api.php @@ -372,7 +372,7 @@ private function commitsForFile($path) */ private function setDefaultValue(array &$entry, $key, $default = false) { - if (isset($entry[$key]) === false) { + if ($this->hasKey($entry, $key) === false) { $entry[$key] = $default; } } @@ -382,7 +382,7 @@ private function setDefaultValue(array &$entry, $key, $default = false) */ private function setEntryType(&$entry) { - if (isset($entry[self::KEY_TYPE]) === true) { + if ($this->hasKey($entry, self::KEY_TYPE) === true) { switch ($entry[self::KEY_TYPE]) { case self::KEY_BLOB: $entry[self::KEY_TYPE] = self::KEY_FILE; @@ -400,7 +400,7 @@ private function setEntryType(&$entry) */ private function setEntryVisibility(&$entry) { - if (isset($entry[self::KEY_MODE])) { + if ($this->hasKey($entry, self::KEY_MODE)) { $entry[self::KEY_VISIBILITY] = $this->guessVisibility($entry[self::KEY_MODE]); } else { $entry[self::KEY_VISIBILITY] = false; @@ -412,14 +412,25 @@ private function setEntryVisibility(&$entry) */ private function setEntryName(&$entry) { - if (isset($entry[self::KEY_NAME]) === false) { - if (isset($entry[self::KEY_FILENAME]) === true) { + if ($this->hasKey($entry, self::KEY_NAME) === false) { + if ($this->hasKey($entry, self::KEY_FILENAME) === true) { $entry[self::KEY_NAME] = $entry[self::KEY_FILENAME]; - } elseif (isset($entry[self::KEY_PATH]) === true) { + } elseif ($this->hasKey($entry, self::KEY_PATH) === true) { $entry[self::KEY_NAME] = $entry[self::KEY_PATH]; } else { $entry[self::KEY_NAME] = null; } } } + + /** + * @param $subject + * @param $key + * @return mixed + */ + private function hasKey(&$subject, $key) + { + /** @noinspection ReferenceMismatchInspection */ + return array_key_exists($key, $subject); + } } From f5e072d1cfc6aad4fe63ed2b76906ad2163d8ae6 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 3 Apr 2016 22:33:11 +0200 Subject: [PATCH 16/50] Fixes metadata in Api::getMetaData() if $path is a directory. --- src/Api.php | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index c40a45b..098531e 100644 --- a/src/Api.php +++ b/src/Api.php @@ -15,6 +15,7 @@ class Api implements ApiInterface { ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ + const ERROR_NO_NAME = 'Could not set name for entry'; const ERROR_NOT_FOUND = 'Not Found'; const API_GIT_DATA = 'git'; @@ -34,7 +35,9 @@ class Api implements ApiInterface const KEY_TREE = 'tree'; const KEY_TYPE = 'type'; const KEY_VISIBILITY = 'visibility'; - const ERROR_NO_NAME = 'Could not set name for entry'; + + const GITHUB_API_URL = 'https://api.github.com'; + const GITHUB_URL = 'https://github.com'; /** @var Client */ private $client; @@ -194,6 +197,25 @@ final public function getMetaData($path) } } + if (is_array($metadata) === true && $this->isMetadataForDirectory($metadata) === true) { + /** @var $metadata array */ + $project = sprintf('%s/%s', $this->settings->getVendor(), $this->settings->getPackage()); + $reference = $this->settings->getReference(); + + $url = sprintf('%s/repos/%s/contents/%s?ref=%s', self::GITHUB_API_URL, $project, $path, $reference); + $htmlUrl = sprintf('%s/%s/blob/%s/%s', self::GITHUB_URL, $project, $reference, $path); + + $metadata = [ + self::KEY_TYPE => self::KEY_DIRECTORY, + 'url' => $url, + 'html_url' => $htmlUrl, + '_links' => [ + 'self' => $url, + 'html' => $htmlUrl + ] + ]; + } + return $metadata; } @@ -423,6 +445,23 @@ private function setEntryName(&$entry) } } + /** + * @param $metadata + * @return bool + */ + private function isMetadataForDirectory($metadata) + { + $isDirectory = false; + + $keys = array_keys($metadata); + + if ($keys[0] === 0) { + $isDirectory = true; + } + + return $isDirectory; + } + /** * @param $subject * @param $key From 0e0f9b8de999f8e51c823a312654b0aee1d39b06 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:23:06 +0200 Subject: [PATCH 17/50] Fixes metadata in GithubAdapter::listContent for file - Only directories should return content, for a file an empty array should be returned. --- src/GithubAdapter.php | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/GithubAdapter.php b/src/GithubAdapter.php index f48345f..566ed3f 100644 --- a/src/GithubAdapter.php +++ b/src/GithubAdapter.php @@ -169,6 +169,8 @@ public function has($path) * @param string $path * * @return array|false + * + * @throws \Github\Exception\ErrorException */ public function read($path) { @@ -185,7 +187,13 @@ public function read($path) */ public function listContents($path = '/', $recursive = false) { - return $this->getApi()->getRecursiveMetadata($path, $recursive); + $contents = $this->getApi()->getRecursiveMetadata($path, $recursive); + + if ($this->isDirectoryContents($contents) === false) { + $contents = []; + } + + return $contents; } /** @@ -249,6 +257,23 @@ public function getVisibility($path) $metadata = $this->getApi()->getRecursiveMetadata($path, $recursive); return $metadata[0]; } + + /** + * @param $contents + * @return bool + */ + private function isDirectoryContents($contents) + { + $isDirectory = false; + + if (is_array($contents)) { + $isDirectory = array_key_exists(Api::KEY_TYPE, $contents) === false + || $contents[Api::KEY_TYPE] === Api::KEY_DIRECTORY + ; + } + + return $isDirectory; + } } /*EOF*/ From 68a69024dca74b65c1edf671abbdfa038653e124 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:26:44 +0200 Subject: [PATCH 18/50] Changes Api::guessMimeType to always use file content when guessing - Adds specific check for directories - Removes guessing by extension as this gives a different result from regular filesystem behaviour --- src/Api.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Api.php b/src/Api.php index 098531e..f0c4509 100644 --- a/src/Api.php +++ b/src/Api.php @@ -253,13 +253,15 @@ final public function getRecursiveMetadata($path, $recursive) * * @throws \Github\Exception\ErrorException * @throws \Github\Exception\InvalidArgumentException + * @throws \Github\Exception\RuntimeException */ final public function guessMimeType($path) { //@NOTE: The github API does not return a MIME type, so we have to guess :-( - if (strrpos($path, '.') > 1) { - $extension = substr($path, strrpos($path, '.')+1); - $mimeType = MimeType::detectByFileExtension($extension) ?: 'text/plain'; + $meta = $this->getMetaData($path); + + if ($this->hasKey($meta, self::KEY_TYPE) && $meta[self::KEY_TYPE] === self::KEY_DIRECTORY) { + $mimeType = 'directory'; //application/x-directory } else { $content = $this->getFileContents($path); $mimeType = MimeType::detectByContent($content); From f23d01cfd0d8a8d9b997b12b382be9682918a85d Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:29:34 +0200 Subject: [PATCH 19/50] Fixes bug Api::getRecursiveMetadata results - The original logic did not properly display all directory content for non-recursive cases when a path was provided. --- src/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index f0c4509..8ff56d7 100644 --- a/src/Api.php +++ b/src/Api.php @@ -324,7 +324,7 @@ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) return $match; }); - return $metadata; + return array_values($metadata); } /** From 20a7a45e4836af751fdca1aed004fe8e3a65843e Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:32:28 +0200 Subject: [PATCH 20/50] Fixes issue with file visibility in the Api class - Visibility is now assumed to be visible by default - Bug in permission comparison is fixed --- src/Api.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Api.php b/src/Api.php index 8ff56d7..c1c11f4 100644 --- a/src/Api.php +++ b/src/Api.php @@ -333,10 +333,13 @@ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) */ private function guessVisibility($permissions) { - return ($permissions & 0044) - ? AdapterInterface::VISIBILITY_PUBLIC - : AdapterInterface::VISIBILITY_PRIVATE - ; + $visibility = AdapterInterface::VISIBILITY_PUBLIC; + + if (! substr($permissions, -4) & 0044) { + $visibility = AdapterInterface::VISIBILITY_PRIVATE; + } + + return $visibility; } /** @@ -427,7 +430,8 @@ private function setEntryVisibility(&$entry) if ($this->hasKey($entry, self::KEY_MODE)) { $entry[self::KEY_VISIBILITY] = $this->guessVisibility($entry[self::KEY_MODE]); } else { - $entry[self::KEY_VISIBILITY] = false; + /* Assume public by default */ + $entry[self::KEY_VISIBILITY] = GithubAdapter::VISIBILITY_PUBLIC; } } From 3d5b12e295494ce91ef4b8e6024feb4f0009653b Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:33:19 +0200 Subject: [PATCH 21/50] Changes check for array keys in the Api class to be more robust. --- src/Api.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index c1c11f4..0973d6c 100644 --- a/src/Api.php +++ b/src/Api.php @@ -475,7 +475,13 @@ private function isMetadataForDirectory($metadata) */ private function hasKey(&$subject, $key) { + $keyExists = false; + + if (is_array($subject)) { /** @noinspection ReferenceMismatchInspection */ - return array_key_exists($key, $subject); + $keyExists = array_key_exists($key, $subject); + } + + return $keyExists; } } From fa7d954ed64ecf4b1f3ac64b30d30f0ae0b349cb Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 4 Apr 2016 22:36:01 +0200 Subject: [PATCH 22/50] Changes Api::getRecursiveMetadata() to always add timestamps - Timestamps for files are retrieved from the github api - Timestamps for directories are calculated from retrieved files --- src/Api.php | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Api.php b/src/Api.php index 0973d6c..7db5628 100644 --- a/src/Api.php +++ b/src/Api.php @@ -229,21 +229,43 @@ final public function getMetaData($path) final public function getRecursiveMetadata($path, $recursive) { // If $info['truncated'] is `true`, the number of items in the tree array - // exceeded the github maximum limit. If you need to fetch more items, + // exceeded the github maximum limit. If we need to fetch more items, // multiple calls will be needed $info = $this->getGitDataApi()->trees()->show( $this->settings->getVendor(), $this->settings->getPackage(), $this->settings->getReference(), - $recursive + true //@NOTE: To retrieve all needed date the 'recursive' flag should always be 'true' ); $path = rtrim($path, '/') . '/'; $treeMetadata = $this->extractMetaDataFromTreeInfo($info[self::KEY_TREE], $path, $recursive); - return $this->normalizeTreeMetadata($treeMetadata); + $normalizeTreeMetadata = $this->normalizeTreeMetadata($treeMetadata); + + $directoryTimestamp = 0000000000; + + array_walk($normalizeTreeMetadata, function (&$entry) use (&$directoryTimestamp) { + if ($this->hasKey($entry, self::KEY_TIMESTAMP) === false + || $entry[self::KEY_TIMESTAMP] === false + ) { + $timestamp = $this->getCreatedTimestamp($entry[self::KEY_PATH])['timestamp']; + + $entry[self::KEY_TIMESTAMP] = $timestamp; + + if ($timestamp > $directoryTimestamp) { + $directoryTimestamp = $timestamp; + } + } + }); + + /* @FIXME: It might be wise to use a filter to find the right entry instead of ussing it will always be the first entry in the array. */ + + $normalizeTreeMetadata[0]['timestamp'] = $directoryTimestamp; + + return $normalizeTreeMetadata; } /** From c31dd10367bd696605847083b33be3c77c1e78e9 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 10:52:45 +0200 Subject: [PATCH 23/50] Moves unit-tests to separate sub-folder. --- phpunit.xml.dist | 4 ++-- tests/{ => unit-tests}/ApiTest.php | 0 tests/{ => unit-tests}/GithubAdapterTest.php | 0 tests/{ => unit-tests}/SettingsTest.php | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename tests/{ => unit-tests}/ApiTest.php (100%) rename tests/{ => unit-tests}/GithubAdapterTest.php (100%) rename tests/{ => unit-tests}/SettingsTest.php (100%) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e41215d..f04bd2b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -26,8 +26,8 @@ printerClass="NyanCat\PHPUnit\ResultPrinter" > - - tests + + tests/unit-tests diff --git a/tests/ApiTest.php b/tests/unit-tests/ApiTest.php similarity index 100% rename from tests/ApiTest.php rename to tests/unit-tests/ApiTest.php diff --git a/tests/GithubAdapterTest.php b/tests/unit-tests/GithubAdapterTest.php similarity index 100% rename from tests/GithubAdapterTest.php rename to tests/unit-tests/GithubAdapterTest.php diff --git a/tests/SettingsTest.php b/tests/unit-tests/SettingsTest.php similarity index 100% rename from tests/SettingsTest.php rename to tests/unit-tests/SettingsTest.php From cd72e41619d0f8935d4d12f959feeaa53b8d4b5b Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 10:57:07 +0200 Subject: [PATCH 24/50] Changes ApiTest to be more readable - Uses shorter alias for MockObject instead of FQN - Adds trailing comma to entries in data provider - Removes test that is no longer relevant --- tests/unit-tests/ApiTest.php | 53 ++++++++++++------------------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index c9f70ce..8452f39 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -8,6 +8,7 @@ use Github\Api\Repository\Contents; use Github\Client; use Github\Exception\RuntimeException; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Tests for the Api class @@ -24,9 +25,9 @@ class ApiTest extends \PHPUnit_Framework_TestCase /** @var Api */ private $api; - /** @var Client|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Client|MockObject */ private $mockClient; - /** @var Settings|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Settings|MockObject */ private $mockSettings; /** @@ -212,8 +213,8 @@ final public function testApiShouldAccountForFileNotExistingWhenAskingInfoForFil $expected = false; $this->mockClient->expects(self::exactly(1)) - ->method('api') - ->willThrowException(new RuntimeException(Api::ERROR_NOT_FOUND)); + ->method('api') + ->willThrowException(new RuntimeException(Api::ERROR_NOT_FOUND)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); @@ -232,8 +233,8 @@ final public function testApiShouldPassOtherRuntimeExceptionsWhenAskingInfoForFi $expected = false; $this->mockClient->expects(self::exactly(1)) - ->method('api') - ->willThrowException(new RuntimeException(self::MOCK_FILE_CONTENTS)); + ->method('api') + ->willThrowException(new RuntimeException(self::MOCK_FILE_CONTENTS)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); @@ -252,8 +253,8 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn $expected = false; $this->mockClient->expects(self::exactly(1)) - ->method('api') - ->willThrowException(new \RuntimeException(Api::ERROR_NOT_FOUND)); + ->method('api') + ->willThrowException(new \RuntimeException(Api::ERROR_NOT_FOUND)); $actual = $api->getMetaData(self::MOCK_FILE_PATH); @@ -293,24 +294,6 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi self::assertEquals($data['expected'], $actual); } - /** - * @covers ::guessMimeType - * - * @uses League\Flysystem\Util\MimeType - */ - final public function testApiShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() - { - $api = $this->api; - - $expected = 'image/png'; - - $this->mockClient->expects(self::never())->method('api'); - - $actual = $api->guessMimeType(self::MOCK_FILE_PATH.'.png'); - - self::assertEquals($expected, $actual); - } - /** * @covers ::guessMimeType * @@ -387,7 +370,7 @@ final public function testApiShouldUseCredentialsWhenTheyHaveBeenGiven() ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** - * @return Client|\PHPUnit_Framework_MockObject_MockObject + * @return Client|MockObject */ private function getMockClient() { @@ -397,7 +380,7 @@ private function getMockClient() } /** - * @return Settings|\PHPUnit_Framework_MockObject_MockObject + * @return Settings|MockObject */ private function getMockSettings() { @@ -622,7 +605,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => true, - 'truncated' => false + 'truncated' => false, ]], 'Filepath, not recursive, truncated' => [[ 'path' => self::MOCK_FILE_PATH, @@ -652,7 +635,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => false, - 'truncated' => true + 'truncated' => true, ]], 'Filepath, recursive, truncated' => [[ 'path' => self::MOCK_FILE_PATH, @@ -693,7 +676,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => true, - 'truncated' => true + 'truncated' => true, ]], 'No Filepath, recursive, not truncated' => [[ 'path' => '', @@ -745,7 +728,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => true, - 'truncated' => false + 'truncated' => false, ]], 'No Filepath, recursive, truncated' => [[ 'path' => '', @@ -797,7 +780,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => true, - 'truncated' => true + 'truncated' => true, ]], 'No Filepath, not recursive, truncated' => [[ 'path' => '', @@ -811,7 +794,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => false, - 'truncated' => true + 'truncated' => true, ]], 'No Filepath, not recursive, not truncated' => [[ 'path' => '', @@ -825,7 +808,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => false, - 'truncated' => false + 'truncated' => false, ]], ]; } From cb1384effa511ee1511cbac55d1f9aba740ce0cc Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 11:01:13 +0200 Subject: [PATCH 25/50] Fixes several tests in ApiTest that were broken by refactoring the API class - Data for metadata is always retrieved recursively. - Marks Api::getCreatedTimestamp() as being used by Api::getRecursiveMetadata() --- tests/unit-tests/ApiTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index 8452f39..e470c08 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -264,6 +264,8 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn /** * @covers ::getRecursiveMetadata * + * @uses Potherca\Flysystem\Github\Api::getCreatedTimestamp + * * @dataProvider provideExpectedMetadata * * @param array $data @@ -284,7 +286,7 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi $this->prepareMockApi( 'show', $api::API_GIT_DATA, - [$mockVendor, $mockPackage, $mockReference, $data['recursive']], + [$mockVendor, $mockPackage, $mockReference, true], $this->getMockApiTreeResponse($data['truncated'], $api), Trees::class ); @@ -564,7 +566,7 @@ final public function provideExpectedMetadata() ] ], 'recursive' => false, - 'truncated' => false + 'truncated' => false, ]], 'Filepath, recursive, not truncated' => [[ 'path' => self::MOCK_FILE_PATH, From 0835203989d85094e4874ad5101ab415695303e0 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 13:00:56 +0200 Subject: [PATCH 26/50] Fixes tests in ApiTest that were broken by refactoring the API class - Changed mock data to match changed normalisation format from Api class - Changed mock data to match the fact that metadata is always retrieved recursively - Adds Api::getCreatedTimestamp() as being used by Api::getRecursiveMetadata() - Adds mock for Commits Repository --- tests/unit-tests/ApiTest.php | 215 ++++++++++++++++++++--------------- 1 file changed, 122 insertions(+), 93 deletions(-) diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index e470c08..e4c3847 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -264,7 +264,7 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn /** * @covers ::getRecursiveMetadata * - * @uses Potherca\Flysystem\Github\Api::getCreatedTimestamp + * @uses Potherca\Flysystem\Github\Api::getCreatedTimestamp * * @dataProvider provideExpectedMetadata * @@ -293,6 +293,11 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi $actual = $api->getRecursiveMetadata($data['path'], $data['recursive']); + $actual = array_map(function ($value) { + $value['timestamp'] = null; + return $value; + }, $actual); + self::assertEquals($data['expected'], $actual); } @@ -300,8 +305,8 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi * @covers ::guessMimeType * * @uses League\Flysystem\Util\MimeType - * * @uses Potherca\Flysystem\Github\Api::getFileContents + * @uses Potherca\Flysystem\Github\Api::getMetaData */ final public function testApiShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() { @@ -408,8 +413,16 @@ private function prepareMockApi( $parts = explode('\\', $repositoryClass); $repositoryName = strtolower(array_pop($parts)); + $methods = [$repositoryName, 'getPerPage', 'setPerPage']; + + $shouldMockCommitsRepository = false; + if (in_array('commits', $methods, true) === false) { + $shouldMockCommitsRepository = true; + $methods[] = 'commits'; + } + $mockApi = $this->getMockBuilder(ApiInterface::class) - ->setMethods([$repositoryName, 'getPerPage', 'setPerPage']) + ->setMethods($methods) ->getMock() ; @@ -432,9 +445,31 @@ private function prepareMockApi( ->willReturn($mockRepository) ; - $this->mockClient->expects(self::exactly(1)) + if ($shouldMockCommitsRepository === true) { + $mockCommitsRepository = $this->getMockBuilder(Commits::class) + ->disableOriginalConstructor() + ->getMock() + ; + + $apiOutput = [ + ['commit' => ['committer' => ['date' => '20150101']]], + ['commit' => ['committer' => ['date' => '20140202']]] + ]; + + $mockCommitsRepository->expects(self::any()) + ->method('all') + ->withAnyParameters() + ->willReturn($apiOutput) + ; + $mockApi->expects(self::any()) + ->method('commits') + ->willReturn($mockCommitsRepository) + ; + } + + $this->mockClient->expects(self::any()) ->method('api') - ->with($apiName) + ->with(self::matchesRegularExpression(sprintf('/%s|repo/', $apiName))) ->willReturn($mockApi) ; } @@ -445,7 +480,7 @@ private function prepareMockApi( private function prepareMockSettings(array $expectations) { foreach ($expectations as $methodName => $returnValue) { - $this->mockSettings->expects(self::exactly(1)) + $this->mockSettings->expects(self::any()) ->method($methodName) ->willReturn($returnValue) ; @@ -453,11 +488,11 @@ private function prepareMockSettings(array $expectations) } /** - * @param $truncated - * @param $api + * @param bool $truncated + * @param Api $api * @return array */ - private function getMockApiTreeResponse($truncated, $api) + private function getMockApiTreeResponse($truncated, Api $api) { return [ $api::KEY_TREE => [ @@ -543,27 +578,38 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' - ] + ], + [ + 'path' => '/path/to/mock/file/Bar', + 'name' => '/path/to/mock/file/Bar', + 'mode' => '100644', + 'type' => 'file', + 'size' => 57, + 'visibility' => 'public', + 'contents' => false, + 'stream' => false, + 'timestamp' => null + ], ], 'recursive' => false, 'truncated' => false, @@ -573,35 +619,35 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/file/Bar', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/file/Bar', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ] @@ -614,27 +660,38 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' - ] + ], + [ + 'path' => '/path/to/mock/file/Bar', + 'name' => '/path/to/mock/file/Bar', + 'mode' => '100644', + 'type' => 'file', + 'size' => 57, + 'visibility' => 'public', + 'contents' => false, + 'stream' => false, + 'timestamp' => null + ], ], 'recursive' => false, 'truncated' => true, @@ -644,35 +701,35 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/file/Bar', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/file/Bar', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ] @@ -685,46 +742,46 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/file/Bar', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/file/Bar', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => 'some/other/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 747, 'name' => 'some/other/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ] @@ -737,46 +794,46 @@ final public function provideExpectedMetadata() 'expected' => [ [ 'path' => '/path/to/mock/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'dir', 'size' => 57, 'name' => '/path/to/mock/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/fileFoo', 'basename' => '/path/to/mock/fileFoo', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/fileFoo', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => '/path/to/mock/file/Bar', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 57, 'name' => '/path/to/mock/file/Bar', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ], [ 'path' => 'some/other/file', - 'mode' => 100644, + 'mode' => '100644', 'type' => 'file', 'size' => 747, 'name' => 'some/other/file', - 'contents' => null, - 'stream' => null, + 'contents' => false, + 'stream' => false, 'timestamp' => null, 'visibility' => 'public' ] @@ -784,34 +841,6 @@ final public function provideExpectedMetadata() 'recursive' => true, 'truncated' => true, ]], - 'No Filepath, not recursive, truncated' => [[ - 'path' => '', - 'expected' => [ - [ - 'name' => null, - 'visibility' => null, - 'contents' => null, - 'stream' => null, - 'timestamp' => null - ] - ], - 'recursive' => false, - 'truncated' => true, - ]], - 'No Filepath, not recursive, not truncated' => [[ - 'path' => '', - 'expected' => [ - [ - 'name' => null, - 'visibility' => null, - 'contents' => null, - 'stream' => null, - 'timestamp' => null, - ] - ], - 'recursive' => false, - 'truncated' => false, - ]], ]; } } From 62787208cdd8d698ab8673b0ce0fcbe4ad14631a Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 13:03:54 +0200 Subject: [PATCH 27/50] Adds object cache for RepositoryApi to the Api class. --- src/Api.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index 7db5628..7652d72 100644 --- a/src/Api.php +++ b/src/Api.php @@ -4,6 +4,7 @@ use Github\Api\GitData; use Github\Api\Repo; +use Github\Api\Repository\Contents; use Github\Client; use Github\Exception\RuntimeException; use League\Flysystem\AdapterInterface; @@ -41,6 +42,8 @@ class Api implements ApiInterface /** @var Client */ private $client; + /** @var Contents */ + private $contents; /** @var SettingsInterface */ private $settings; /** @var bool */ @@ -87,7 +90,10 @@ private function getRepositoryApi() */ private function getRepositoryContent() { - return $this->getRepositoryApi()->contents(); + if ($this->contents === null) { + $this->contents = $this->getRepositoryApi()->contents(); + } + return $this->contents; } //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ From 6c858b58383e66b51aa4f45ddd42582d943ce046 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 15:38:11 +0200 Subject: [PATCH 28/50] Fixes typo in FIXME comment in the Api class. --- src/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index 7652d72..b14413d 100644 --- a/src/Api.php +++ b/src/Api.php @@ -267,7 +267,7 @@ final public function getRecursiveMetadata($path, $recursive) } }); - /* @FIXME: It might be wise to use a filter to find the right entry instead of ussing it will always be the first entry in the array. */ + /* @FIXME: It might be wise to use a filter to find the right entry instead of always using the first entry in the array. */ $normalizeTreeMetadata[0]['timestamp'] = $directoryTimestamp; From 470237ae97a2846d3447e2c305eaf95f52904300 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 15:43:19 +0200 Subject: [PATCH 29/50] Changes method name of ApiInterface::getRecursiveMetadata() to ApiInterface::getTreeMetadata(). --- src/Api.php | 2 +- src/ApiInterface.php | 2 +- src/GithubAdapter.php | 4 ++-- tests/unit-tests/ApiTest.php | 6 +++--- tests/unit-tests/GithubAdapterTest.php | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Api.php b/src/Api.php index b14413d..fecda9d 100644 --- a/src/Api.php +++ b/src/Api.php @@ -232,7 +232,7 @@ final public function getMetaData($path) * @return array * @throws \Github\Exception\InvalidArgumentException */ - final public function getRecursiveMetadata($path, $recursive) + final public function getTreeMetadata($path, $recursive) { // If $info['truncated'] is `true`, the number of items in the tree array // exceeded the github maximum limit. If we need to fetch more items, diff --git a/src/ApiInterface.php b/src/ApiInterface.php index d8cb101..d5168de 100644 --- a/src/ApiInterface.php +++ b/src/ApiInterface.php @@ -42,7 +42,7 @@ public function getMetaData($path); * * @return array */ - public function getRecursiveMetadata($path, $recursive); + public function getTreeMetadata($path, $recursive); /** * @param string $path diff --git a/src/GithubAdapter.php b/src/GithubAdapter.php index 566ed3f..1379ecc 100644 --- a/src/GithubAdapter.php +++ b/src/GithubAdapter.php @@ -187,7 +187,7 @@ public function read($path) */ public function listContents($path = '/', $recursive = false) { - $contents = $this->getApi()->getRecursiveMetadata($path, $recursive); + $contents = $this->getApi()->getTreeMetadata($path, $recursive); if ($this->isDirectoryContents($contents) === false) { $contents = []; @@ -254,7 +254,7 @@ public function getTimestamp($path) public function getVisibility($path) { $recursive = false; - $metadata = $this->getApi()->getRecursiveMetadata($path, $recursive); + $metadata = $this->getApi()->getTreeMetadata($path, $recursive); return $metadata[0]; } diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index e4c3847..450f2e4 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -262,7 +262,7 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn } /** - * @covers ::getRecursiveMetadata + * @covers ::getTreeMetadata * * @uses Potherca\Flysystem\Github\Api::getCreatedTimestamp * @@ -270,7 +270,7 @@ final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAn * * @param array $data */ - final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursiveMetadata($data) { + final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetTreeMetadata($data) { $api = $this->api; $mockVendor = 'vendor'; @@ -291,7 +291,7 @@ final public function testApiShouldRetrieveExpectedMetadataWhenAskedToGetRecursi Trees::class ); - $actual = $api->getRecursiveMetadata($data['path'], $data['recursive']); + $actual = $api->getTreeMetadata($data['path'], $data['recursive']); $actual = array_map(function ($value) { $value['timestamp'] = null; diff --git a/tests/unit-tests/GithubAdapterTest.php b/tests/unit-tests/GithubAdapterTest.php index 54c10af..ec6619d 100644 --- a/tests/unit-tests/GithubAdapterTest.php +++ b/tests/unit-tests/GithubAdapterTest.php @@ -65,12 +65,12 @@ final public function provideReadMethods() return [ ['has', 'exists', [self::MOCK_FILE_PATH]], ['read', 'getFileContents', [self::MOCK_FILE_PATH]], - ['listContents', 'getRecursiveMetadata', [self::MOCK_FILE_PATH, true]], + ['listContents', 'getTreeMetadata', [self::MOCK_FILE_PATH, true]], ['getMetadata', 'getMetadata', [self::MOCK_FILE_PATH]], ['getSize', 'getMetadata', [self::MOCK_FILE_PATH]], ['getMimetype', 'guessMimeType', [self::MOCK_FILE_PATH]], ['getTimestamp', 'getLastUpdatedTimestamp', [self::MOCK_FILE_PATH]], - ['getVisibility', 'getRecursiveMetadata', [self::MOCK_FILE_PATH]], + ['getVisibility', 'getTreeMetadata', [self::MOCK_FILE_PATH, false]], ]; } } From 263701799c4f66a9df6853824333c7d3a1c4fdb4 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 20:40:41 +0200 Subject: [PATCH 30/50] Fixes bug in Api class caused by merge conflict in rebase. --- src/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index fecda9d..50215c4 100644 --- a/src/Api.php +++ b/src/Api.php @@ -345,7 +345,7 @@ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) if ($recursive === true) { $match = true; } else { - $match = ($matchPath !== '' && strpos($entryPath, '/', $length) === false); + $match = ($matchPath !== '' || strpos($entryPath, '/', $length) === false); } } From 32daf4d61e40e93b00e17e370e6a602ad4281c7f Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 16 Apr 2016 21:40:14 +0200 Subject: [PATCH 31/50] Adds test for recursive folder functionality of GithubAdapter::listContents() - Adds keys to provided data for easy idintifiaction in case of failure - Adds JSON file as fixture --- .../listContents-folder-recursive.json | 46 +++++++++++++++++++ tests/unit-tests/GithubAdapterTest.php | 44 ++++++++++++------ 2 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 tests/fixtures/listContents-folder-recursive.json diff --git a/tests/fixtures/listContents-folder-recursive.json b/tests/fixtures/listContents-folder-recursive.json new file mode 100644 index 0000000..785a69b --- /dev/null +++ b/tests/fixtures/listContents-folder-recursive.json @@ -0,0 +1,46 @@ +{ + "sha": "676a09fd1c7df28938e8c12dc5d9f3c3271f0249", + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/trees/676a09fd1c7df28938e8c12dc5d9f3c3271f0249", + "tree": [ + { + "path": "README", + "mode": "100755", + "type": "blob", + "sha": "1ff3a296caf2d27828dd8c40673c88dbf99d4b3a", + "size": 58, + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/blobs/1ff3a296caf2d27828dd8c40673c88dbf99d4b3a" + }, + { + "path": "a-directory", + "mode": "040000", + "type": "tree", + "sha": "30b7e362894eecb159ce0ba2921a8363cd297213", + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/trees/30b7e362894eecb159ce0ba2921a8363cd297213" + }, + { + "path": "a-directory/another-file.js", + "mode": "100755", + "type": "blob", + "sha": "f542363e1b45aa7a33e5e731678dee18f7a1e729", + "size": 52, + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/blobs/f542363e1b45aa7a33e5e731678dee18f7a1e729" + }, + { + "path": "a-directory/readme.txt", + "mode": "100755", + "type": "blob", + "sha": "27f8ec8435cb07992ecf18f9d5494ffc14948368", + "size": 31, + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/blobs/27f8ec8435cb07992ecf18f9d5494ffc14948368" + }, + { + "path": "a-file.php", + "mode": "100755", + "type": "blob", + "sha": "c6e6cd91e3ae40ab74883720a0d6cfb2af89e4b1", + "size": 117, + "url": "https://api.github.com/repos/potherca-bot/test-repository/git/blobs/c6e6cd91e3ae40ab74883720a0d6cfb2af89e4b1" + } + ], + "truncated": false +} \ No newline at end of file diff --git a/tests/unit-tests/GithubAdapterTest.php b/tests/unit-tests/GithubAdapterTest.php index ec6619d..afd1d72 100644 --- a/tests/unit-tests/GithubAdapterTest.php +++ b/tests/unit-tests/GithubAdapterTest.php @@ -1,8 +1,10 @@ mockClient->expects($this->exactly(1)) - ->method($apiMethod); + if (is_string($returnValue) && is_file(sprintf('%s/../fixtures/%s.json', __DIR__, $returnValue))) { + $fixturePath = sprintf('%s/../fixtures/%s.json', __DIR__, $returnValue); + $fixture = json_decode(file_get_contents($fixturePath), true); + $returnValue = $fixture['tree']; + } + + + $mocker = $this->mockClient->expects(self::exactly(1)) + ->method($apiMethod) + ->willReturn($returnValue) + ; - $mocker->getMatcher()->parametersMatcher = new \PHPUnit_Framework_MockObject_Matcher_Parameters($parameters); + $mocker->getMatcher()->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_Parameters($parameters); call_user_func_array([$this->adapter, $method], $parameters); } @@ -63,14 +76,17 @@ final public function testAdapterShouldPassParameterToClient($method, $apiMethod final public function provideReadMethods() { return [ - ['has', 'exists', [self::MOCK_FILE_PATH]], - ['read', 'getFileContents', [self::MOCK_FILE_PATH]], - ['listContents', 'getTreeMetadata', [self::MOCK_FILE_PATH, true]], - ['getMetadata', 'getMetadata', [self::MOCK_FILE_PATH]], - ['getSize', 'getMetadata', [self::MOCK_FILE_PATH]], - ['getMimetype', 'guessMimeType', [self::MOCK_FILE_PATH]], - ['getTimestamp', 'getLastUpdatedTimestamp', [self::MOCK_FILE_PATH]], - ['getVisibility', 'getTreeMetadata', [self::MOCK_FILE_PATH, false]], + 'has' => ['has', 'exists', [self::MOCK_FILE_PATH]], + 'read' => ['read', 'getFileContents', [self::MOCK_FILE_PATH]], + 'listContents - File' => ['listContents', 'getTreeMetadata', [self::MOCK_FILE_PATH, false]], + 'listContents - File - recursive' => ['listContents', 'getTreeMetadata', [self::MOCK_FILE_PATH, true]], + 'listContents - Folder' => ['listContents', 'getTreeMetadata', [self::MOCK_FOLDER_PATH, false], ''], + 'listContents - Folder - recursive' => ['listContents', 'getTreeMetadata', [self::MOCK_FOLDER_PATH, true], 'listContents-folder-recursive'], + 'getMetadata' => ['getMetadata', 'getMetadata', [self::MOCK_FILE_PATH]], + 'getSize' => ['getSize', 'getMetadata', [self::MOCK_FILE_PATH]], + 'getMimetype' => ['getMimetype', 'guessMimeType', [self::MOCK_FILE_PATH]], + 'getTimestamp' => ['getTimestamp', 'getLastUpdatedTimestamp', [self::MOCK_FILE_PATH]], + 'getVisibility' => ['getVisibility', 'getTreeMetadata', [self::MOCK_FILE_PATH]], ]; } } From e0b3654f01b2719fd10686d37f708d8dc5a7990e Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 18 Apr 2016 20:36:09 +0200 Subject: [PATCH 32/50] Fixes bug caused by not using absolute numbers for string count. --- src/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index 50215c4..a9a4399 100644 --- a/src/Api.php +++ b/src/Api.php @@ -334,7 +334,7 @@ private function authenticate() private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) { $matchPath = substr($path, 0, -1); - $length = strlen($matchPath) - 1; + $length = abs(strlen($matchPath) - 1); $metadata = array_filter($tree, function ($entry) use ($matchPath, $recursive, $length) { $match = false; From f6549eba6ebb355b7a4651806f37bcdc7ad9a3df Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 18 Apr 2016 21:12:59 +0200 Subject: [PATCH 33/50] Adds test to cover Api::getMetaData() when given directory path. --- src/Api.php | 16 ++++++++-- tests/unit-tests/ApiTest.php | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/Api.php b/src/Api.php index a9a4399..6abe064 100644 --- a/src/Api.php +++ b/src/Api.php @@ -208,8 +208,20 @@ final public function getMetaData($path) $project = sprintf('%s/%s', $this->settings->getVendor(), $this->settings->getPackage()); $reference = $this->settings->getReference(); - $url = sprintf('%s/repos/%s/contents/%s?ref=%s', self::GITHUB_API_URL, $project, $path, $reference); - $htmlUrl = sprintf('%s/%s/blob/%s/%s', self::GITHUB_URL, $project, $reference, $path); + $url = sprintf( + '%s/repos/%s/contents/%s?ref=%s', + self::GITHUB_API_URL, + $project, + trim($path, '/'), + $reference + ); + $htmlUrl = sprintf( + '%s/%s/blob/%s/%s', + self::GITHUB_URL, + $project, + $reference, + trim($path, '/') + ); $metadata = [ self::KEY_TYPE => self::KEY_DIRECTORY, diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index 450f2e4..6ab5804 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -22,6 +22,7 @@ class ApiTest extends \PHPUnit_Framework_TestCase ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ const MOCK_FILE_PATH = '/path/to/mock/file'; const MOCK_FILE_CONTENTS = 'Mock file contents'; + const MOCK_FOLDER_PATH = 'a-directory'; /** @var Api */ private $api; @@ -221,6 +222,65 @@ final public function testApiShouldAccountForFileNotExistingWhenAskingInfoForFil self::assertEquals($expected, $actual); } + /** + * @covers ::getMetaData + */ + final public function testApiShouldReturnMetadataForDirectoryWhenGivenPathIsDirectory() + { + $api = $this->api; + + $mockPackage = 'mockPackage'; + $mockPath = self::MOCK_FOLDER_PATH; + $mockReference = 'mockReference'; + $mockVendor = 'mockVendor'; + + $expectedUrl = sprintf( + '%s/repos/%s/%s/contents/%s?ref=%s', + $api::GITHUB_API_URL, + $mockVendor, + $mockPackage, + $mockPath, + $mockReference + ); + + $expectedHtmlUrl = sprintf( + '%s/%s/%s/blob/%s/%s', + $api::GITHUB_URL, + $mockVendor, + $mockPackage, + $mockReference, + $mockPath + ); + + $expected = [ + 'type' => $api::KEY_DIRECTORY, + 'url' => $expectedUrl, + 'html_url' => $expectedHtmlUrl, + '_links' => Array ( + 'self' => $expectedUrl, + 'html' => $expectedHtmlUrl, + ), + ]; + + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'show', + $api::API_REPO, + [$mockVendor, $mockPackage, $mockPath, $mockReference], + [0 => null] + ); + + $actual = $api->getMetaData($mockPath); + + self::assertEquals($expected, $actual); + } + /** * @covers ::getMetaData */ From bc8cdba185abea90d41d631a10acb7a86ce6b4cd Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Mon, 18 Apr 2016 21:41:07 +0200 Subject: [PATCH 34/50] Adds test to cover Api::guessMimeType() when given directory path. --- src/Api.php | 4 +++- tests/unit-tests/ApiTest.php | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Api.php b/src/Api.php index 6abe064..2919ddf 100644 --- a/src/Api.php +++ b/src/Api.php @@ -40,6 +40,8 @@ class Api implements ApiInterface const GITHUB_API_URL = 'https://api.github.com'; const GITHUB_URL = 'https://github.com'; + const MIME_TYPE_DIRECTORY = 'directory'; + /** @var Client */ private $client; /** @var Contents */ @@ -301,7 +303,7 @@ final public function guessMimeType($path) $meta = $this->getMetaData($path); if ($this->hasKey($meta, self::KEY_TYPE) && $meta[self::KEY_TYPE] === self::KEY_DIRECTORY) { - $mimeType = 'directory'; //application/x-directory + $mimeType = self::MIME_TYPE_DIRECTORY; // or application/x-directory } else { $content = $this->getFileContents($path); $mimeType = MimeType::detectByContent($content); diff --git a/tests/unit-tests/ApiTest.php b/tests/unit-tests/ApiTest.php index 6ab5804..c1c3a19 100644 --- a/tests/unit-tests/ApiTest.php +++ b/tests/unit-tests/ApiTest.php @@ -403,6 +403,41 @@ final public function testApiShouldUseFileContentsToGuessMimeTypeWhenExtensionUn self::assertEquals($expected, $actual); } + /** + * @covers ::guessMimeType + * + * @uses League\Flysystem\Util\MimeType + * @uses Potherca\Flysystem\Github\Api::getFileContents + * @uses Potherca\Flysystem\Github\Api::getMetaData + */ + final public function testApiShouldGuessMimeTypeCorrectlyWhenGivenPathIsDirectory() + { + $api = $this->api; + + $expected = $api::MIME_TYPE_DIRECTORY; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'show', + $api::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FOLDER_PATH, $mockReference], + [0 => [$api::KEY_TYPE => $api::MIME_TYPE_DIRECTORY]] + ); + + $actual = $api->guessMimeType(self::MOCK_FOLDER_PATH); + + self::assertEquals($expected, $actual); + } + /** * @uses Potherca\Flysystem\Github\Api::exists */ From b29d1edf2682b0c179b92800470765fcc7055c94 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 19 Apr 2016 13:39:37 +0200 Subject: [PATCH 35/50] Changes repository name check in Settings class to be more readable. --- src/Settings.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Settings.php b/src/Settings.php index c10a080..faeae5d 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -101,9 +101,7 @@ final public function __construct( private function isValidRepositoryName($repository) { if (is_string($repository) === false - || substr_count($repository, '/') !== 1 - || substr($repository, 0, 1) === '/' - || substr($repository, -1, 1) === '/' + || preg_match('#^[^/]+/[^/]+$#', $repository) !== 1 ) { $message = sprintf( self::ERROR_INVALID_REPOSITORY_NAME, From 7372b5409fb3f1e69c8c1140f7aa43ab1be1a3ee Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 19 Apr 2016 13:41:02 +0200 Subject: [PATCH 36/50] Adds changes for upcoming release to CHANGELOG file. --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3210a3..78a7292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,29 +13,55 @@ and [Semantic Versioning](http://semver.org/) conventions. ### Security --> +## [Unreleased][unreleased] + +### Added + +- Adds various @throws annotations +- Adds timestamps for files and folders + +### Changed + +- Changes the default for file visibility to visible +- Changes guessing MIME type for files to always use the file's content instead of extension to guarantee results are the same across different filesystem adapters +- Changes method name of ApiInterface::getRecursiveMetadata() to ApiInterface::getTreeMetadata() + +### Fixed + +- Fixes bug that caused invalid repository names to be accepted (issue #4) +- Fixes bug that caused incorrect Metadata for directories to be returned (issue #6) +- Fixes bug that didn't validate paths ended in a trailing slash (issue #8) +- Fixes bug in permission comparison +- Fixes various links in the README file + ## [v0.2.0] - 2015-07-21 - Improvements and UnitTests ### Added + - Adds automated checks (a.k.a. unit-tests) for the Adapter, Client and Settings classes. - Adds various utility files for Travis builds, Coveralls and Composer ### Changed + - Makes the PHPUnit configuration more strict - Renames the Client class to "Api" ## [v0.1.0] - 2015-07-18 - Read functionality ### Added + - Read functionality and Github API authentication have been implemented. ## v0.0.0 - 2015-05-11 - Project Setup ### Added + - Set up project basics like .gitignore file, PHPUnit Configuration file, Contributing guidelines, Composer file stating dependencies, MIT License, README file and this CHANGELOG file. [unreleased]: https://github.com/potherca/flysystem-github/compare/v0.2.0...HEAD +[v0.3.0]: https://github.com/potherca/flysystem-github/compare/v0.2.0...v0.3.0 [v0.2.0]: https://github.com/potherca/flysystem-github/compare/v0.1.0...v0.2.0 [v0.1.0]: https://github.com/potherca/flysystem-github/compare/v0.0.0...v0.1.0 [keep-a-changelog]: http://keepachangelog.com/ From 96582da0937863ec5cd46dbe91b8ccc3a21a6188 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 19 Apr 2016 13:42:01 +0200 Subject: [PATCH 37/50] Adds explicit mention of CHANGELOG to CONTRIBUTING file. --- CONTRIBUTING.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1631d2b..947f2e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,14 +4,13 @@ Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/potherca/flysystem-github). - ## Pull Requests - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). - **Add tests!** - Your patch won't be accepted if it doesn't have tests. -- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. +- **Document any change in behaviour** - Make sure the `README.md`, `CHANGELOG.md` and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. @@ -21,7 +20,6 @@ We accept contributions via Pull Requests on [Github](https://github.com/potherc - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. - ## Running Tests ``` bash From a98fe4bac6affe08af245e353438ed6373498cb8 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 19 Apr 2016 14:55:10 +0200 Subject: [PATCH 38/50] Adds .env file to gitignore. --- .gitignore | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index bfcf449..8f25c95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ -build -vendor +/* Directories to ignore */ +/build +/vendor -composer.lock -phpunit.xml +/* Files to ignore */ +/.env +/composer.lock +/phpunit.xml From 209126faf87609c0c3133ee151bd3b33f3892964 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:43:27 +0200 Subject: [PATCH 39/50] Adds "josegonzalez/dotenv" as development dependency. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1dfbc83..b20d968 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ }, "require-dev": { "php" : ">=5.5", + "josegonzalez/dotenv": "~2.0", "phpunit/phpunit" : "~4.8", "satooshi/php-coveralls": "~0.6", "scrutinizer/ocular": "~1.1", From 6613989eabd36ace5874f73a44f46fe2fd31e63e Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:43:54 +0200 Subject: [PATCH 40/50] Adds example .env file for integration tests. --- tests/integration-tests/.env.example | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/integration-tests/.env.example diff --git a/tests/integration-tests/.env.example b/tests/integration-tests/.env.example new file mode 100644 index 0000000..7fefd21 --- /dev/null +++ b/tests/integration-tests/.env.example @@ -0,0 +1,2 @@ +# Github API key used by integration tests +GITHUB_API_KEY='abcdef0123456789abcdef0123456789abcdef01' \ No newline at end of file From 578087f6b236d36c8e30707331c8bf753465e73d Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:44:35 +0200 Subject: [PATCH 41/50] Adds integration test fixture directory to the gitignore file. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8f25c95..d1ed533 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ /* Directories to ignore */ /build /vendor +/tests/fixtures/integration-test-repository /* Files to ignore */ -/.env +.env /composer.lock /phpunit.xml From 6fce6394f6db6458e46d93c3adc16d8dc48f4c20 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:45:20 +0200 Subject: [PATCH 42/50] Adds mention of the integation tests to the CHANGELOG file. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78a7292..e34dfdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and [Semantic Versioning](http://semver.org/) conventions. - Adds various @throws annotations - Adds timestamps for files and folders +- Adds integration tests to validate output against comparable output from LocalAdapter ### Changed From eaa5e852850f457258f81fd4dc52ee5b98ce9556 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:46:19 +0200 Subject: [PATCH 43/50] Adds integration test to validate output against LocalAdapter output. --- .../integration-tests/compareToLocalCalls.php | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 tests/integration-tests/compareToLocalCalls.php diff --git a/tests/integration-tests/compareToLocalCalls.php b/tests/integration-tests/compareToLocalCalls.php new file mode 100644 index 0000000..720825f --- /dev/null +++ b/tests/integration-tests/compareToLocalCalls.php @@ -0,0 +1,181 @@ +filesystem = new Filesystem(new GithubAdapter(new Api(new Client(), new Settings($project, $credentials)))); + } + + /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @param string $function + * @param array $parameters + * @param array $file + * + * @dataProvider provideFiles + */ + final public function testOutputMatchesLocalAdapterOutputWhenCalledOnTheSameSource($function, array $parameters, array $file) + { + $path = $file['path']; + + $localFileSystem = $this->getLocalFileSystem(); + + $localResult = $localFileSystem->{$function}($path); + $result = $this->filesystem->{$function}($path); + + if (array_key_exists('callback', $parameters)) { + $parameters['callback']($localResult, $result); + } else { + self::assertEquals($localResult, $result); + } + } + + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + final public function provideFiles() + { + $this->createFixture(); + + $files = []; + + $localFileSystem = $this->getLocalFileSystem(); + + // @TODO: Test for FileNotFoundException + //$files[] = ['path'=>'NON-EXISTENT-FILE']; + + $functions = [ + 'assertPresent' => [], + 'get' => ['callback' => function ($localResult, $result) { + if ($localResult instanceof \League\Flysystem\Directory) { + /** @var $localResult \League\Flysystem\Directory */ + /** @var $result \League\Flysystem\Directory */ + $localContents = $localResult->getContents(); + /** @var array $contents */ + $contents = $result->getContents(); + $this->compare($localContents, $contents); + } elseif ($localResult instanceof \League\Flysystem\File) { + /** @var $localResult \League\Flysystem\File */ + /** @var $result \League\Flysystem\File */ + self::assertEquals($localResult->read(), $result->read()); + } else { + self::assertEquals($localResult, $result); + } + }], + 'getMimetype' => [], + 'getSize' => [], + //@FIXME: Synchronize local timestamp with remote git repo timestamp so "getTimestamp" can be tested + // 'getTimestamp' => [], + 'getVisibility' => [], + 'has' => [], + 'listContents' => ['type' => 'dir', 'callback' => [$this, 'compare']], + 'read' => ['type' => 'file'], + 'readStream' => ['type' => 'file', 'callback' => function ($localStream, $githubStream){ + self::assertEquals(stream_get_contents($localStream), stream_get_contents($githubStream)); + }], + ]; + + $localFiles = $localFileSystem->listContents('', true); + + foreach ($localFiles as $index => $file) { + + $path = $file['path']; + + if (strpos($path, '.') !== 0) { + foreach ($functions as $function => $parameters) { + if (array_key_exists('type', $parameters) === false || $file['type'] === $parameters['type']) { + $key = sprintf('%s - %s', $function, $path); + $files[$key] = [$function, $parameters, $file]; + } + } + } + } + ksort($files); + + return $files; + } + + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @param $envFilePath + */ + private static function loadEnvironmentalVariables($envFilePath) + { + if (is_file($envFilePath)) { + $loader = (new josegonzalez\Dotenv\Loader($envFilePath)); + $loader->parse()->putenv(); + } + } + + private function createFixture() + { + $gitRemote = $this->gitRemote; + $fixturesPath = $this->getFixturePath(); + $glob = glob($fixturesPath.'**'); + + if (is_dir($fixturesPath) === false || count($glob) === 0) { + + fwrite(STDERR, sprintf('Creating fixture directory from %s.%s', $gitRemote, "\n")); + + exec(sprintf('git clone %s %s', $gitRemote, $fixturesPath)); + } + } + + /** + * @param array $localContents + * @param array $contents + */ + private function compare(array $localContents, array $contents) + { + array_walk($contents, 'ksort'); + array_walk($localContents, 'ksort'); + + $localContents = array_map(function ($value) { + unset($value['timestamp']); + return $value; + }, $localContents); + + foreach ($localContents as $index => $localContent) { + foreach ($localContent as $key => $value) { + self::assertEquals($value, $contents[$index][$key]); + } + } + } + + private function getLocalFileSystem() + { + return new Filesystem(new LocalAdapter($this->getFixturePath())); + } + + /** + * @return string + */ + private function getFixturePath() + { + return dirname(__DIR__) . '/fixtures/integration-test-repository/'; + } +} + +/*EOF*/ From cc4cb6878e47b812afb407cb595ebf234d060702 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:52:45 +0200 Subject: [PATCH 44/50] Adds settings for integration tests to Travis' phpunit configuration. --- build/phpunit.xml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/build/phpunit.xml b/build/phpunit.xml index 5b7a9c4..5de965e 100644 --- a/build/phpunit.xml +++ b/build/phpunit.xml @@ -1,7 +1,7 @@ - - ../tests + + ../tests/unit-tests + + + ../tests/integration-tests + src/ + From ce43cf964edccf7b9b26910d8ebbae03744a85a8 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:56:29 +0200 Subject: [PATCH 45/50] Adds command to run all tests to composer configuration file. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b20d968..5257566 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ } }, "scripts": { - "test" : "phpunit" + "test" : "phpunit", + "test-all" : "phpunit --configuration build/phpunit.xml" } } From a6b8408c22ce5915572805dd243e3fb9742a67e7 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 16:56:44 +0200 Subject: [PATCH 46/50] Adds mention of integration tests to README file. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 854f5c7..791b1b4 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,23 @@ $filesystem = new Filesystem($adapter); ## Testing +The unit-tests can be run with the following command: + ``` bash $ composer test ``` +To run integration tests, which use the Github API, a [Github API token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) might be needed (to stop the tests hitting the API Limit). +An API key can be added by setting in the environment as `GITHUB_API_KEY` or by vreating an `.env` file in the integration tests directory and setting it there. +See `tests/integration-tests/.env.example` for an example. + +To run the integration test, run the following command (this will also run the unit-tests): + +``` bash +$ composer test-all +``` + + ## Security If you discover any security related issues, please email potherca@gmail.com instead of using the issue tracker. From d583b77e6093c28e4a4b794544e39573072fab01 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 17:31:02 +0200 Subject: [PATCH 47/50] Places badge links at the bottom of the README file. --- README.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 791b1b4..f80f51c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Flysystem Adapter for Github -[![Latest Version](https://img.shields.io/github/release/potherca/flysystem-github.svg?style=flat-square)](https://github.com/potherca/flysystem-github/releases) -[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) -[![Build Status](https://img.shields.io/travis/potherca/flysystem-github.svg?style=flat-square)](https://travis-ci.org/potherca/flysystem-github) -[![Coverage Status](https://coveralls.io/repos/potherca/flysystem-github/badge.svg)](https://coveralls.io/github/potherca/flysystem-github) -[![Quality Score](https://img.shields.io/scrutinizer/g/potherca/flysystem-github.svg?style=flat-square)](https://scrutinizer-ci.com/g/potherca/flysystem-github) -[![Total Downloads](https://img.shields.io/packagist/dt/potherca/flysystem-github.svg?style=flat-square)](https://packagist.org/packages/potherca/flysystem-github) +[![Latest Version][Latest Version Badge]][Release Page] +[![Software License][Software License Badge]][License file] +[![Build Status][Build Status Badge]][Travis Page] +[![Coverage Status][Coverage Status Badge]][Coveralls Page] +[![Quality Score][Quality Score Badge]][Scrutinizer Page] +[![Total Downloads][Total Downloads Badge]][Packagist Page] ## Install @@ -124,4 +124,18 @@ Please see [CHANGELOG](CHANGELOG.md) for details. ## License -The MIT License (MIT). Please see [License File](LICENSE.md) for more information. +The MIT License (MIT). Please see [License File] for more information. + +[Release Page]: https://github.com/potherca/flysystem-github/releases +[License File]: LICENSE.md +[Travis Page]: https://travis-ci.org/Potherca/flysystem-github +[Coveralls Page]: https://coveralls.io/github/potherca/flysystem-github +[Scrutinizer Page]: https://scrutinizer-ci.com/g/potherca/flysystem-github +[Packagist Page]: https://packagist.org/packages/potherca/flysystem-github + +[Latest Version Badge]: https://img.shields.io/github/release/potherca/flysystem-github.svg +[Software License Badge]: https://img.shields.io/badge/license-MIT-brightgreen.svg +[Build Status Badge]: https://img.shields.io/travis/Potherca/flysystem-github.svg +[Coverage Status Badge]: https://coveralls.io/repos/potherca/flysystem-github/badge.svg +[Quality Score Badge]: https://img.shields.io/scrutinizer/g/potherca/flysystem-github.svg +[Total Downloads Badge]: https://img.shields.io/packagist/dt/potherca/flysystem-github.svg \ No newline at end of file From 0218520da93b8c2b3a92257b13c95c6471208815 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 18:31:45 +0200 Subject: [PATCH 48/50] Fixes typo in the README file. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f80f51c..9eb5ecd 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ $ composer test ``` To run integration tests, which use the Github API, a [Github API token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) might be needed (to stop the tests hitting the API Limit). -An API key can be added by setting in the environment as `GITHUB_API_KEY` or by vreating an `.env` file in the integration tests directory and setting it there. +An API key can be added by setting it in the environment as `GITHUB_API_KEY` or by creating an `.env` file in the integration tests directory and setting it there. See `tests/integration-tests/.env.example` for an example. To run the integration test, run the following command (this will also run the unit-tests): @@ -104,7 +104,6 @@ To run the integration test, run the following command (this will also run the u ``` bash $ composer test-all ``` - ## Security From be20718cee27a171a7b27eb88fd3f24c6e5b511b Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 18:58:42 +0200 Subject: [PATCH 49/50] Moves several links to the bottom of the README file. --- README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9eb5ecd..8eb6839 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ The unit-tests can be run with the following command: $ composer test ``` -To run integration tests, which use the Github API, a [Github API token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) might be needed (to stop the tests hitting the API Limit). +To run integration tests, which use the Github API, a [Github API token] might be needed (to stop the tests hitting the API Limit). An API key can be added by setting it in the environment as `GITHUB_API_KEY` or by creating an `.env` file in the integration tests directory and setting it there. See `tests/integration-tests/.env.example` for an example. @@ -111,15 +111,16 @@ If you discover any security related issues, please email potherca@gmail.com ins ## Contributing -Please see [CONTRIBUTING](CONTRIBUTING.md) for details. +Please see [CONTRIBUTING] for details. ## Change Log -Please see [CHANGELOG](CHANGELOG.md) for details. +Please see [CHANGELOG] for details. ## Credits -- [Potherca](https://github.com/potherca) +- [Potherca] +- [Contributors] ## License @@ -137,4 +138,10 @@ The MIT License (MIT). Please see [License File] for more information. [Build Status Badge]: https://img.shields.io/travis/Potherca/flysystem-github.svg [Coverage Status Badge]: https://coveralls.io/repos/potherca/flysystem-github/badge.svg [Quality Score Badge]: https://img.shields.io/scrutinizer/g/potherca/flysystem-github.svg -[Total Downloads Badge]: https://img.shields.io/packagist/dt/potherca/flysystem-github.svg \ No newline at end of file +[Total Downloads Badge]: https://img.shields.io/packagist/dt/potherca/flysystem-github.svg + +[Contributors]: https://github.com/Potherca/flysystem-github/graphs/contributors +[CHANGELOG]: CHANGELOG.md +[CONTRIBUTING]: CONTRIBUTING.md +[Potherca]: https://github.com/potherca +[Github API token]: https://help.github.com/articles/creating-an-access-token-for-command-line-use/ \ No newline at end of file From 3b2ccb01cf0992904046be6c74022c5c50a41513 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Wed, 20 Apr 2016 19:00:45 +0200 Subject: [PATCH 50/50] Updates CHANGELOG with changes for v0.3.0 release. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e34dfdb..95b6526 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and [Semantic Versioning](http://semver.org/) conventions. ### Security --> -## [Unreleased][unreleased] +## [v0.3.0] - 2016-04-20 - Metadata for directories and various other bugfixes ### Added @@ -61,7 +61,7 @@ and [Semantic Versioning](http://semver.org/) conventions. Contributing guidelines, Composer file stating dependencies, MIT License, README file and this CHANGELOG file. -[unreleased]: https://github.com/potherca/flysystem-github/compare/v0.2.0...HEAD +[unreleased]: https://github.com/potherca/flysystem-github/compare/v0.3.0...HEAD [v0.3.0]: https://github.com/potherca/flysystem-github/compare/v0.2.0...v0.3.0 [v0.2.0]: https://github.com/potherca/flysystem-github/compare/v0.1.0...v0.2.0 [v0.1.0]: https://github.com/potherca/flysystem-github/compare/v0.0.0...v0.1.0