diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..047a8a97 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,29 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + status: + project: + default: + target: auto + threshold: 0% + patch: + default: + target: auto + threshold: 0% + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,tree" + behavior: default + require_changes: false diff --git a/.gitattributes b/.gitattributes index 0226c621..62408021 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,12 @@ -/.gitattributes export-ignore -/.github/ export-ignore -/.gitignore export-ignore -/docs/ export-ignore -/phpunit.xml export-ignore -/test/ export-ignore +/.codecov.yml export-ignore +/.gitattributes export-ignore +/.github/ export-ignore +/.gitignore export-ignore +/CHANGELOG.md export-ignore +/CODE_OF_CONDUCT.md export-ignore +/CONTRIBUTING.md export-ignore +/CREDITS.md export-ignore +/README.PROVIDER-GUIDE.md export-ignore +/docs/ export-ignore +/phpunit.xml export-ignore +/test/ export-ignore diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 83d147f9..e56dd68f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -79,10 +79,5 @@ jobs: composer-options: "${{ matrix.composer-options }}" - name: "Run unit tests" run: "./vendor/bin/phpunit --colors=always --coverage-clover build/logs/clover.xml" - - name: "Publish coverage report to Coveralls" - continue-on-error: true - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - composer global --ansi require php-coveralls/php-coveralls - php-coveralls --ansi -vv --coverage_clover=build/logs/clover.xml + - name: "Publish coverage report to Codecov" + uses: "codecov/codecov-action@v1" diff --git a/README.md b/README.md index cebc5054..ea655f61 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ This package makes it simple to integrate your application with [OAuth 2.0](http://oauth.net/2/) service providers. [![Gitter Chat](https://img.shields.io/badge/gitter-join_chat-brightgreen.svg?style=flat-square)](https://gitter.im/thephpleague/oauth2-client) -[![Source Code](http://img.shields.io/badge/source-thephpleague/oauth2--client-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client) +[![Source Code](https://img.shields.io/badge/source-thephpleague/oauth2--client-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client) [![Latest Version](https://img.shields.io/github/release/thephpleague/oauth2-client.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE) [![Build Status](https://img.shields.io/github/workflow/status/thephpleague/oauth2-client/CI?label=CI&logo=github&style=flat-square)](https://github.com/thephpleague/oauth2-client/actions?query=workflow%3ACI) -[![Coverage Status](https://img.shields.io/coveralls/thephpleague/oauth2-client/master.svg?style=flat-square)](https://coveralls.io/r/thephpleague/oauth2-client?branch=master) +[![Codecov Code Coverage](https://img.shields.io/codecov/c/gh/thephpleague/oauth2-client?label=codecov&logo=codecov&style=flat-square)](https://codecov.io/gh/thephpleague/oauth2-client) [![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-client.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-client) --- diff --git a/docs/index.md b/docs/index.md index 6a06277c..f58721b3 100755 --- a/docs/index.md +++ b/docs/index.md @@ -12,7 +12,7 @@ League/oauth2-client [![Latest Version](https://img.shields.io/github/release/thephpleague/oauth2-client.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE) [![Build Status](https://img.shields.io/github/workflow/status/thephpleague/oauth2-client/CI?label=CI&logo=github&style=flat-square)](https://github.com/thephpleague/oauth2-client/actions?query=workflow%3ACI) -[![Coverage Status](https://img.shields.io/coveralls/thephpleague/oauth2-client/master.svg?style=flat-square)](https://coveralls.io/r/thephpleague/oauth2-client?branch=master) +[![Codecov Code Coverage](https://img.shields.io/codecov/c/gh/thephpleague/oauth2-client?label=codecov&logo=codecov&style=flat-square)](https://codecov.io/gh/thephpleague/oauth2-client) [![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-client.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-client) The OAuth2 login flow, seen commonly around the web in the form of "Connect with Facebook/Google/etc." buttons, is a very diff --git a/src/Token/AccessToken.php b/src/Token/AccessToken.php index 2c55c23c..81533c30 100644 --- a/src/Token/AccessToken.php +++ b/src/Token/AccessToken.php @@ -49,6 +49,40 @@ class AccessToken implements AccessTokenInterface, ResourceOwnerAccessTokenInter */ protected $values = []; + /** + * @var int + */ + private static $timeNow; + + /** + * Set the time now. This should only be used for testing purposes. + * + * @param int $timeNow the time in seconds since epoch + * @return void + */ + public static function setTimeNow($timeNow) + { + self::$timeNow = $timeNow; + } + + /** + * Reset the time now if it was set for test purposes. + * + * @return void + */ + public static function resetTimeNow() + { + self::$timeNow = null; + } + + /** + * @return int + */ + public function getTimeNow() + { + return self::$timeNow ? self::$timeNow : time(); + } + /** * Constructs an access token. * @@ -80,14 +114,14 @@ public function __construct(array $options = []) throw new \InvalidArgumentException('expires_in value must be an integer'); } - $this->expires = $options['expires_in'] != 0 ? time() + $options['expires_in'] : 0; + $this->expires = $options['expires_in'] != 0 ? $this->getTimeNow() + $options['expires_in'] : 0; } elseif (!empty($options['expires'])) { // Some providers supply the seconds until expiration rather than // the exact timestamp. Take a best guess at which we received. $expires = $options['expires']; if (!$this->isExpirationTimestamp($expires)) { - $expires += time(); + $expires += $this->getTimeNow(); } $this->expires = $expires; diff --git a/src/Token/AccessTokenInterface.php b/src/Token/AccessTokenInterface.php index cec37f61..c5f13350 100644 --- a/src/Token/AccessTokenInterface.php +++ b/src/Token/AccessTokenInterface.php @@ -34,7 +34,7 @@ public function getToken(); public function getRefreshToken(); /** - * Returns the expiration timestamp, if defined. + * Returns the expiration timestamp in seconds, if defined. * * @return integer|null */ diff --git a/test/src/Token/AccessTokenTest.php b/test/src/Token/AccessTokenTest.php index 84898546..23ad105f 100644 --- a/test/src/Token/AccessTokenTest.php +++ b/test/src/Token/AccessTokenTest.php @@ -10,11 +10,27 @@ class AccessTokenTest extends TestCase { + /** + * BC teardown. + * + * This is for backwards compatibility of older PHP versions. Ideally we would just implement a tearDown() here but + * older PHP versions this library supports don't have return typehint support, so this is the workaround. + * + * @return void + */ + private static function tearDownForBackwardsCompatibility() + { + /* reset the test double time if it was set */ + AccessToken::resetTimeNow(); + } + public function testInvalidRefreshToken() { $this->expectException(InvalidArgumentException::class); $token = $this->getAccessToken(['invalid_access_token' => 'none']); + + self::tearDownForBackwardsCompatibility(); } protected function getAccessToken($options = []) @@ -32,6 +48,49 @@ public function testExpiresInCorrection() $this->assertNotNull($expires); $this->assertGreaterThan(time(), $expires); $this->assertLessThan(time() + 200, $expires); + + self::tearDownForBackwardsCompatibility(); + } + + public function testExpiresInCorrectionUsingSetTimeNow() + { + /* set fake time at 2020-01-01 00:00:00 */ + AccessToken::setTimeNow(1577836800); + $options = ['access_token' => 'access_token', 'expires_in' => 100]; + $token = $this->getAccessToken($options); + + $expires = $token->getExpires(); + + $this->assertNotNull($expires); + $this->assertEquals(1577836900, $expires); + + self::tearDownForBackwardsCompatibility(); + } + + public function testSetTimeNow() + { + AccessToken::setTimeNow(1577836800); + $timeNow = $this->getAccessToken(['access_token' => 'asdf'])->getTimeNow(); + + $this->assertEquals(1577836800, $timeNow); + + self::tearDownForBackwardsCompatibility(); + } + + public function testResetTimeNow() + { + AccessToken::setTimeNow(1577836800); + $token = $this->getAccessToken(['access_token' => 'asdf']); + + $this->assertEquals(1577836800, $token->getTimeNow()); + AccessToken::resetTimeNow(); + + $this->assertNotEquals(1577836800, $token->getTimeNow()); + + $timeBeforeAssertion = time(); + $this->assertGreaterThanOrEqual($timeBeforeAssertion, $token->getTimeNow()); + + self::tearDownForBackwardsCompatibility(); } public function testExpiresPastTimestamp() @@ -45,6 +104,8 @@ public function testExpiresPastTimestamp() $token = $this->getAccessToken($options); $this->assertFalse($token->hasExpired()); + + self::tearDownForBackwardsCompatibility(); } public function testGetRefreshToken() @@ -58,6 +119,8 @@ public function testGetRefreshToken() $refreshToken = $token->getRefreshToken(); $this->assertEquals($options['refresh_token'], $refreshToken); + + self::tearDownForBackwardsCompatibility(); } public function testHasNotExpiredWhenPropertySetInFuture() @@ -75,6 +138,8 @@ public function testHasNotExpiredWhenPropertySetInFuture() ->andReturn($expectedExpires); $this->assertFalse($token->hasExpired()); + + self::tearDownForBackwardsCompatibility(); } public function testHasExpiredWhenPropertySetInPast() @@ -92,6 +157,8 @@ public function testHasExpiredWhenPropertySetInPast() ->andReturn($expectedExpires); $this->assertTrue($token->hasExpired()); + + self::tearDownForBackwardsCompatibility(); } public function testCannotReportExpiredWhenNoExpirationSet() @@ -104,6 +171,8 @@ public function testCannotReportExpiredWhenNoExpirationSet() $this->expectException(RuntimeException::class); $hasExpired = $token->hasExpired(); + + self::tearDownForBackwardsCompatibility(); } public function testInvalidExpiresIn() @@ -116,6 +185,8 @@ public function testInvalidExpiresIn() $this->expectException(InvalidArgumentException::class); $token = $this->getAccessToken($options); + + self::tearDownForBackwardsCompatibility(); } @@ -132,6 +203,8 @@ public function testJsonSerializable() $jsonToken = json_encode($token); $this->assertEquals($options, json_decode($jsonToken, true)); + + self::tearDownForBackwardsCompatibility(); } public function testValues() @@ -151,5 +224,7 @@ public function testValues() $this->assertTrue(is_array($values)); $this->assertArrayHasKey('custom_thing', $values); $this->assertSame($options['custom_thing'], $values['custom_thing']); + + self::tearDownForBackwardsCompatibility(); } }