From 34d9b6990abc07da33994d899220a6318e6ad02c Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 04:37:56 +0200 Subject: [PATCH 01/23] Added Tests for the Settings class. --- src/Settings.php | 44 +++++++--- tests/SettingsTest.php | 191 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 tests/SettingsTest.php diff --git a/src/Settings.php b/src/Settings.php index 706eb28..6d5918f 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -11,6 +11,7 @@ class Settings const REFERENCE_HEAD = 'HEAD'; const BRANCH_MASTER = 'master'; + const ERROR_INVALID_REPOSITORY_NAME = 'Given Repository name "%s" should be in the format of "vendor/project"'; /** @var string */ private $repository; @@ -27,19 +28,28 @@ final public function __construct( $branch = self::BRANCH_MASTER, $reference = self::REFERENCE_HEAD ) { - $this->branch = $branch; + $this->isValidRepositoryName($repository); + + $this->branch = (string) $branch; $this->credentials = $credentials; - $this->reference = $reference; - $this->repository = $repository; + $this->reference = (string) $reference; + $this->repository = (string) $repository; } - /** * @return string */ - final public function getRepository() + final public function getBranch() { - return $this->repository; + return $this->branch; + } + + /** + * @return array + */ + final public function getCredentials() + { + return $this->credentials; } /** @@ -51,19 +61,29 @@ final public function getReference() } /** - * @return array + * @return string */ - final public function getCredentials() + final public function getRepository() { - return $this->credentials; + return $this->repository; } /** - * @return string + * @param $repository */ - final public function getBranch() + private function isValidRepositoryName($repository) { - return $this->branch; + if (is_string($repository) === false + || strpos($repository, '/') === false + || strpos($repository, '/') === 0 + || substr_count($repository, '/') !== 1 + ) { + $message = sprintf( + self::ERROR_INVALID_REPOSITORY_NAME, + var_export($repository, true) + ); + throw new \InvalidArgumentException($message); + } } } diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php new file mode 100644 index 0000000..b45b9b8 --- /dev/null +++ b/tests/SettingsTest.php @@ -0,0 +1,191 @@ + + * @covers ::__construct + */ +class SettingsTest extends \PHPUnit_Framework_TestCase +{ + const MOCK_REPOSITORY_NAME = 'foo/bar'; + const MOCK_CREDENTIALS = ['mock_type', 'mock_user', 'mock_password']; + const MOCK_BRANCH = 'mock_branch'; + const MOCK_REFERENCE = 'mock_reference'; + + /** @var Settings */ + private $settings; + + /** + * + */ + final protected function setUp() + { + $this->settings = new Settings( + self::MOCK_REPOSITORY_NAME, + self::MOCK_CREDENTIALS, + self::MOCK_BRANCH, + self::MOCK_REFERENCE + ); + } + + /** + * @covers ::__construct + */ + final public function testSettingsShouldComplainWhenInstantiatedWithoutProject() + { + $this->setExpectedException( + \PHPUnit_Framework_Error_Warning::class, + sprintf('Missing argument %d for %s::__construct()', 1, Settings::class) + ); + + /** @noinspection PhpParamsInspection */ + new Settings(); + } + + /** + * @covers ::getRepository + */ + final public function testSettingsShouldContainRepositoryItWasGivenGivenWhenInstantiated() + { + $settings = $this->settings; + + $expected = self::MOCK_REPOSITORY_NAME; + + $actual = $settings->getRepository(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::__construct + * + * @dataProvider provideInvalidRepositoryNames + * + * @param string $name + */ + final public function testSettingsShouldComplainWhenGivenInvalidRepositoryNames($name) + { + $this->setExpectedException( + \InvalidArgumentException::class, + sprintf(Settings::ERROR_INVALID_REPOSITORY_NAME, var_export($name, true)) + ); + new Settings($name); + } + + /** + * @covers ::getRepository + */ + final public function testSettingsShouldOnlyNeedRepositoryNameWhenInstantiated() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $this->assertInstanceOf(Settings::class, $settings); + } + + /** + * @covers ::getCredentials + */ + final public function testSettingsShouldContainEmptyCredentialsWhenInstantiatedWithoutCredentials() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + + $expected = []; + + $actual = $settings->getCredentials(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getCredentials + */ + final public function testSettingsShouldContainCredentialsItWasGivenGivenWhenInstantiated() + { + $settings = $this->settings; + + $expected = self::MOCK_CREDENTIALS; + + $actual = $settings->getCredentials(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getBranch + */ + final public function testSettingsShouldContainMasterAsBranchWhenInstantiatedWithoutBranch() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + + $expected = 'master'; + + $actual = $settings->getBranch(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getBranch + */ + final public function testSettingsShouldContainBranchItWasGivenGivenWhenInstantiated() + { + $settings = $this->settings; + + $expected = self::MOCK_BRANCH; + + $actual = $settings->getBranch(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getReference + */ + final public function testSettingsShouldContainHeadAsReferenceWhenInstantiatedWithoutReference() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + + $expected = 'HEAD'; + + $actual = $settings->getReference(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getReference + */ + final public function testSettingsShouldContaingetReferenceItWasGivenGivenWhenInstantiated() + { + $settings = $this->settings; + + $expected = self::MOCK_REFERENCE; + + $actual = $settings->getReference(); + + $this->assertEquals($expected, $actual); + } + + final public function provideInvalidRepositoryNames() + { + return [ + [''], + [null], + [true], + [array()], + ['foo'], + ['/foo'], + ['foo/bar/'], + ['/foo/bar/'], + ['foo/bar/baz'], + ['/foo/bar/baz/'], + ['foo/bar/baz/'], + ['/foo/bar/baz'], + ]; + } +} + +/*EOF*/ From d0e027a87ba11384b82f78e17bcd1d92163e4de6 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 14:17:53 +0200 Subject: [PATCH 02/23] Renamed and re-ordered various method in the Client class --- src/Client.php | 255 +++++++++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 124 deletions(-) diff --git a/src/Client.php b/src/Client.php index c0c1aaf..6d8be1b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,6 +11,7 @@ class Client { + ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ const ERROR_NOT_FOUND = 'Not Found'; const KEY_BLOB = 'blob'; @@ -40,7 +41,43 @@ class Client /** @var string */ private $vendor; - final public function __construct(GithubClient $client, Settings $settings) + //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @param string $name + * @return \Github\Api\ApiInterface + */ + private function getApi($name) + { + $this->authenticate(); + return $this->client->api($name); + } + + /** + * @return GitData + */ + private function getGitDataApi() + { + return $this->getApi(self::KEY_GIT_DATA); + } + + /** + * @return Repo + */ + private function getRepositoryApi() + { + return $this->getApi(self::KEY_REPO); + } + + /** + * @return \Github\Api\Repository\Contents + */ + private function getRepositoryContent() + { + return $this->getRepositoryApi()->contents(); + } + + //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + final public function __construct(GithubClient $client, SettingsInterface $settings) { $this->client = $client; $this->settings = $settings; @@ -51,21 +88,6 @@ final public function __construct(GithubClient $client, Settings $settings) list($this->vendor, $this->package) = explode('/', $this->settings->getRepository()); } - /** - * @param string $path - * - * @return bool - */ - final public function exists($path) - { - return $this->repositoryContents()->exists( - $this->vendor, - $this->package, - $path, - $this->settings->getReference() - ); - } - /** * @param $path * @@ -75,7 +97,7 @@ final public function exists($path) */ final public function download($path) { - $fileContent = $this->repositoryContents()->download( + $fileContent = $this->getRepositoryContent()->download( $this->vendor, $this->package, $path, @@ -87,21 +109,17 @@ final public function download($path) /** * @param string $path - * @param bool $recursive * - * @return array + * @return bool */ - final public function metadata($path, $recursive) + final public function exists($path) { - // 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, - // multiple calls will be needed - - $info = $this->trees($recursive); - $tree = $this->getPathFromTree($info, $path, $recursive); - $result = $this->normalizeMetadata($tree); - - return $result; + return $this->getRepositoryContent()->exists( + $this->vendor, + $this->package, + $path, + $this->settings->getReference() + ); } /** @@ -109,16 +127,24 @@ final public function metadata($path, $recursive) * * @return array */ - final public function show($path) + final public function getLastUpdatedTimestamp($path) { - // Get information about a repository file or directory - $fileInfo = $this->repositoryContents()->show( + // List commits for a file + $commits = $this->getRepositoryApi()->commits()->all( $this->vendor, $this->package, - $path, - $this->settings->getReference() + array( + 'sha' => $this->settings->getBranch(), + 'path' => $path + ) ); - return $fileInfo; + + $updated = array_shift($commits); + //@NOTE: $created = array_pop($commits); + + $time = new \DateTime($updated['commit']['committer']['date']); + + return ['timestamp' => $time->getTimestamp()]; } /** @@ -141,6 +167,25 @@ final public function getMetaData($path) return $metadata; } + /** + * @param string $path + * @param bool $recursive + * + * @return array + */ + final public function getMetadataFromTree($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, + // multiple calls will be needed + + $info = $this->getTreeInfo($recursive); + $treeMetadata = $this->extractMetaDataFromTreeInfo($info, $path, $recursive); + $metadata = $this->normalizeTreeMetadata($treeMetadata); + + return $metadata; + } + /** * @param string $path * @@ -164,38 +209,24 @@ final public function guessMimeType($path) } /** + * Get information about a repository file or directory + * * @param string $path * * @return array */ - final public function updated($path) + final public function show($path) { - // List commits for a file - $commits = $this->repository()->commits()->all( + $fileInfo = $this->getRepositoryContent()->show( $this->vendor, $this->package, - array( - 'sha' => $this->settings->getBranch(), - 'path' => $path - ) + $path, + $this->settings->getReference() ); - - $updated = array_shift($commits); - //@NOTE: $created = array_pop($commits); - - $time = new \DateTime($updated['commit']['committer']['date']); - - return ['timestamp' => $time->getTimestamp()]; - } - - /** - * @return \Github\Api\Repository\Contents - */ - private function repositoryContents() - { - return $this->repository()->contents(); + return $fileInfo; } + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * */ @@ -204,10 +235,12 @@ private function authenticate() static $hasRun; if ($hasRun === null) { - if (empty($this->settings->getCredentials()) === false) { + $credentials = $this->settings->getCredentials(); + + if (empty($credentials) === false) { $credentials = array_replace( [null, null, null], - $this->settings->getCredentials() + $credentials ); $this->client->authenticate( @@ -221,40 +254,16 @@ private function authenticate() } /** - * @return Repo - */ - private function repository() - { - return $this->fetchApi(self::KEY_REPO); - } - - /** - * @param string $name - * @return \Github\Api\ApiInterface - */ - private function fetchApi($name) - { - $this->authenticate(); - return $this->client->api($name); - } - - /** - * @param array $metadata + * @param array $tree * @param string $path * @param bool $recursive * * @return array */ - private function getPathFromTree(array $metadata, $path, $recursive) + private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) { - if (empty($path)) { - if ($recursive === false) { - $metadata = array_filter($metadata, function ($entry) use ($path) { - return (strpos($entry[self::KEY_PATH], '/', strlen($path)) === false); - }); - } - } else { - $metadata = array_filter($metadata, function ($entry) use ($path, $recursive) { + if(empty($path) === false) { + $metadata = array_filter($tree, function ($entry) use ($path, $recursive) { $match = false; if (strpos($entry[self::KEY_PATH], $path) === 0) { @@ -268,17 +277,50 @@ private function getPathFromTree(array $metadata, $path, $recursive) 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 $metadata; } + /** + * @param bool $recursive + * @return \Guzzle\Http\EntityBodyInterface|mixed|string + */ + private function getTreeInfo($recursive) + { + $trees = $this->getGitDataApi()->trees(); + + $info = $trees->show( + $this->vendor, + $this->package, + $this->settings->getReference(), + $recursive + ); + + return $info[self::KEY_TREE]; + } + + /** + * @param $permissions + * @return string + */ + private function guessVisibility($permissions) + { + return $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE; + } + /** * @param array $metadata * * @return array */ - private function normalizeMetadata($metadata) + private function normalizeTreeMetadata($metadata) { $result = []; @@ -322,7 +364,7 @@ private function normalizeMetadata($metadata) } if (isset($entry[self::KEY_MODE])) { - $entry[self::KEY_VISIBILITY] = $this->visibility($entry[self::KEY_MODE]); + $entry[self::KEY_VISIBILITY] = $this->guessVisibility($entry[self::KEY_MODE]); } else { $entry[self::KEY_VISIBILITY] = false; } @@ -332,39 +374,4 @@ private function normalizeMetadata($metadata) return $result; } - - /** - * @return GitData - */ - private function gitData() - { - return $this->fetchApi(self::KEY_GIT_DATA); - } - - /** - * @param bool $recursive - * @return \Guzzle\Http\EntityBodyInterface|mixed|string - */ - private function trees($recursive) - { - $trees = $this->gitData()->trees(); - - $info = $trees->show( - $this->vendor, - $this->package, - $this->settings->getReference(), - $recursive - ); - - return $info[self::KEY_TREE]; - } - - /** - * @param $permissions - * @return string - */ - private function visibility($permissions) - { - return $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE; - } } From 65b2b6f8ef105ff27db2db97e9ea82fdd777f484 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 14:19:33 +0200 Subject: [PATCH 03/23] Created an Interface for the Settings class to implement. --- src/Settings.php | 8 ++++---- src/SettingsInterface.php | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/SettingsInterface.php diff --git a/src/Settings.php b/src/Settings.php index 6d5918f..48a7862 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -2,12 +2,12 @@ namespace Potherca\Flysystem\Github; -use Github\Client; +use Github\Client as GithubClient; -class Settings +class Settings implements SettingsInterface { - const AUTHENTICATE_USING_TOKEN = Client::AUTH_URL_TOKEN; - const AUTHENTICATE_USING_PASSWORD = Client::AUTH_HTTP_PASSWORD; + const AUTHENTICATE_USING_TOKEN = GithubClient::AUTH_URL_TOKEN; + const AUTHENTICATE_USING_PASSWORD = GithubClient::AUTH_HTTP_PASSWORD; const REFERENCE_HEAD = 'HEAD'; const BRANCH_MASTER = 'master'; diff --git a/src/SettingsInterface.php b/src/SettingsInterface.php new file mode 100644 index 0000000..d606728 --- /dev/null +++ b/src/SettingsInterface.php @@ -0,0 +1,26 @@ + Date: Sat, 18 Jul 2015 14:20:36 +0200 Subject: [PATCH 04/23] Whitespace fix for the .gitignore file. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 82405c2..bfcf449 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ build vendor composer.lock -phpunit.xml \ No newline at end of file +phpunit.xml From 5e94461f7744c3cfcbe2711ed6c9aafd20124b54 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 14:23:04 +0200 Subject: [PATCH 05/23] Updates various dependencies - Corrects branch-alias for the develop branch - Adds whatthejeff/nyancat-phpunit-resultprinter for prettier test output --- composer.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 8e0aa82..4204968 100644 --- a/composer.json +++ b/composer.json @@ -20,13 +20,14 @@ } ], "require": { - "php" : ">=5.3.0", + "php" : ">=5.5.0", "knplabs/github-api": "^1.4", "league/flysystem": "^1.0" }, "require-dev": { - "phpunit/phpunit" : "4.*", - "scrutinizer/ocular": "~1.1" + "phpunit/phpunit" : "^4.7.7", + "scrutinizer/ocular": "^1.1", + "whatthejeff/nyancat-phpunit-resultprinter": "^1.2" }, "autoload": { "psr-4": { @@ -40,7 +41,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "0.2.0-dev" } }, "scripts": { From 693d96d4a588296226aa26f496a3efb0d0d50091 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 14:24:16 +0200 Subject: [PATCH 06/23] Makes the PHPUnit configuration more strict - Also adds the Nyancat resultprinter for prettier test output --- phpunit.xml.dist | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 68bbdfd..43d1256 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,23 @@ - + tests From 556965f186c2d53229c319012fe4d097fe3d6758 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 15:27:23 +0200 Subject: [PATCH 07/23] Moves vendor and package name from Client to Settings class --- src/Client.php | 31 ++++++++----------- src/Settings.php | 65 +++++++++++++++++++++++++++------------ src/SettingsInterface.php | 10 ++++++ tests/SettingsTest.php | 37 ++++++++++++++++++++-- 4 files changed, 104 insertions(+), 39 deletions(-) diff --git a/src/Client.php b/src/Client.php index 6d8be1b..5f3fbbc 100644 --- a/src/Client.php +++ b/src/Client.php @@ -36,10 +36,6 @@ class Client private $client; /** @var Settings */ private $settings; - /** @var string */ - private $package; - /** @var string */ - private $vendor; //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ /** @@ -79,13 +75,12 @@ private function getRepositoryContent() //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ final public function __construct(GithubClient $client, SettingsInterface $settings) { - $this->client = $client; - $this->settings = $settings; - /* @NOTE: If $settings contains `credentials` but not an `author` we are * still in `read-only` mode. */ - list($this->vendor, $this->package) = explode('/', $this->settings->getRepository()); + + $this->client = $client; + $this->settings = $settings; } /** @@ -98,8 +93,8 @@ final public function __construct(GithubClient $client, SettingsInterface $setti final public function download($path) { $fileContent = $this->getRepositoryContent()->download( - $this->vendor, - $this->package, + $this->settings->getVendor(), + $this->settings->getPackage(), $path, $this->settings->getReference() ); @@ -115,8 +110,8 @@ final public function download($path) final public function exists($path) { return $this->getRepositoryContent()->exists( - $this->vendor, - $this->package, + $this->settings->getVendor(), + $this->settings->getPackage(), $path, $this->settings->getReference() ); @@ -131,8 +126,8 @@ final public function getLastUpdatedTimestamp($path) { // List commits for a file $commits = $this->getRepositoryApi()->commits()->all( - $this->vendor, - $this->package, + $this->settings->getVendor(), + $this->settings->getPackage(), array( 'sha' => $this->settings->getBranch(), 'path' => $path @@ -218,8 +213,8 @@ final public function guessMimeType($path) final public function show($path) { $fileInfo = $this->getRepositoryContent()->show( - $this->vendor, - $this->package, + $this->settings->getVendor(), + $this->settings->getPackage(), $path, $this->settings->getReference() ); @@ -297,8 +292,8 @@ private function getTreeInfo($recursive) $trees = $this->getGitDataApi()->trees(); $info = $trees->show( - $this->vendor, - $this->package, + $this->settings->getVendor(), + $this->settings->getPackage(), $this->settings->getReference(), $recursive ); diff --git a/src/Settings.php b/src/Settings.php index 48a7862..4ef4f8d 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -6,36 +6,29 @@ class Settings implements SettingsInterface { + ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ const AUTHENTICATE_USING_TOKEN = GithubClient::AUTH_URL_TOKEN; const AUTHENTICATE_USING_PASSWORD = GithubClient::AUTH_HTTP_PASSWORD; - const REFERENCE_HEAD = 'HEAD'; const BRANCH_MASTER = 'master'; + const REFERENCE_HEAD = 'HEAD'; + const ERROR_INVALID_REPOSITORY_NAME = 'Given Repository name "%s" should be in the format of "vendor/project"'; /** @var string */ - private $repository; - /** @var string */ - private $reference = self::REFERENCE_HEAD; + private $branch; /** @var array */ private $credentials; /** @var string */ - private $branch = self::BRANCH_MASTER; - - final public function __construct( - $repository, - array $credentials = [], - $branch = self::BRANCH_MASTER, - $reference = self::REFERENCE_HEAD - ) { - $this->isValidRepositoryName($repository); - - $this->branch = (string) $branch; - $this->credentials = $credentials; - $this->reference = (string) $reference; - $this->repository = (string) $repository; - } + private $reference; + /** @var string */ + private $repository; + /** @var string */ + private $vendor; + /** @var string */ + private $package; + //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @return string */ @@ -52,6 +45,14 @@ final public function getCredentials() return $this->credentials; } + /** + * @return string + */ + final public function getPackage() + { + return $this->package; + } + /** * @return string */ @@ -68,6 +69,32 @@ final public function getRepository() return $this->repository; } + /** + * @return string + */ + final public function getVendor() + { + return $this->vendor; + } + + //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + final public function __construct( + $repository, + array $credentials = [], + $branch = self::BRANCH_MASTER, + $reference = self::REFERENCE_HEAD + ) { + $this->isValidRepositoryName($repository); + + $this->branch = (string) $branch; + $this->credentials = $credentials; + $this->reference = (string) $reference; + $this->repository = (string) $repository; + + list($this->vendor, $this->package) = explode('/', $repository); + } + + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @param $repository */ diff --git a/src/SettingsInterface.php b/src/SettingsInterface.php index d606728..92518bf 100644 --- a/src/SettingsInterface.php +++ b/src/SettingsInterface.php @@ -14,6 +14,11 @@ public function getBranch(); */ public function getCredentials(); + /** + * @return string + */ + public function getPackage(); + /** * @return string */ @@ -23,4 +28,9 @@ public function getReference(); * @return string */ public function getRepository(); + + /** + * @return string + */ + public function getVendor(); } diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php index b45b9b8..bdb1e55 100644 --- a/tests/SettingsTest.php +++ b/tests/SettingsTest.php @@ -11,7 +11,10 @@ */ class SettingsTest extends \PHPUnit_Framework_TestCase { - const MOCK_REPOSITORY_NAME = 'foo/bar'; + ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + const MOCK_VENDOR_NAME = 'mock_vendor'; + const MOCK_PACKAGE_NAME = 'mock_package'; + const MOCK_REPOSITORY_NAME = self::MOCK_VENDOR_NAME.'/'.self::MOCK_PACKAGE_NAME; const MOCK_CREDENTIALS = ['mock_type', 'mock_user', 'mock_password']; const MOCK_BRANCH = 'mock_branch'; const MOCK_REFERENCE = 'mock_reference'; @@ -32,10 +35,11 @@ final protected function setUp() ); } + /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @covers ::__construct */ - final public function testSettingsShouldComplainWhenInstantiatedWithoutProject() + final public function testSettingsShouldComplainWhenInstantiatedWithoutRepositoryName() { $this->setExpectedException( \PHPUnit_Framework_Error_Warning::class, @@ -85,6 +89,34 @@ final public function testSettingsShouldOnlyNeedRepositoryNameWhenInstantiated() $this->assertInstanceOf(Settings::class, $settings); } + /** + * @covers ::getVendor + */ + final public function testSettingsShouldContainVendorNameFromGivenRepositoryWhenInstantiated() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + + $expected = self::MOCK_VENDOR_NAME; + + $actual = $settings->getVendor(); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getPackage + */ + final public function testSettingsShouldContainPackageNameFromGivenRepositoryWhenInstantiated() + { + $settings = new Settings(self::MOCK_REPOSITORY_NAME); + + $expected = self::MOCK_PACKAGE_NAME; + + $actual = $settings->getPackage(); + + $this->assertEquals($expected, $actual); + } + /** * @covers ::getCredentials */ @@ -169,6 +201,7 @@ final public function testSettingsShouldContaingetReferenceItWasGivenGivenWhenIn $this->assertEquals($expected, $actual); } + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ final public function provideInvalidRepositoryNames() { return [ From 5959398f0d61cdb7e96461d8f8ddbd0fb4811b3c Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 16:57:58 +0200 Subject: [PATCH 08/23] Renames and re-ordered various methods in the Client class. --- src/Client.php | 58 +++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/src/Client.php b/src/Client.php index 5f3fbbc..466191d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -14,16 +14,17 @@ class Client ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ const ERROR_NOT_FOUND = 'Not Found'; + const API_GIT_DATA = 'git'; + const API_REPO = 'repo'; + const KEY_BLOB = 'blob'; const KEY_CONTENTS = 'contents'; const KEY_DIRECTORY = 'dir'; const KEY_FILE = 'file'; const KEY_FILENAME = 'basename'; - const KEY_GIT_DATA = 'git'; const KEY_MODE = 'mode'; const KEY_NAME = 'name'; const KEY_PATH = 'path'; - const KEY_REPO = 'repo'; const KEY_SHA = 'sha'; const KEY_SIZE = 'size'; const KEY_STREAM = 'stream'; @@ -53,7 +54,7 @@ private function getApi($name) */ private function getGitDataApi() { - return $this->getApi(self::KEY_GIT_DATA); + return $this->getApi(self::API_GIT_DATA); } /** @@ -61,7 +62,7 @@ private function getGitDataApi() */ private function getRepositoryApi() { - return $this->getApi(self::KEY_REPO); + return $this->getApi(self::API_REPO); } /** @@ -84,32 +85,30 @@ final public function __construct(GithubClient $client, SettingsInterface $setti } /** - * @param $path - * - * @return null|string + * @param string $path * - * @throws \Github\Exception\ErrorException + * @return bool */ - final public function download($path) + final public function exists($path) { - $fileContent = $this->getRepositoryContent()->download( + return $this->getRepositoryContent()->exists( $this->settings->getVendor(), $this->settings->getPackage(), $path, $this->settings->getReference() ); - - return $fileContent; } /** - * @param string $path + * @param $path * - * @return bool + * @return null|string + * + * @throws \Github\Exception\ErrorException */ - final public function exists($path) + final public function getFileContents($path) { - return $this->getRepositoryContent()->exists( + return $this->getRepositoryContent()->download( $this->settings->getVendor(), $this->settings->getPackage(), $path, @@ -150,7 +149,12 @@ final public function getLastUpdatedTimestamp($path) final public function getMetaData($path) { try { - $metadata = $this->show($path); + $metadata = $this->getRepositoryContent()->show( + $this->settings->getVendor(), + $this->settings->getPackage(), + $path, + $this->settings->getReference() + ); } catch (RuntimeException $exception) { if ($exception->getMessage() === self::ERROR_NOT_FOUND) { $metadata = false; @@ -196,31 +200,13 @@ final public function guessMimeType($path) if (isset($extension)) { $mimeType = MimeType::detectByFileExtension($extension) ?: 'text/plain'; } else { - $content = $this->download($path); + $content = $this->getFileContents($path); $mimeType = MimeType::detectByContent($content); } return $mimeType; } - /** - * Get information about a repository file or directory - * - * @param string $path - * - * @return array - */ - final public function show($path) - { - $fileInfo = $this->getRepositoryContent()->show( - $this->settings->getVendor(), - $this->settings->getPackage(), - $path, - $this->settings->getReference() - ); - return $fileInfo; - } - ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * From 721d5d12763db0ee71ee1add4d223fd72f96f7ba Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 21:38:31 +0200 Subject: [PATCH 09/23] Adds an Interface for the Client class. --- src/Client.php | 46 +++++++++++---------------------- src/ClientInterface.php | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 src/ClientInterface.php diff --git a/src/Client.php b/src/Client.php index 466191d..89982ed 100644 --- a/src/Client.php +++ b/src/Client.php @@ -9,7 +9,7 @@ use League\Flysystem\AdapterInterface; use League\Flysystem\Util\MimeType; -class Client +class Client implements ClientInterface { ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ const ERROR_NOT_FOUND = 'Not Found'; @@ -37,6 +37,8 @@ class Client private $client; /** @var Settings */ private $settings; + /** @var bool */ + private $isAuthenticationAttempted = false; //////////////////////////// SETTERS AND GETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\ /** @@ -172,17 +174,22 @@ final public function getMetaData($path) * * @return array */ - final public function getMetadataFromTree($path, $recursive) + 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, // multiple calls will be needed - $info = $this->getTreeInfo($recursive); - $treeMetadata = $this->extractMetaDataFromTreeInfo($info, $path, $recursive); - $metadata = $this->normalizeTreeMetadata($treeMetadata); + $info = $this->getGitDataApi()->trees()->show( + $this->settings->getVendor(), + $this->settings->getPackage(), + $this->settings->getReference(), + $recursive + ); - return $metadata; + $treeMetadata = $this->extractMetaDataFromTreeInfo($info[self::KEY_TREE], $path, $recursive); + + return $this->normalizeTreeMetadata($treeMetadata); } /** @@ -195,9 +202,6 @@ 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); - } - - if (isset($extension)) { $mimeType = MimeType::detectByFileExtension($extension) ?: 'text/plain'; } else { $content = $this->getFileContents($path); @@ -213,9 +217,7 @@ final public function guessMimeType($path) */ private function authenticate() { - static $hasRun; - - if ($hasRun === null) { + if ($this->isAuthenticationAttempted === false) { $credentials = $this->settings->getCredentials(); if (empty($credentials) === false) { @@ -230,7 +232,7 @@ private function authenticate() $credentials[0] ); } - $hasRun = true; + $this->isAuthenticationAttempted = true; } } @@ -269,24 +271,6 @@ private function extractMetaDataFromTreeInfo(array $tree, $path, $recursive) return $metadata; } - /** - * @param bool $recursive - * @return \Guzzle\Http\EntityBodyInterface|mixed|string - */ - private function getTreeInfo($recursive) - { - $trees = $this->getGitDataApi()->trees(); - - $info = $trees->show( - $this->settings->getVendor(), - $this->settings->getPackage(), - $this->settings->getReference(), - $recursive - ); - - return $info[self::KEY_TREE]; - } - /** * @param $permissions * @return string diff --git a/src/ClientInterface.php b/src/ClientInterface.php new file mode 100644 index 0000000..dd65de7 --- /dev/null +++ b/src/ClientInterface.php @@ -0,0 +1,56 @@ + Date: Sat, 18 Jul 2015 21:39:34 +0200 Subject: [PATCH 10/23] Adds unit tests for the Client class. --- tests/ClientTest.php | 771 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 771 insertions(+) create mode 100644 tests/ClientTest.php diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..47e978b --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,771 @@ + + * @covers ::__construct + */ +class ClientTest extends \PHPUnit_Framework_TestCase +{ + ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + const MOCK_FILE_PATH = '/path/to/mock/file'; + const MOCK_FILE_CONTENTS = 'Mock file contents'; + + /** @var Client */ + private $client; + /** @var GithubClient|\PHPUnit_Framework_MockObject_MockObject */ + private $mockClient; + /** @var Settings|\PHPUnit_Framework_MockObject_MockObject */ + private $mockSettings; + + protected function setUp() + { + $this->mockClient = $this->getMockClient(); + $this->mockSettings = $this->getMockSettings(); + + $this->client = new Client($this->mockClient, $this->mockSettings); + } + + private function getClient() + { + return $this->client; + } + + /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @uses Potherca\Flysystem\Github\Client::exists + */ + final public function testClientShouldComplainWhenInstantiatedWithoutGithubClient() + { + $message = sprintf( + 'Argument %d passed to %s::__construct() must be an instance of %s', + 1, + Client::class, + GithubClient::class + ); + + $this->setExpectedException( + \PHPUnit_Framework_Error::class, + $message + ); + + /** @noinspection PhpParamsInspection */ + new Client(); + } + + /** + * @coversNothing + */ + final public function testClientShouldComplainWhenInstantiatedWithoutSettings() + { + $message = sprintf( + 'Argument %d passed to %s::__construct() must implement interface %s', + 2, + Client::class, + SettingsInterface::class + ); + + $this->setExpectedException( + \PHPUnit_Framework_Error::class, + $message + ); + + /** @noinspection PhpParamsInspection */ + new Client($this->getMockClient()); + } + + /** + * @covers ::getFileContents + */ + final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileContent() + { + $client = $this->getClient(); + + $expected = self::MOCK_FILE_CONTENTS; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'download', + $client::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], + $expected + ); + + $actual = $client->getFileContents(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::exists + */ + final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFileExists() + { + $client = $this->getClient(); + + $expected = self::MOCK_FILE_CONTENTS; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'exists', + $client::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], + $expected + ); + + $actual = $client->exists(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getLastUpdatedTimestamp + */ + final public function testClientShouldUseValuesFromSettingsWhenAskingClientForgetLastUpdatedTimestamp() + { + $client = $this->getClient(); + + $expected = ['timestamp' => 1420066800]; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockBranch = 'branch'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getBranch' => $mockBranch, + ]); + + $apiParameters = [ + $mockVendor, + $mockPackage, + [ + 'sha' => $mockBranch, + 'path' => self::MOCK_FILE_PATH + ] + + ]; + $apiOutput = [ + ['commit' => ['committer' => ['date' => '20150101']]], + ['commit' => ['committer' => ['date' => '20140202']]], + ['commit' => ['committer' => ['date' => '20130303']]], + ]; + + $this->prepareMockApi( + 'all', + $client::API_REPO, + $apiParameters, + $apiOutput, + Commits::class + ); + + $actual = $client->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getMetaData + */ + final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileInfo() + { + $client = $this->getClient(); + + $expected = self::MOCK_FILE_CONTENTS; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'show', + $client::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], + $expected + ); + + $actual = $client->getMetaData(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getMetaData + */ + final public function testClientShouldAccountForFileNotExistingWhenAskingInfoForFile() + { + $client = $this->getClient(); + + $expected = false; + + $this->mockClient->expects($this->exactly(1)) + ->method('api') + ->willThrowException(new RuntimeException(Client::ERROR_NOT_FOUND)); + + $actual = $client->getMetaData(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getMetaData + */ + final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoForFileCausesRuntimeException() + { + $client = $this->getClient(); + + $this->setExpectedException(RuntimeException::class, self::MOCK_FILE_CONTENTS); + + $expected = false; + + $this->mockClient->expects($this->exactly(1)) + ->method('api') + ->willThrowException(new RuntimeException(self::MOCK_FILE_CONTENTS)); + + $actual = $client->getMetaData(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getMetaData + */ + final public function testClientShouldPassOnExceptionsWhenAskingInfoForFileCausesAnException() + { + $client = $this->getClient(); + + $this->setExpectedException(\RuntimeException::class, Client::ERROR_NOT_FOUND); + + $expected = false; + + $this->mockClient->expects($this->exactly(1)) + ->method('api') + ->willThrowException(new \RuntimeException(Client::ERROR_NOT_FOUND)); + + $actual = $client->getMetaData(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::getRecursiveMetadata + * + * @dataProvider provideExpectedMetadata + * + * @param string $path + * @param array $expected + * @param bool $recursive + * @param bool $truncated + */ + final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecursiveMetadata( + $path, + $expected, + $recursive, + $truncated + ) { + $client = $this->getClient(); + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $this->prepareMockApi( + 'show', + $client::API_GIT_DATA, + [$mockVendor, $mockPackage, $mockReference, $recursive], + $this->getMockApiTreeResponse($truncated, $client), + Trees::class + ); + + $actual = $client->getRecursiveMetadata($path, $recursive); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::guessMimeType + */ + final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() + { + $client = $this->getClient(); + + $expected = 'image/png'; + + $this->mockClient->expects($this->never())->method('api'); + + $actual = $client->guessMimeType(self::MOCK_FILE_PATH.'.png'); + + $this->assertEquals($expected, $actual); + } + + /** + * @covers ::guessMimeType + * + * @uses Potherca\Flysystem\Github\Client::getFileContents + */ + final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() + { + $client = $this->getClient(); + + $expected = 'image/png'; + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + ]); + + $image = imagecreatetruecolor(1,1); + ob_start(); + imagepng($image); + $contents = ob_get_contents(); + ob_end_clean(); + imagedestroy($image); + + $this->prepareMockApi( + 'download', + $client::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], + $contents + ); + + $actual = $client->guessMimeType(self::MOCK_FILE_PATH); + + $this->assertEquals($expected, $actual); + } + + /** + * @uses Potherca\Flysystem\Github\Client::exists + */ + final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() + { + $client = $this->getClient(); + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockReference = 'reference'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getReference' => $mockReference, + 'getCredentials' => ['foo'] + ]); + + $this->prepareMockApi( + 'exists', + $client::API_REPO, + [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], + '' + ); + + $this->mockClient->expects($this->exactly(1)) + ->method('authenticate') + ; + + $client->exists(self::MOCK_FILE_PATH); + } + ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @return GithubClient|\PHPUnit_Framework_MockObject_MockObject + */ + private function getMockClient() + { + return $this->getMockBuilder(GithubClient::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return Settings|\PHPUnit_Framework_MockObject_MockObject + */ + private function getMockSettings() + { + return $this->getMockBuilder(SettingsInterface::class) + ->getMock(); + } + + /** + * @param string $method + * @param string $apiName + * @param array $apiParameters + * @param mixed $apiOutput + * @param string $repositoryClass + */ + private function prepareMockApi($method, $apiName, $apiParameters, $apiOutput, $repositoryClass = Contents::class) + { + + $parts = explode('\\', $repositoryClass); + $repositoryName = strtolower(array_pop($parts)); + + $mockApi = $this->getMockBuilder(ApiInterface::class) + ->setMethods([$repositoryName, 'getPerPage', 'setPerPage']) + ->getMock() + ; + + $mockRepository = $this->getMockBuilder($repositoryClass) + ->disableOriginalConstructor() + ->getMock() + ; + + $mockRepository->expects($this->exactly(1)) + ->method($method) + ->withAnyParameters() + ->willReturnCallback(function () use ($apiParameters, $apiOutput) { + $this->assertEquals($apiParameters, func_get_args()); + return $apiOutput; + }) + ; + + $mockApi->expects($this->exactly(1)) + ->method($repositoryName) + ->willReturn($mockRepository) + ; + + $this->mockClient->expects($this->exactly(1)) + ->method('api') + ->with($apiName) + ->willReturn($mockApi) + ; + } + + /** + * @param array $expectations + */ + private function prepareMockSettings(array $expectations) + { + foreach ($expectations as $methodName => $returnValue) { + $this->mockSettings->expects($this->exactly(1)) + ->method($methodName) + ->willReturn($returnValue) + ; + } + } + + /** + * @param $truncated + * @param $client + * @return array + */ + private function getMockApiTreeResponse($truncated, $client) + { + return [ + $client::KEY_TREE => [ + [ + 'path' => self::MOCK_FILE_PATH, + 'mode' => '100644', + 'type' => 'tree', + 'size' => 57, + ], + [ + 'path' => self::MOCK_FILE_PATH . 'Foo', + 'basename' => self::MOCK_FILE_PATH . 'Foo', + 'mode' => '100644', + 'type' => 'blob', + 'size' => 57, + ], + [ + 'path' => self::MOCK_FILE_PATH . '/Bar', + 'name' => self::MOCK_FILE_PATH . '/Bar', + 'mode' => '100644', + 'type' => 'blob', + 'size' => 57, + ], + [ + 'path' => 'some/other/file', + 'mode' => '100644', + 'type' => 'blob', + 'size' => 747, + ], + ], + 'truncated' => $truncated, + ]; + } + + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @return array + */ + final public function provideExpectedMetadata() + { + return [ + 'Filepath, not recursive, not truncated' => [ + self::MOCK_FILE_PATH, + [ + [ + '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' + ] + ], + false, + false + ], + 'Filepath, recursive, not truncated' => [ + self::MOCK_FILE_PATH, + [ + [ + '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' + ] + ], + true, + false + ], + 'Filepath, not recursive, truncated' => [ + self::MOCK_FILE_PATH, + [ + [ + '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' + ] + ], + false, + true + ], + 'No Filepath, recursive, not truncated' => [ + '', + [ + [ + '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' + ], + [ + 'path' => 'some/other/file', + 'mode' => 100644, + 'type' => 'file', + 'size' => 747, + 'name' => 'some/other/file', + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => 'public' + ] + ], + true, + false + ], + 'No Filepath, recursive, truncated' => [ + '', + [ + [ + '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' + ], + [ + 'path' => 'some/other/file', + 'mode' => 100644, + 'type' => 'file', + 'size' => 747, + 'name' => 'some/other/file', + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => 'public' + ] + ], + true, + true + ], + 'No Filepath, not recursive, truncated' => [ + '', + [ + [ + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => null + ] + ], + false, + true + ], + 'No Filepath, not recursive, not truncated' => [ + '', + [ + [ + 'contents' => null, + 'stream' => null, + 'timestamp' => null, + 'visibility' => null + ] + ], + false, + false + ], + ]; + } +} From 72a66340bdae31ca7294e07c6e037b55c1d157c0 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 22:19:57 +0200 Subject: [PATCH 11/23] Moves vendor and package name from Client to Settings class --- .travis.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..23463ad --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +sudo: false + +matrix: + allow_failures: + - php: + - 7.0 + - hhvm + +install: + - composer install + +script: + - composer test + +after_script: + # @TODO: ocular or coveralls? + cat build/testdox.txt build/coverage.txt From 90c696d65354f7113f07dcd37c2ff9d8806636d0 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 22:24:22 +0200 Subject: [PATCH 12/23] Fixes for build - Adds timezone for time related unit test in ClientTest.php - Sets minimum PHP version in Composer to 5.4 - PHP 5.4 and HHVM Compatibility in SettingsTest.php --- composer.json | 2 +- phpunit.xml.dist | 7 ++++--- tests/ClientTest.php | 4 +++- tests/SettingsTest.php | 39 +++++++++++++++++++++++++++------------ 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index 4204968..14e5880 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php" : ">=5.5.0", + "php" : ">=5.4.0", "knplabs/github-api": "^1.4", "league/flysystem": "^1.0" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 43d1256..9a2ec48 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -29,10 +29,11 @@ + - - - + + + diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 47e978b..24f7893 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -153,8 +153,9 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFil final public function testClientShouldUseValuesFromSettingsWhenAskingClientForgetLastUpdatedTimestamp() { $client = $this->getClient(); + date_default_timezone_set('UTC'); - $expected = ['timestamp' => 1420066800]; + $expected = ['timestamp' => 1420070400]; $mockVendor = 'vendor'; $mockPackage = 'package'; @@ -411,6 +412,7 @@ final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() $client->exists(self::MOCK_FILE_PATH); } + ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @return GithubClient|\PHPUnit_Framework_MockObject_MockObject diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php index bdb1e55..bd46a24 100644 --- a/tests/SettingsTest.php +++ b/tests/SettingsTest.php @@ -14,8 +14,6 @@ class SettingsTest extends \PHPUnit_Framework_TestCase ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ const MOCK_VENDOR_NAME = 'mock_vendor'; const MOCK_PACKAGE_NAME = 'mock_package'; - const MOCK_REPOSITORY_NAME = self::MOCK_VENDOR_NAME.'/'.self::MOCK_PACKAGE_NAME; - const MOCK_CREDENTIALS = ['mock_type', 'mock_user', 'mock_password']; const MOCK_BRANCH = 'mock_branch'; const MOCK_REFERENCE = 'mock_reference'; @@ -28,8 +26,8 @@ class SettingsTest extends \PHPUnit_Framework_TestCase final protected function setUp() { $this->settings = new Settings( - self::MOCK_REPOSITORY_NAME, - self::MOCK_CREDENTIALS, + $this->getMockRespositoryName(), + $this->getMockCredentials(), self::MOCK_BRANCH, self::MOCK_REFERENCE ); @@ -57,7 +55,7 @@ final public function testSettingsShouldContainRepositoryItWasGivenGivenWhenInst { $settings = $this->settings; - $expected = self::MOCK_REPOSITORY_NAME; + $expected = $this->getMockRespositoryName(); $actual = $settings->getRepository(); @@ -85,7 +83,7 @@ final public function testSettingsShouldComplainWhenGivenInvalidRepositoryNames( */ final public function testSettingsShouldOnlyNeedRepositoryNameWhenInstantiated() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $this->assertInstanceOf(Settings::class, $settings); } @@ -94,7 +92,7 @@ final public function testSettingsShouldOnlyNeedRepositoryNameWhenInstantiated() */ final public function testSettingsShouldContainVendorNameFromGivenRepositoryWhenInstantiated() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $expected = self::MOCK_VENDOR_NAME; @@ -108,7 +106,7 @@ final public function testSettingsShouldContainVendorNameFromGivenRepositoryWhen */ final public function testSettingsShouldContainPackageNameFromGivenRepositoryWhenInstantiated() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $expected = self::MOCK_PACKAGE_NAME; @@ -122,7 +120,7 @@ final public function testSettingsShouldContainPackageNameFromGivenRepositoryWhe */ final public function testSettingsShouldContainEmptyCredentialsWhenInstantiatedWithoutCredentials() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $expected = []; @@ -138,7 +136,7 @@ final public function testSettingsShouldContainCredentialsItWasGivenGivenWhenIns { $settings = $this->settings; - $expected = self::MOCK_CREDENTIALS; + $expected = $this->getMockCredentials(); $actual = $settings->getCredentials(); @@ -150,7 +148,7 @@ final public function testSettingsShouldContainCredentialsItWasGivenGivenWhenIns */ final public function testSettingsShouldContainMasterAsBranchWhenInstantiatedWithoutBranch() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $expected = 'master'; @@ -178,7 +176,7 @@ final public function testSettingsShouldContainBranchItWasGivenGivenWhenInstanti */ final public function testSettingsShouldContainHeadAsReferenceWhenInstantiatedWithoutReference() { - $settings = new Settings(self::MOCK_REPOSITORY_NAME); + $settings = new Settings($this->getMockRespositoryName()); $expected = 'HEAD'; @@ -201,6 +199,23 @@ final public function testSettingsShouldContaingetReferenceItWasGivenGivenWhenIn $this->assertEquals($expected, $actual); } + ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @return string + */ + private function getMockRespositoryName() + { + return self::MOCK_VENDOR_NAME . '/' . self::MOCK_PACKAGE_NAME; + } + + /** + * @return array + */ + private function getMockCredentials() + { + return ['mock_type', 'mock_user', 'mock_password']; + } + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ final public function provideInvalidRepositoryNames() { From 5a523a4ba29c3a4e17bbb2bfe3588e7695655f35 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 22:49:44 +0200 Subject: [PATCH 13/23] Cleanup for Travis build - Creates separate PHPUnit config file for builds - Tweaks the output of Travis - Fixes risky code test --- .travis.yml | 4 ++-- build/phpunit.xml | 35 +++++++++++++++++++++++++++++++++++ tests/ClientTest.php | 4 ++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 build/phpunit.xml diff --git a/.travis.yml b/.travis.yml index 23463ad..0c24fcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ install: - composer install script: - - composer test + - vendor/bin/phpunit --configuration build/phpunit.xml + - cat build/testdox.txt build/coverage.txt after_script: # @TODO: ocular or coveralls? - cat build/testdox.txt build/coverage.txt diff --git a/build/phpunit.xml b/build/phpunit.xml new file mode 100644 index 0000000..55a5a26 --- /dev/null +++ b/build/phpunit.xml @@ -0,0 +1,35 @@ + + + + + ../tests + + + + + src/ + + + + + + + + + + diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 24f7893..83dd057 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -327,6 +327,8 @@ final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecu /** * @covers ::guessMimeType + * + * @uses League\Flysystem\Util\MimeType */ final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() { @@ -344,6 +346,8 @@ final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensi /** * @covers ::guessMimeType * + * @uses League\Flysystem\Util\MimeType + * * @uses Potherca\Flysystem\Github\Client::getFileContents */ final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() From 6416025708b6b9ec7b8866ae37896dce7b8147fc Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 22:56:10 +0200 Subject: [PATCH 14/23] Removes unneeded directory name. --- build/phpunit.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/phpunit.xml b/build/phpunit.xml index 55a5a26..df9f636 100644 --- a/build/phpunit.xml +++ b/build/phpunit.xml @@ -26,10 +26,10 @@ - - - - - + + + + + From 5f4c1d8345c8ea155d2ed288432c88baceb3e3da Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 23:04:09 +0200 Subject: [PATCH 15/23] Reverting back to not officially supporting PHP 5.4 --- .travis.yml | 4 +--- composer.json | 2 +- phpunit.xml.dist | 8 +++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c24fcf..bb2f5b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 5.4 - 5.5 - 5.6 - 7.0 @@ -19,8 +18,7 @@ install: - composer install script: - - vendor/bin/phpunit --configuration build/phpunit.xml - - cat build/testdox.txt build/coverage.txt + - vendor/bin/phpunit --configuration build/phpunit.xml && cat build/testdox.txt build/coverage.txt after_script: # @TODO: ocular or coveralls? diff --git a/composer.json b/composer.json index 14e5880..5d67353 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php" : ">=5.4.0", + "php" : ">=5.5", "knplabs/github-api": "^1.4", "league/flysystem": "^1.0" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9a2ec48..9b8dda9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,7 +19,7 @@ printerClass="NyanCat\PHPUnit\ResultPrinter" > - + tests @@ -29,11 +29,9 @@ - - - + - + From 4d02fbce38547734e157053a6a1923c0908b8e05 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 23:09:20 +0200 Subject: [PATCH 16/23] Cleanup for Travis build --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb2f5b7..e56ebde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,8 @@ sudo: false matrix: allow_failures: - - php: - - 7.0 - - hhvm + - php: 7.0 + - php: hhvm install: - composer install From fefccc548487453692e6566078b150d7025b37f8 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sat, 18 Jul 2015 23:25:06 +0200 Subject: [PATCH 17/23] Adds support for Coveralls. --- .gitattributes | 9 +++++++++ .travis.yml | 2 +- README.md | 3 +-- composer.json | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0d76554 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* text=auto + +/tests export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/phpunit.xml.dist export-ignore +/CHANGELOG.md export-ignore +/README.md export-ignore diff --git a/.travis.yml b/.travis.yml index e56ebde..093a889 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,4 +20,4 @@ script: - vendor/bin/phpunit --configuration build/phpunit.xml && cat build/testdox.txt build/coverage.txt after_script: - # @TODO: ocular or coveralls? + - php vendor/bin/coveralls -v diff --git a/README.md b/README.md index bc81427..0cdbf85 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,10 @@ [![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/master.svg?style=flat-square)](https://travis-ci.org/potherca/flysystem-github) -[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/potherca/flysystem-github.svg?style=flat-square)](https://scrutinizer-ci.com/g/potherca/flysystem-github/code-structure) +[![Coverage Status](https://img.shields.io/coveralls/potherca/flysystem-github.svg?style=flat-square)](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) - ## Install Via Composer diff --git a/composer.json b/composer.json index 5d67353..bc1dfeb 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ }, "require-dev": { "phpunit/phpunit" : "^4.7.7", + "satooshi/php-coveralls": "^0.6.1", "scrutinizer/ocular": "^1.1", "whatthejeff/nyancat-phpunit-resultprinter": "^1.2" }, From d71619ffb9bc24f82aa0cc8f32ebefecb2c04f48 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 19 Jul 2015 01:57:39 +0200 Subject: [PATCH 18/23] Code cleanup for Client class. --- src/Client.php | 145 +++++++++++++++++++++++++++------------- src/ClientInterface.php | 2 + tests/ClientTest.php | 123 ++++++++++++++++++++-------------- 3 files changed, 173 insertions(+), 97 deletions(-) diff --git a/src/Client.php b/src/Client.php index 89982ed..76d8e70 100644 --- a/src/Client.php +++ b/src/Client.php @@ -18,7 +18,6 @@ class Client implements ClientInterface const API_REPO = 'repo'; const KEY_BLOB = 'blob'; - const KEY_CONTENTS = 'contents'; const KEY_DIRECTORY = 'dir'; const KEY_FILE = 'file'; const KEY_FILENAME = 'basename'; @@ -32,10 +31,11 @@ class Client implements ClientInterface const KEY_TREE = 'tree'; const KEY_TYPE = 'type'; const KEY_VISIBILITY = 'visibility'; + const ERROR_NO_NAME = 'Could not set name for entry'; /** @var GithubClient */ private $client; - /** @var Settings */ + /** @var SettingsInterface */ private $settings; /** @var bool */ private $isAuthenticationAttempted = false; @@ -125,24 +125,31 @@ final public function getFileContents($path) */ final public function getLastUpdatedTimestamp($path) { - // List commits for a file - $commits = $this->getRepositoryApi()->commits()->all( - $this->settings->getVendor(), - $this->settings->getPackage(), - array( - 'sha' => $this->settings->getBranch(), - 'path' => $path - ) - ); + $commits = $this->commitsForFile($path); $updated = array_shift($commits); - //@NOTE: $created = array_pop($commits); $time = new \DateTime($updated['commit']['committer']['date']); return ['timestamp' => $time->getTimestamp()]; } + /** + * @param string $path + * + * @return array + */ + final public function getCreatedTimestamp($path) + { + $commits = $this->commitsForFile($path); + + $created = array_pop($commits); + + $time = new \DateTime($created['commit']['committer']['date']); + + return ['timestamp' => $time->getTimestamp()]; + } + /** * @param string $path * @@ -294,49 +301,95 @@ private function normalizeTreeMetadata($metadata) } foreach ($metadata as $entry) { - if (isset($entry[self::KEY_NAME]) === false){ - if(isset($entry[self::KEY_FILENAME]) === true) { - $entry[self::KEY_NAME] = $entry[self::KEY_FILENAME]; - } elseif(isset($entry[self::KEY_PATH]) === true) { - $entry[self::KEY_NAME] = $entry[self::KEY_PATH]; - } else { - // ? - } - } + $this->setEntryName($entry); + $this->setEntryType($entry); + $this->setEntryVisibility($entry); - if (isset($entry[self::KEY_TYPE]) === true) { - switch ($entry[self::KEY_TYPE]) { - case self::KEY_BLOB: - $entry[self::KEY_TYPE] = self::KEY_FILE; - break; + $this->setDefaultValue($entry, self::KEY_CONTENTS); + $this->setDefaultValue($entry, self::KEY_STREAM); + $this->setDefaultValue($entry, self::KEY_TIMESTAMP); - case self::KEY_TREE: - $entry[self::KEY_TYPE] = self::KEY_DIRECTORY; - break; - } - } - if (isset($entry[self::KEY_CONTENTS]) === false) { - $entry[self::KEY_CONTENTS] = false; - } + $result[] = $entry; + } - if (isset($entry[self::KEY_STREAM]) === false) { - $entry[self::KEY_STREAM] = false; - } + return $result; + } - if (isset($entry[self::KEY_TIMESTAMP]) === false) { - $entry[self::KEY_TIMESTAMP] = false; - } + /** + * @param $path + * + * @return array + */ + private function commitsForFile($path) + { + return $this->getRepositoryApi()->commits()->all( + $this->settings->getVendor(), + $this->settings->getPackage(), + array( + 'sha' => $this->settings->getBranch(), + 'path' => $path + ) + ); + } - if (isset($entry[self::KEY_MODE])) { - $entry[self::KEY_VISIBILITY] = $this->guessVisibility($entry[self::KEY_MODE]); - } else { - $entry[self::KEY_VISIBILITY] = false; + /** + * @param array $entry + * @param string $key + * @param bool $default + * + * @return mixed + */ + private function setDefaultValue(array &$entry, $key, $default = false) + { + if (isset($entry[$key]) === false) { + $entry[$key] = $default; + } + } + + /** + * @param $entry + */ + private function setEntryType(&$entry) + { + if (isset($entry[self::KEY_TYPE]) === true) { + switch ($entry[self::KEY_TYPE]) { + case self::KEY_BLOB: + $entry[self::KEY_TYPE] = self::KEY_FILE; + break; + + case self::KEY_TREE: + $entry[self::KEY_TYPE] = self::KEY_DIRECTORY; + break; } + } + } - $result[] = $entry; + /** + * @param $entry + */ + private function setEntryVisibility(&$entry) + { + if (isset($entry[self::KEY_MODE])) { + $entry[self::KEY_VISIBILITY] = $this->guessVisibility($entry[self::KEY_MODE]); + } else { + $entry[self::KEY_VISIBILITY] = false; } + } - return $result; + /** + * @param $entry + */ + private function setEntryName(&$entry) + { + if (isset($entry[self::KEY_NAME]) === false) { + if (isset($entry[self::KEY_FILENAME]) === true) { + $entry[self::KEY_NAME] = $entry[self::KEY_FILENAME]; + } elseif (isset($entry[self::KEY_PATH]) === true) { + $entry[self::KEY_NAME] = $entry[self::KEY_PATH]; + } else { + $entry[self::KEY_NAME] = null; + } + } } } diff --git a/src/ClientInterface.php b/src/ClientInterface.php index dd65de7..580cc1b 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -9,6 +9,8 @@ interface ClientInterface { + const KEY_CONTENTS = 'contents'; + /** * @param string $path * diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 83dd057..da52ae7 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -37,11 +37,6 @@ protected function setUp() $this->client = new Client($this->mockClient, $this->mockSettings); } - private function getClient() - { - return $this->client; - } - /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @uses Potherca\Flysystem\Github\Client::exists @@ -90,7 +85,7 @@ final public function testClientShouldComplainWhenInstantiatedWithoutSettings() */ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileContent() { - $client = $this->getClient(); + $client = $this->client; $expected = self::MOCK_FILE_CONTENTS; @@ -121,7 +116,7 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi */ final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFileExists() { - $client = $this->getClient(); + $client = $this->client; $expected = self::MOCK_FILE_CONTENTS; @@ -150,57 +145,42 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFil /** * @covers ::getLastUpdatedTimestamp */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientForgetLastUpdatedTimestamp() + final public function testClientShouldUseValuesFromSettingsWhenAskingClientForLastUpdatedTimestamp() { - $client = $this->getClient(); - date_default_timezone_set('UTC'); + $client = $this->client; $expected = ['timestamp' => 1420070400]; - $mockVendor = 'vendor'; - $mockPackage = 'package'; - $mockBranch = 'branch'; + $this->prepareFixturesForTimeStamp(); - $this->prepareMockSettings([ - 'getVendor' => $mockVendor, - 'getPackage' => $mockPackage, - 'getBranch' => $mockBranch, - ]); + $actual = $client->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); - $apiParameters = [ - $mockVendor, - $mockPackage, - [ - 'sha' => $mockBranch, - 'path' => self::MOCK_FILE_PATH - ] + $this->assertEquals($expected, $actual); + } - ]; - $apiOutput = [ - ['commit' => ['committer' => ['date' => '20150101']]], - ['commit' => ['committer' => ['date' => '20140202']]], - ['commit' => ['committer' => ['date' => '20130303']]], - ]; + /** + * @covers ::getCreatedTimestamp + * + * @param Client $client + */ + final public function testClientShouldUseValuesFromSettingsWhenAskingClientForCreatedTimestamp() + { + $client = $this->client; - $this->prepareMockApi( - 'all', - $client::API_REPO, - $apiParameters, - $apiOutput, - Commits::class - ); + $expected = ['timestamp' => 1362268800]; - $actual = $client->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); + $this->prepareFixturesForTimeStamp(); + + $actual = $client->getCreatedTimestamp(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } - /** * @covers ::getMetaData */ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileInfo() { - $client = $this->getClient(); + $client = $this->client; $expected = self::MOCK_FILE_CONTENTS; @@ -231,7 +211,7 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi */ final public function testClientShouldAccountForFileNotExistingWhenAskingInfoForFile() { - $client = $this->getClient(); + $client = $this->client; $expected = false; @@ -249,7 +229,7 @@ final public function testClientShouldAccountForFileNotExistingWhenAskingInfoFor */ final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoForFileCausesRuntimeException() { - $client = $this->getClient(); + $client = $this->client; $this->setExpectedException(RuntimeException::class, self::MOCK_FILE_CONTENTS); @@ -269,7 +249,7 @@ final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoFo */ final public function testClientShouldPassOnExceptionsWhenAskingInfoForFileCausesAnException() { - $client = $this->getClient(); + $client = $this->client; $this->setExpectedException(\RuntimeException::class, Client::ERROR_NOT_FOUND); @@ -300,7 +280,7 @@ final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecu $recursive, $truncated ) { - $client = $this->getClient(); + $client = $this->client; $mockVendor = 'vendor'; $mockPackage = 'package'; @@ -332,7 +312,7 @@ final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecu */ final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() { - $client = $this->getClient(); + $client = $this->client; $expected = 'image/png'; @@ -352,7 +332,7 @@ final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensi */ final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() { - $client = $this->getClient(); + $client = $this->client; $expected = 'image/png'; @@ -390,7 +370,7 @@ final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensio */ final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() { - $client = $this->getClient(); + $client = $this->client; $mockVendor = 'vendor'; $mockPackage = 'package'; @@ -534,6 +514,45 @@ private function getMockApiTreeResponse($truncated, $client) ]; } + private function prepareFixturesForTimeStamp() + { + date_default_timezone_set('UTC'); + + $mockVendor = 'vendor'; + $mockPackage = 'package'; + $mockBranch = 'branch'; + + $this->prepareMockSettings([ + 'getVendor' => $mockVendor, + 'getPackage' => $mockPackage, + 'getBranch' => $mockBranch, + ]); + + $apiParameters = [ + $mockVendor, + $mockPackage, + [ + 'sha' => $mockBranch, + 'path' => self::MOCK_FILE_PATH + ] + + ]; + + $apiOutput = [ + ['commit' => ['committer' => ['date' => '20150101']]], + ['commit' => ['committer' => ['date' => '20140202']]], + ['commit' => ['committer' => ['date' => '20130303']]], + ]; + + $this->prepareMockApi( + 'all', + Client::API_REPO, + $apiParameters, + $apiOutput, + Commits::class + ); + } + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** * @return array @@ -750,10 +769,11 @@ final public function provideExpectedMetadata() '', [ [ + 'name' => null, + 'visibility' => null, 'contents' => null, 'stream' => null, - 'timestamp' => null, - 'visibility' => null + 'timestamp' => null ] ], false, @@ -763,10 +783,11 @@ final public function provideExpectedMetadata() '', [ [ + 'name' => null, + 'visibility' => null, 'contents' => null, 'stream' => null, 'timestamp' => null, - 'visibility' => null ] ], false, From e32b673592dc50b9a1fa54ff273cec4611aa38ca Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 19 Jul 2015 01:59:22 +0200 Subject: [PATCH 19/23] Updates GithubAdapter class to changes from other classes. --- src/GithubAdapter.php | 44 +++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/GithubAdapter.php b/src/GithubAdapter.php index c4e5d2a..eb12630 100644 --- a/src/GithubAdapter.php +++ b/src/GithubAdapter.php @@ -21,13 +21,21 @@ class GithubAdapter extends AbstractAdapter const VISIBILITY_PRIVATE = 'private'; const VISIBILITY_PUBLIC = 'public'; - /** @var Client */ + /** @var ClientInterface */ private $client; /** - * @param Client $client + * @return ClientInterface */ - public function __construct(Client $client) + final public function getClient() + { + return $this->client; + } + + /** + * @param ClientInterface $client + */ + public function __construct(ClientInterface $client) { $this->client = $client; } @@ -44,7 +52,7 @@ public function __construct(Client $client) public function write($path, $contents, Config $config) { throw new Exception('Write action are not (yet) supported'); - //@TODO: return $this->client->create($path, $contents); + //@TODO: return $this->getClient()->create($path, $contents); } /** @@ -59,7 +67,7 @@ public function write($path, $contents, Config $config) public function update($path, $contents, Config $config) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->update($path, $contents); + // @TODO: return $this->getClient()->update($path, $contents); } /** @@ -73,7 +81,7 @@ public function update($path, $contents, Config $config) public function rename($path, $newpath) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->rename($path, $newPath); + // @TODO: return $this->getClient()->rename($path, $newPath); } /** @@ -87,7 +95,7 @@ public function rename($path, $newpath) public function copy($path, $newpath) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->copy($path, $newPath); + // @TODO: return $this->getClient()->copy($path, $newPath); } /** @@ -100,7 +108,7 @@ public function copy($path, $newpath) public function delete($path) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->delete($path); + // @TODO: return $this->getClient()->delete($path); } /** @@ -113,7 +121,7 @@ public function delete($path) public function deleteDir($dirname) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->deleteDir($dirname); + // @TODO: return $this->getClient()->deleteDir($dirname); } /** @@ -127,7 +135,7 @@ public function deleteDir($dirname) public function createDir($dirname, Config $config) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->client->createDir($dirname); + // @TODO: return $this->getClient()->createDir($dirname); } /** @@ -152,7 +160,7 @@ public function setVisibility($path, $visibility) */ public function has($path) { - return $this->client->exists($path); + return $this->getClient()->exists($path); } /** @@ -164,7 +172,7 @@ public function has($path) */ public function read($path) { - return [Client::KEY_CONTENTS => $this->client->download($path)]; + return [ClientInterface::KEY_CONTENTS => $this->getClient()->getFileContents($path)]; } /** @@ -177,7 +185,7 @@ public function read($path) */ public function listContents($path = '/', $recursive = false) { - return $this->client->metadata($path, $recursive); + return $this->getClient()->getRecursiveMetadata($path, $recursive); } /** @@ -189,7 +197,7 @@ public function listContents($path = '/', $recursive = false) */ public function getMetadata($path) { - return $this->client->show($path); + return $this->getClient()->getMetaData($path); } /** @@ -201,7 +209,7 @@ public function getMetadata($path) */ public function getSize($path) { - return $this->client->getMetaData($path); + return $this->getClient()->getMetaData($path); } /** @@ -213,7 +221,7 @@ public function getSize($path) */ public function getMimetype($path) { - return ['mimetype' => $this->client->guessMimeType($path)]; + return ['mimetype' => $this->getClient()->guessMimeType($path)]; } /** @@ -225,7 +233,7 @@ public function getMimetype($path) */ public function getTimestamp($path) { - return $this->client->updated($path); + return $this->getClient()->getLastUpdatedTimestamp($path); } /** @@ -238,7 +246,7 @@ public function getTimestamp($path) public function getVisibility($path) { $recursive = false; - $metadata = $this->client->metadata($path, $recursive); + $metadata = $this->getClient()->getRecursiveMetadata($path, $recursive); return $metadata[0]; } } From c3f52c38735d84c90ee018319754d392613dcb36 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Sun, 19 Jul 2015 02:00:15 +0200 Subject: [PATCH 20/23] Adds tests for the GithubAdapter class. --- tests/GithubAdapterTest.php | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tests/GithubAdapterTest.php diff --git a/tests/GithubAdapterTest.php b/tests/GithubAdapterTest.php new file mode 100644 index 0000000..49d8876 --- /dev/null +++ b/tests/GithubAdapterTest.php @@ -0,0 +1,76 @@ + + * @covers ::__construct + * @covers ::getClient + */ +class GithubAdapterTest extends \PHPUnit_Framework_TestCase +{ + ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + const MOCK_FILE_PATH = '/path/to/mock/file'; + + /** @var GithubAdapter */ + private $adapter; + /** @var ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $mockClient; + + /** + * + */ + protected function setup() + { + $this->mockClient = $this->getMock(ClientInterface::class); + $this->adapter = new GithubAdapter($this->mockClient); + } + + /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + /** + * @covers ::has + * @covers ::read + * @covers ::listContents + * @covers ::getMetadata + * @covers ::getSize + * @covers ::getMimetype + * @covers ::getTimestamp + * @covers ::getVisibility + * + * @dataProvider provideReadMethods + * + * @param $method + * @param $clientMethod + * @param $parameters + */ + final public function testAdapterShouldPassParameterToClient($method, $clientMethod, $parameters) + { + $mocker = $this->mockClient->expects($this->exactly(1)) + ->method($clientMethod); + + $mocker->getMatcher()->parametersMatcher = new \PHPUnit_Framework_MockObject_Matcher_Parameters($parameters); + + call_user_func_array([$this->adapter, $method], $parameters); + } + + ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + /////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + final public function provideReadMethods() + { + return [ + ['has', 'exists', [self::MOCK_FILE_PATH]], + ['read', 'getFileContents', [self::MOCK_FILE_PATH]], + ['listContents', 'getRecursiveMetadata', [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]], + ]; + } +} From f37f18d05b3f1e835418eafe06e82c72ffd10643 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 21 Jul 2015 23:27:07 +0200 Subject: [PATCH 21/23] Cleanup for Travis build --- README.md | 36 +++-- src/{Client.php => Api.php} | 11 +- src/{ClientInterface.php => ApiInterface.php} | 9 +- src/GithubAdapter.php | 46 +++--- src/Settings.php | 6 +- tests/{ClientTest.php => ApiTest.php} | 145 +++++++++--------- tests/GithubAdapterTest.php | 10 +- 7 files changed, 133 insertions(+), 130 deletions(-) rename src/{Client.php => Api.php} (97%) rename src/{ClientInterface.php => ApiInterface.php} (89%) rename tests/{ClientTest.php => ApiTest.php} (83%) diff --git a/README.md b/README.md index 0cdbf85..854f5c7 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![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/master.svg?style=flat-square)](https://travis-ci.org/potherca/flysystem-github) -[![Coverage Status](https://img.shields.io/coveralls/potherca/flysystem-github.svg?style=flat-square)](https://coveralls.io/github/potherca/flysystem-github) +[![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) @@ -27,9 +27,9 @@ limit. ### Basic Usage ```php -use Github\Client as GithubClient; +use Github\Client; use League\Flysystem\Filesystem; -use Potherca\Flysystem\Github\Client; +use Potherca\Flysystem\Github\Api; use Potherca\Flysystem\Github\GithubAdapter; use Potherca\Flysystem\Github\Settings; @@ -37,17 +37,17 @@ $project = 'thephpleague/flysystem'; $settings = new Settings($project); -$client = new Client(new GithubClient(), $settings); -$adapter = new GithubAdapter($client); +$api = new Api(new Client(), $settings); +$adapter = new GithubAdapter($api); $filesystem = new Filesystem($adapter); ``` ### Authentication ```php -use Github\Client as GithubClient; +use Github\Client; use League\Flysystem\Filesystem; -use Potherca\Flysystem\Github\Client; +use Potherca\Flysystem\Github\Api; use Potherca\Flysystem\Github\GithubAdapter; use Potherca\Flysystem\Github\Settings; @@ -57,19 +57,19 @@ $credentials = [Settings::AUTHENTICATE_USING_TOKEN, '83347e315b8bb4790a48ed6953a $settings = new Settings($project, $credentials); -$client = new Client(new GithubClient(), $settings); -$adapter = new GithubAdapter($client); +$api = new Api(new Client(), $settings); +$adapter = new GithubAdapter($api); $filesystem = new Filesystem($adapter); ``` ### Cache Usage ```php -use Github\Client as GithubClient; +use Github\Client; use Github\HttpClient\CachedHttpClient as CachedClient; use Github\HttpClient\Cache\FilesystemCache as Cache; use League\Flysystem\Filesystem; -use Potherca\Flysystem\Github\Client; +use Potherca\Flysystem\Github\Api; use Potherca\Flysystem\Github\GithubAdapter; use Potherca\Flysystem\Github\Settings; @@ -81,8 +81,8 @@ $cache = new Cache('/tmp/github-api-cache') $cacheClient = new CachedClient(); $cacheClient->setCache($cache); -$client = new Client($cacheClient, $settings); -$adapter = new GithubAdapter($client); +$api = new Api($cacheClient, $settings); +$adapter = new GithubAdapter($api); $filesystem = new Filesystem($adapter); ``` @@ -93,13 +93,17 @@ $filesystem = new Filesystem($adapter); $ composer test ``` +## Security + +If you discover any security related issues, please email potherca@gmail.com instead of using the issue tracker. + ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. -## Security +## Change Log -If you discover any security related issues, please email potherca@gmail.com instead of using the issue tracker. +Please see [CHANGELOG](CHANGELOG.md) for details. ## Credits diff --git a/src/Client.php b/src/Api.php similarity index 97% rename from src/Client.php rename to src/Api.php index 76d8e70..c5fbbef 100644 --- a/src/Client.php +++ b/src/Api.php @@ -4,12 +4,15 @@ use Github\Api\GitData; use Github\Api\Repo; -use Github\Client as GithubClient; +use Github\Client; use Github\Exception\RuntimeException; use League\Flysystem\AdapterInterface; use League\Flysystem\Util\MimeType; -class Client implements ClientInterface +/** + * Facade class for the Github Api Library + */ +class Api implements ApiInterface { ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ const ERROR_NOT_FOUND = 'Not Found'; @@ -33,7 +36,7 @@ class Client implements ClientInterface const KEY_VISIBILITY = 'visibility'; const ERROR_NO_NAME = 'Could not set name for entry'; - /** @var GithubClient */ + /** @var Client */ private $client; /** @var SettingsInterface */ private $settings; @@ -76,7 +79,7 @@ private function getRepositoryContent() } //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - final public function __construct(GithubClient $client, SettingsInterface $settings) + final public function __construct(Client $client, SettingsInterface $settings) { /* @NOTE: If $settings contains `credentials` but not an `author` we are * still in `read-only` mode. diff --git a/src/ClientInterface.php b/src/ApiInterface.php similarity index 89% rename from src/ClientInterface.php rename to src/ApiInterface.php index 580cc1b..d8cb101 100644 --- a/src/ClientInterface.php +++ b/src/ApiInterface.php @@ -1,13 +1,8 @@ client; + return $this->api; } /** - * @param ClientInterface $client + * @param ApiInterface $api */ - public function __construct(ClientInterface $client) + public function __construct(ApiInterface $api) { - $this->client = $client; + $this->api = $api; } /** @@ -52,7 +52,7 @@ public function __construct(ClientInterface $client) public function write($path, $contents, Config $config) { throw new Exception('Write action are not (yet) supported'); - //@TODO: return $this->getClient()->create($path, $contents); + //@TODO: return $this->getApi()->create($path, $contents); } /** @@ -67,7 +67,7 @@ public function write($path, $contents, Config $config) public function update($path, $contents, Config $config) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->update($path, $contents); + // @TODO: return $this->getApi()->update($path, $contents); } /** @@ -81,7 +81,7 @@ public function update($path, $contents, Config $config) public function rename($path, $newpath) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->rename($path, $newPath); + // @TODO: return $this->getApi()->rename($path, $newPath); } /** @@ -95,7 +95,7 @@ public function rename($path, $newpath) public function copy($path, $newpath) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->copy($path, $newPath); + // @TODO: return $this->getApi()->copy($path, $newPath); } /** @@ -108,7 +108,7 @@ public function copy($path, $newpath) public function delete($path) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->delete($path); + // @TODO: return $this->getApi()->delete($path); } /** @@ -121,7 +121,7 @@ public function delete($path) public function deleteDir($dirname) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->deleteDir($dirname); + // @TODO: return $this->getApi()->deleteDir($dirname); } /** @@ -135,7 +135,7 @@ public function deleteDir($dirname) public function createDir($dirname, Config $config) { throw new Exception('Write action are not (yet) supported'); - // @TODO: return $this->getClient()->createDir($dirname); + // @TODO: return $this->getApi()->createDir($dirname); } /** @@ -160,7 +160,7 @@ public function setVisibility($path, $visibility) */ public function has($path) { - return $this->getClient()->exists($path); + return $this->getApi()->exists($path); } /** @@ -172,7 +172,7 @@ public function has($path) */ public function read($path) { - return [ClientInterface::KEY_CONTENTS => $this->getClient()->getFileContents($path)]; + return [ApiInterface::KEY_CONTENTS => $this->getApi()->getFileContents($path)]; } /** @@ -185,7 +185,7 @@ public function read($path) */ public function listContents($path = '/', $recursive = false) { - return $this->getClient()->getRecursiveMetadata($path, $recursive); + return $this->getApi()->getRecursiveMetadata($path, $recursive); } /** @@ -197,7 +197,7 @@ public function listContents($path = '/', $recursive = false) */ public function getMetadata($path) { - return $this->getClient()->getMetaData($path); + return $this->getApi()->getMetaData($path); } /** @@ -209,7 +209,7 @@ public function getMetadata($path) */ public function getSize($path) { - return $this->getClient()->getMetaData($path); + return $this->getApi()->getMetaData($path); } /** @@ -221,7 +221,7 @@ public function getSize($path) */ public function getMimetype($path) { - return ['mimetype' => $this->getClient()->guessMimeType($path)]; + return ['mimetype' => $this->getApi()->guessMimeType($path)]; } /** @@ -233,7 +233,7 @@ public function getMimetype($path) */ public function getTimestamp($path) { - return $this->getClient()->getLastUpdatedTimestamp($path); + return $this->getApi()->getLastUpdatedTimestamp($path); } /** @@ -246,7 +246,7 @@ public function getTimestamp($path) public function getVisibility($path) { $recursive = false; - $metadata = $this->getClient()->getRecursiveMetadata($path, $recursive); + $metadata = $this->getApi()->getRecursiveMetadata($path, $recursive); return $metadata[0]; } } diff --git a/src/Settings.php b/src/Settings.php index 4ef4f8d..8ea14b1 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -2,13 +2,13 @@ namespace Potherca\Flysystem\Github; -use Github\Client as GithubClient; +use Github\Client; class Settings implements SettingsInterface { ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ - const AUTHENTICATE_USING_TOKEN = GithubClient::AUTH_URL_TOKEN; - const AUTHENTICATE_USING_PASSWORD = GithubClient::AUTH_HTTP_PASSWORD; + const AUTHENTICATE_USING_TOKEN = Client::AUTH_URL_TOKEN; + const AUTHENTICATE_USING_PASSWORD = Client::AUTH_HTTP_PASSWORD; const BRANCH_MASTER = 'master'; const REFERENCE_HEAD = 'HEAD'; diff --git a/tests/ClientTest.php b/tests/ApiTest.php similarity index 83% rename from tests/ClientTest.php rename to tests/ApiTest.php index da52ae7..f57ecf0 100644 --- a/tests/ClientTest.php +++ b/tests/ApiTest.php @@ -6,48 +6,51 @@ use Github\Api\GitData\Trees; use Github\Api\Repository\Commits; use Github\Api\Repository\Contents; -use Github\Client as GithubClient; +use Github\Client; use Github\Exception\RuntimeException; /** - * Tests for the Client class + * Tests for the Api class * - * @coversDefaultClass \Potherca\Flysystem\Github\Client + * @coversDefaultClass \Potherca\Flysystem\Github\Api * @covers :: * @covers ::__construct */ -class ClientTest extends \PHPUnit_Framework_TestCase +class ApiTest extends \PHPUnit_Framework_TestCase { ////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ const MOCK_FILE_PATH = '/path/to/mock/file'; const MOCK_FILE_CONTENTS = 'Mock file contents'; - /** @var Client */ - private $client; - /** @var GithubClient|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Api */ + private $api; + /** @var Client|\PHPUnit_Framework_MockObject_MockObject */ private $mockClient; /** @var Settings|\PHPUnit_Framework_MockObject_MockObject */ private $mockSettings; + /** + * + */ protected function setUp() { $this->mockClient = $this->getMockClient(); $this->mockSettings = $this->getMockSettings(); - $this->client = new Client($this->mockClient, $this->mockSettings); + $this->api = new Api($this->mockClient, $this->mockSettings); } /////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** - * @uses Potherca\Flysystem\Github\Client::exists + * @uses Potherca\Flysystem\Github\Api::exists */ - final public function testClientShouldComplainWhenInstantiatedWithoutGithubClient() + final public function testApiShouldComplainWhenInstantiatedWithoutClient() { $message = sprintf( 'Argument %d passed to %s::__construct() must be an instance of %s', 1, - Client::class, - GithubClient::class + Api::class, + Client::class ); $this->setExpectedException( @@ -56,18 +59,18 @@ final public function testClientShouldComplainWhenInstantiatedWithoutGithubClien ); /** @noinspection PhpParamsInspection */ - new Client(); + new Api(); } /** * @coversNothing */ - final public function testClientShouldComplainWhenInstantiatedWithoutSettings() + final public function testApiShouldComplainWhenInstantiatedWithoutSettings() { $message = sprintf( 'Argument %d passed to %s::__construct() must implement interface %s', 2, - Client::class, + Api::class, SettingsInterface::class ); @@ -77,15 +80,15 @@ final public function testClientShouldComplainWhenInstantiatedWithoutSettings() ); /** @noinspection PhpParamsInspection */ - new Client($this->getMockClient()); + new Api($this->getMockClient()); } /** * @covers ::getFileContents */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileContent() + final public function testApiShouldUseValuesFromSettingsWhenAskingClientForFileContent() { - $client = $this->client; + $api = $this->api; $expected = self::MOCK_FILE_CONTENTS; @@ -101,12 +104,12 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi $this->prepareMockApi( 'download', - $client::API_REPO, + $api::API_REPO, [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], $expected ); - $actual = $client->getFileContents(self::MOCK_FILE_PATH); + $actual = $api->getFileContents(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -114,9 +117,9 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi /** * @covers ::exists */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFileExists() + final public function testApiShouldUseValuesFromSettingsWhenAskingClientIfFileExists() { - $client = $this->client; + $api = $this->api; $expected = self::MOCK_FILE_CONTENTS; @@ -132,12 +135,12 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFil $this->prepareMockApi( 'exists', - $client::API_REPO, + $api::API_REPO, [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], $expected ); - $actual = $client->exists(self::MOCK_FILE_PATH); + $actual = $api->exists(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -145,42 +148,40 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientIfFil /** * @covers ::getLastUpdatedTimestamp */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientForLastUpdatedTimestamp() + final public function testApiShouldUseValuesFromSettingsWhenAskingClientForLastUpdatedTimestamp() { - $client = $this->client; + $api = $this->api; $expected = ['timestamp' => 1420070400]; $this->prepareFixturesForTimeStamp(); - $actual = $client->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); + $actual = $api->getLastUpdatedTimestamp(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } /** * @covers ::getCreatedTimestamp - * - * @param Client $client */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientForCreatedTimestamp() + final public function testApiShouldUseValuesFromSettingsWhenAskingClientForCreatedTimestamp() { - $client = $this->client; + $api = $this->api; $expected = ['timestamp' => 1362268800]; $this->prepareFixturesForTimeStamp(); - $actual = $client->getCreatedTimestamp(self::MOCK_FILE_PATH); + $actual = $api->getCreatedTimestamp(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } /** * @covers ::getMetaData */ - final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFileInfo() + final public function testApiShouldUseValuesFromSettingsWhenAskingClientForFileInfo() { - $client = $this->client; + $api = $this->api; $expected = self::MOCK_FILE_CONTENTS; @@ -196,12 +197,12 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi $this->prepareMockApi( 'show', - $client::API_REPO, + $api::API_REPO, [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], $expected ); - $actual = $client->getMetaData(self::MOCK_FILE_PATH); + $actual = $api->getMetaData(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -209,17 +210,17 @@ final public function testClientShouldUseValuesFromSettingsWhenAskingClientForFi /** * @covers ::getMetaData */ - final public function testClientShouldAccountForFileNotExistingWhenAskingInfoForFile() + final public function testApiShouldAccountForFileNotExistingWhenAskingInfoForFile() { - $client = $this->client; + $api = $this->api; $expected = false; $this->mockClient->expects($this->exactly(1)) ->method('api') - ->willThrowException(new RuntimeException(Client::ERROR_NOT_FOUND)); + ->willThrowException(new RuntimeException(Api::ERROR_NOT_FOUND)); - $actual = $client->getMetaData(self::MOCK_FILE_PATH); + $actual = $api->getMetaData(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -227,9 +228,9 @@ final public function testClientShouldAccountForFileNotExistingWhenAskingInfoFor /** * @covers ::getMetaData */ - final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoForFileCausesRuntimeException() + final public function testApiShouldPassOtherRuntimeExceptionsWhenAskingInfoForFileCausesRuntimeException() { - $client = $this->client; + $api = $this->api; $this->setExpectedException(RuntimeException::class, self::MOCK_FILE_CONTENTS); @@ -239,7 +240,7 @@ final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoFo ->method('api') ->willThrowException(new RuntimeException(self::MOCK_FILE_CONTENTS)); - $actual = $client->getMetaData(self::MOCK_FILE_PATH); + $actual = $api->getMetaData(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -247,19 +248,19 @@ final public function testClientShouldPassOtherRuntimeExceptionsWhenAskingInfoFo /** * @covers ::getMetaData */ - final public function testClientShouldPassOnExceptionsWhenAskingInfoForFileCausesAnException() + final public function testApiShouldPassOnExceptionsWhenAskingInfoForFileCausesAnException() { - $client = $this->client; + $api = $this->api; - $this->setExpectedException(\RuntimeException::class, Client::ERROR_NOT_FOUND); + $this->setExpectedException(\RuntimeException::class, Api::ERROR_NOT_FOUND); $expected = false; $this->mockClient->expects($this->exactly(1)) ->method('api') - ->willThrowException(new \RuntimeException(Client::ERROR_NOT_FOUND)); + ->willThrowException(new \RuntimeException(Api::ERROR_NOT_FOUND)); - $actual = $client->getMetaData(self::MOCK_FILE_PATH); + $actual = $api->getMetaData(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } @@ -274,13 +275,13 @@ final public function testClientShouldPassOnExceptionsWhenAskingInfoForFileCause * @param bool $recursive * @param bool $truncated */ - final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecursiveMetadata( + final public function testApiShouldRetrieveExpectedMetadataWhenAskedTogetRecursiveMetadata( $path, $expected, $recursive, $truncated ) { - $client = $this->client; + $api = $this->api; $mockVendor = 'vendor'; $mockPackage = 'package'; @@ -294,13 +295,13 @@ final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecu $this->prepareMockApi( 'show', - $client::API_GIT_DATA, + $api::API_GIT_DATA, [$mockVendor, $mockPackage, $mockReference, $recursive], - $this->getMockApiTreeResponse($truncated, $client), + $this->getMockApiTreeResponse($truncated, $api), Trees::class ); - $actual = $client->getRecursiveMetadata($path, $recursive); + $actual = $api->getRecursiveMetadata($path, $recursive); $this->assertEquals($expected, $actual); } @@ -310,15 +311,15 @@ final public function testClientShouldRetrieveExpectedMetadataWhenAskedTogetRecu * * @uses League\Flysystem\Util\MimeType */ - final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() + final public function testApiShouldUseFileExtensionToGuessMimeTypeWhenExtensionIsAvailable() { - $client = $this->client; + $api = $this->api; $expected = 'image/png'; $this->mockClient->expects($this->never())->method('api'); - $actual = $client->guessMimeType(self::MOCK_FILE_PATH.'.png'); + $actual = $api->guessMimeType(self::MOCK_FILE_PATH.'.png'); $this->assertEquals($expected, $actual); } @@ -328,11 +329,11 @@ final public function testClientShouldUseFileExtensionToGuessMimeTypeWhenExtensi * * @uses League\Flysystem\Util\MimeType * - * @uses Potherca\Flysystem\Github\Client::getFileContents + * @uses Potherca\Flysystem\Github\Api::getFileContents */ - final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() + final public function testApiShouldUseFileContentsToGuessMimeTypeWhenExtensionUnavailable() { - $client = $this->client; + $api = $this->api; $expected = 'image/png'; @@ -355,22 +356,22 @@ final public function testClientShouldUseFileContentsToGuessMimeTypeWhenExtensio $this->prepareMockApi( 'download', - $client::API_REPO, + $api::API_REPO, [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], $contents ); - $actual = $client->guessMimeType(self::MOCK_FILE_PATH); + $actual = $api->guessMimeType(self::MOCK_FILE_PATH); $this->assertEquals($expected, $actual); } /** - * @uses Potherca\Flysystem\Github\Client::exists + * @uses Potherca\Flysystem\Github\Api::exists */ - final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() + final public function testApiShouldUseCredentialsWhenTheyHaveBeenGiven() { - $client = $this->client; + $api = $this->api; $mockVendor = 'vendor'; $mockPackage = 'package'; @@ -385,7 +386,7 @@ final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() $this->prepareMockApi( 'exists', - $client::API_REPO, + $api::API_REPO, [$mockVendor, $mockPackage, self::MOCK_FILE_PATH, $mockReference], '' ); @@ -394,16 +395,16 @@ final public function testClientShouldUseCredentialsWhenTheyHaveBeenGiven() ->method('authenticate') ; - $client->exists(self::MOCK_FILE_PATH); + $api->exists(self::MOCK_FILE_PATH); } ////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ /** - * @return GithubClient|\PHPUnit_Framework_MockObject_MockObject + * @return Client|\PHPUnit_Framework_MockObject_MockObject */ private function getMockClient() { - return $this->getMockBuilder(GithubClient::class) + return $this->getMockBuilder(Client::class) ->disableOriginalConstructor() ->getMock(); } @@ -476,13 +477,13 @@ private function prepareMockSettings(array $expectations) /** * @param $truncated - * @param $client + * @param $api * @return array */ - private function getMockApiTreeResponse($truncated, $client) + private function getMockApiTreeResponse($truncated, $api) { return [ - $client::KEY_TREE => [ + $api::KEY_TREE => [ [ 'path' => self::MOCK_FILE_PATH, 'mode' => '100644', @@ -546,7 +547,7 @@ private function prepareFixturesForTimeStamp() $this->prepareMockApi( 'all', - Client::API_REPO, + Api::API_REPO, $apiParameters, $apiOutput, Commits::class diff --git a/tests/GithubAdapterTest.php b/tests/GithubAdapterTest.php index 49d8876..b471bcf 100644 --- a/tests/GithubAdapterTest.php +++ b/tests/GithubAdapterTest.php @@ -18,7 +18,7 @@ class GithubAdapterTest extends \PHPUnit_Framework_TestCase /** @var GithubAdapter */ private $adapter; - /** @var ClientInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ApiInterface|\PHPUnit_Framework_MockObject_MockObject */ private $mockClient; /** @@ -26,7 +26,7 @@ class GithubAdapterTest extends \PHPUnit_Framework_TestCase */ protected function setup() { - $this->mockClient = $this->getMock(ClientInterface::class); + $this->mockClient = $this->getMock(ApiInterface::class); $this->adapter = new GithubAdapter($this->mockClient); } @@ -44,13 +44,13 @@ protected function setup() * @dataProvider provideReadMethods * * @param $method - * @param $clientMethod + * @param $apiMethod * @param $parameters */ - final public function testAdapterShouldPassParameterToClient($method, $clientMethod, $parameters) + final public function testAdapterShouldPassParameterToClient($method, $apiMethod, $parameters) { $mocker = $this->mockClient->expects($this->exactly(1)) - ->method($clientMethod); + ->method($apiMethod); $mocker->getMatcher()->parametersMatcher = new \PHPUnit_Framework_MockObject_Matcher_Parameters($parameters); From eeeb84ad3d924642e72c26fb2b1ffae56bd99288 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 21 Jul 2015 23:28:11 +0200 Subject: [PATCH 22/23] Updates CHANGELOG file to include new changes. --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 576590e..c386634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,42 @@ # Change Log All notable changes to the `flysystem-github` project will be documented in this -file. This project adheres to [Semantic Versioning](http://semver.org/). +file. This project adheres to the [keep-a-changelog](http://keepachangelog.com/) +and [Semantic Versioning](http://semver.org/) conventions. -## 0.1.0 - 2015-07-18 - Read functionality + + +## 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 -## 0.0.0 - 2015-05-11 - Project Setup ### Added -Set up project basics like .gitignore file, PHPUnit Configuration file, +- 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/flystystem-github/compare/v0.1.0...HEAD +[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 +[keep-a-changelog]: http://keepachangelog.com/ +[Semantic Versioning]: http://semver.org/ From 9f77b42adc882ad62f6e5b188dc427dbbce5aa7c Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Tue, 21 Jul 2015 23:37:35 +0200 Subject: [PATCH 23/23] Fixes typo. --- tests/GithubAdapterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/GithubAdapterTest.php b/tests/GithubAdapterTest.php index b471bcf..54c10af 100644 --- a/tests/GithubAdapterTest.php +++ b/tests/GithubAdapterTest.php @@ -9,7 +9,7 @@ * @coversDefaultClass \Potherca\Flysystem\Github\GithubAdapter * @covers :: * @covers ::__construct - * @covers ::getClient + * @covers ::getApi */ class GithubAdapterTest extends \PHPUnit_Framework_TestCase {