diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e474760893..53878351b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,11 +10,8 @@ jobs: test: strategy: matrix: + os: [ "ubuntu-latest", "windows-latest" ] php: [ "8.1", "8.2", "8.3", "8.4" ] - os: [ ubuntu-latest ] - include: - - os: windows-latest - php: "8.1" runs-on: ${{ matrix.os }} name: PHP ${{ matrix.php }} Unit Test${{ matrix.os == 'windows-latest' && ' on Windows' || '' }} steps: @@ -31,7 +28,7 @@ jobs: max_attempts: 3 command: composer install - name: Run Script - run: vendor/bin/phpunit ${{ matrix.os == 'windows-latest' && '--filter GCECredentialsTest' || '' }} + run: vendor/bin/phpunit test_lowest: runs-on: ubuntu-latest name: Test Prefer Lowest diff --git a/tests/ApplicationDefaultCredentialsTest.php b/tests/ApplicationDefaultCredentialsTest.php index 1db9b9e43e..873b289a74 100644 --- a/tests/ApplicationDefaultCredentialsTest.php +++ b/tests/ApplicationDefaultCredentialsTest.php @@ -70,7 +70,7 @@ public function testLoadsOKIfEnvSpecifiedIsValid() public function testLoadsDefaultFileIfPresentAndEnvVarIsNotSet() { - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $this->assertNotNull( ApplicationDefaultCredentials::getCredentials('a scope') ); @@ -80,7 +80,7 @@ public function testFailsIfNotOnGceAndNoDefaultFileFound() { $this->expectException(DomainException::class); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); // simulate not being GCE and retry attempts by returning multiple 500s $httpHandler = getHandler([ new Response(500), @@ -93,7 +93,7 @@ public function testFailsIfNotOnGceAndNoDefaultFileFound() public function testSuccedsIfNoDefaultFilesButIsOnGCE() { - putenv('HOME'); + setHomeEnv(null); $wantedTokens = [ 'access_token' => '1/abdef1234567890', @@ -116,7 +116,7 @@ public function testSuccedsIfNoDefaultFilesButIsOnGCE() public function testGceCredentials() { - putenv('HOME'); + setHomeEnv(null); $jsonTokens = json_encode(['access_token' => 'abc']); @@ -160,7 +160,7 @@ public function testGceCredentials() public function testImpersonatedServiceAccountCredentials() { - putenv('HOME=' . __DIR__ . '/fixtures5'); + setHomeEnv(__DIR__ . '/fixtures5'); $creds = ApplicationDefaultCredentials::getCredentials( null, null, @@ -183,7 +183,7 @@ public function testImpersonatedServiceAccountCredentials() public function testUserRefreshCredentials() { - putenv('HOME=' . __DIR__ . '/fixtures2'); + setHomeEnv(__DIR__ . '/fixtures2'); $creds = ApplicationDefaultCredentials::getCredentials( null, // $scope @@ -219,7 +219,7 @@ public function testUserRefreshCredentials() public function testServiceAccountCredentials() { - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $creds = ApplicationDefaultCredentials::getCredentials( null, // $scope @@ -255,7 +255,7 @@ public function testServiceAccountCredentials() public function testDefaultScopeArray() { - putenv('HOME=' . __DIR__ . '/fixtures2'); + setHomeEnv(__DIR__ . '/fixtures2'); $creds = ApplicationDefaultCredentials::getCredentials( null, // $scope @@ -292,7 +292,7 @@ public function testGetMiddlewareLoadsOKIfEnvSpecifiedIsValid() public function testLGetMiddlewareoadsDefaultFileIfPresentAndEnvVarIsNotSet() { - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $this->assertNotNull(ApplicationDefaultCredentials::getMiddleware('a scope')); } @@ -300,7 +300,7 @@ public function testGetMiddlewareFailsIfNotOnGceAndNoDefaultFileFound() { $this->expectException(DomainException::class); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); // simulate not being GCE and retry attempts by returning multiple 500s $httpHandler = getHandler([ @@ -356,7 +356,7 @@ public function testOnGceCacheWithHit() { $this->expectException(DomainException::class); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $mockCacheItem = $this->prophesize('Psr\Cache\CacheItemInterface'); $mockCacheItem->isHit() @@ -380,7 +380,7 @@ public function testOnGceCacheWithHit() public function testOnGceCacheWithoutHit() { - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $gceIsCalled = false; $dummyHandler = function ($request) use (&$gceIsCalled) { @@ -416,7 +416,7 @@ public function testOnGceCacheWithoutHit() public function testOnGceCacheWithOptions() { - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $prefix = 'test_prefix_'; $lifetime = '70707'; @@ -473,7 +473,7 @@ public function testGetIdTokenCredentialsLoadsOKIfEnvSpecifiedIsValid() public function testGetIdTokenCredentialsLoadsDefaultFileIfPresentAndEnvVarIsNotSet() { - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $creds = ApplicationDefaultCredentials::getIdTokenCredentials($this->targetAudience); $this->assertInstanceOf(ServiceAccountCredentials::class, $creds); } @@ -483,7 +483,7 @@ public function testGetIdTokenCredentialsFailsIfNotOnGceAndNoDefaultFileFound() $this->expectException(DomainException::class); $this->expectExceptionMessage('Your default credentials were not found'); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); // simulate not being GCE and retry attempts by returning multiple 500s $httpHandler = getHandler([ @@ -500,7 +500,7 @@ public function testGetIdTokenCredentialsFailsIfNotOnGceAndNoDefaultFileFound() public function testGetIdTokenCredentialsWithImpersonatedServiceAccountCredentials() { - putenv('HOME=' . __DIR__ . '/fixtures5'); + setHomeEnv(__DIR__ . '/fixtures5'); $creds = ApplicationDefaultCredentials::getIdTokenCredentials('123@456.com'); $this->assertInstanceOf(ImpersonatedServiceAccountCredentials::class, $creds); } @@ -529,7 +529,7 @@ public function testGetIdTokenCredentialsWithCacheOptions() public function testGetIdTokenCredentialsSuccedsIfNoDefaultFilesButIsOnGCE() { - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $wantedTokens = [ 'access_token' => '1/abdef1234567890', 'expires_in' => '57', @@ -553,7 +553,7 @@ public function testGetIdTokenCredentialsSuccedsIfNoDefaultFilesButIsOnGCE() public function testGetIdTokenCredentialsWithUserRefreshCredentials() { - putenv('HOME=' . __DIR__ . '/fixtures2'); + setHomeEnv(__DIR__ . '/fixtures2'); $creds = ApplicationDefaultCredentials::getIdTokenCredentials( $this->targetAudience, @@ -610,7 +610,7 @@ public function testGetCredentialsUtilizesQuotaProjectEnvVar() { $quotaProject = 'quota-project-from-env-var'; putenv(CredentialsLoader::QUOTA_PROJECT_ENV_VAR . '=' . $quotaProject); - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $credentials = ApplicationDefaultCredentials::getCredentials(); @@ -625,7 +625,7 @@ public function testGetCredentialsUtilizesQuotaProjectParameterOverEnvVar() { $quotaProject = 'quota-project-from-parameter'; putenv(CredentialsLoader::QUOTA_PROJECT_ENV_VAR . '=quota-project-from-env-var'); - putenv('HOME=' . __DIR__ . '/fixtures'); + setHomeEnv(__DIR__ . '/fixtures'); $credentials = ApplicationDefaultCredentials::getCredentials( null, // $scope @@ -688,7 +688,7 @@ public function testWithFetchAuthTokenCacheAndExplicitQuotaProject() public function testWithGCECredentials() { - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $wantedTokens = [ 'access_token' => '1/abdef1234567890', 'expires_in' => '57', @@ -721,7 +721,7 @@ public function testWithGCECredentials() public function testAppEngineStandard() { $_SERVER['SERVER_SOFTWARE'] = 'Google App Engine'; - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $this->assertInstanceOf( 'Google\Auth\Credentials\AppIdentityCredentials', ApplicationDefaultCredentials::getCredentials() @@ -732,7 +732,7 @@ public function testAppEngineFlexible() { $_SERVER['SERVER_SOFTWARE'] = 'Google App Engine'; putenv('GAE_INSTANCE=aef-default-20180313t154438'); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $httpHandler = getHandler([ new Response(200, [GCECredentials::FLAVOR_HEADER => 'Google']), ]); @@ -746,7 +746,7 @@ public function testAppEngineFlexibleIdToken() { $_SERVER['SERVER_SOFTWARE'] = 'Google App Engine'; putenv('GAE_INSTANCE=aef-default-20180313t154438'); - putenv('HOME=' . __DIR__ . '/not_exist_fixtures'); + setHomeEnv(__DIR__ . '/not_exist_fixtures'); $httpHandler = getHandler([ new Response(200, [GCECredentials::FLAVOR_HEADER => 'Google']), ]); @@ -868,7 +868,7 @@ public function testUniverseDomainInKeyFile() /** @runInSeparateProcess */ public function testUniverseDomainInGceCredentials() { - putenv('HOME'); + setHomeEnv(null); $expectedUniverseDomain = 'example-universe.com'; $creds = ApplicationDefaultCredentials::getCredentials( diff --git a/tests/Credentials/ExternalAccountCredentialsTest.php b/tests/Credentials/ExternalAccountCredentialsTest.php index 3cade03037..60149b7970 100644 --- a/tests/Credentials/ExternalAccountCredentialsTest.php +++ b/tests/Credentials/ExternalAccountCredentialsTest.php @@ -586,7 +586,12 @@ public function testExecutableSourceCacheKey() */ public function testExecutableCredentialSourceEnvironmentVars() { + if (PHP_OS_FAMILY === 'Windows') { + $this->markTestSkipped('This test does not work on Windows'); + } + putenv('GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES=1'); + $tmpFile = tempnam(sys_get_temp_dir(), 'test'); $outputFile = tempnam(sys_get_temp_dir(), 'output'); $fileContents = 'foo-' . rand(); @@ -597,20 +602,23 @@ public function testExecutableCredentialSourceEnvironmentVars() 'id_token' => 'abc', 'expiration_time' => time() + 100, ]); + + $command = sprintf( + 'echo $GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE,$GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE,%s > %s' . + ' && echo \'%s\' > $GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE' . + ' && echo \'%s\'', + $fileContents, + $tmpFile, + $successJson, + $successJson + ); + $json = [ 'audience' => 'test-audience', 'subject_token_type' => 'test-token-type', 'credential_source' => [ 'executable' => [ - 'command' => sprintf( - 'echo $GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE,$GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE,%s > %s' . - ' && echo \'%s\' > $GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE ' . - ' && echo \'%s\'', - $fileContents, - $tmpFile, - $successJson, - $successJson, - ), + 'command' => $command, 'timeout_millis' => 5000, 'output_file' => $outputFile, ], diff --git a/tests/Credentials/ServiceAccountCredentialsTest.php b/tests/Credentials/ServiceAccountCredentialsTest.php index 63110448e8..3af95e939a 100644 --- a/tests/Credentials/ServiceAccountCredentialsTest.php +++ b/tests/Credentials/ServiceAccountCredentialsTest.php @@ -192,7 +192,7 @@ public function testSucceedIfFileExists() /** @runInSeparateProcess */ public function testIsNullIfFileDoesNotExist() { - putenv('HOME=' . __DIR__ . '/../not_exists_fixtures'); + setHomeEnv(__DIR__ . '/../not_exists_fixtures'); $this->assertNull( ServiceAccountCredentials::fromWellKnownFile() ); @@ -201,7 +201,7 @@ public function testIsNullIfFileDoesNotExist() /** @runInSeparateProcess */ public function testSucceedIfFileIsPresent() { - putenv('HOME=' . __DIR__ . '/../fixtures'); + setHomeEnv(__DIR__ . '/../fixtures'); $this->assertNotNull( ApplicationDefaultCredentials::getCredentials('a scope') ); diff --git a/tests/Credentials/UserRefreshCredentialsTest.php b/tests/Credentials/UserRefreshCredentialsTest.php index 7a8f393b90..0432182492 100644 --- a/tests/Credentials/UserRefreshCredentialsTest.php +++ b/tests/Credentials/UserRefreshCredentialsTest.php @@ -40,7 +40,7 @@ protected function tearDown(): void { putenv(UserRefreshCredentials::ENV_VAR); // removes it from if ($this->originalHome != getenv('HOME')) { - putenv('HOME=' . $this->originalHome); + setHomeEnv($this->originalHome); } } @@ -179,7 +179,7 @@ public function testSucceedIfFileExists() public function testIsNullIfFileDoesNotExist() { - putenv('HOME=' . __DIR__ . '/../not_exist_fixtures'); + setHomeEnv(__DIR__ . '/../not_exist_fixtures'); $this->assertNull( UserRefreshCredentials::fromWellKnownFile('a scope') ); @@ -187,7 +187,7 @@ public function testIsNullIfFileDoesNotExist() public function testSucceedIfFileIsPresent() { - putenv('HOME=' . __DIR__ . '/../fixtures2'); + setHomeEnv(__DIR__ . '/../fixtures2'); $this->assertNotNull( ApplicationDefaultCredentials::getCredentials('a scope') ); diff --git a/tests/CredentialsLoaderTest.php b/tests/CredentialsLoaderTest.php index dcf257b765..d0336766c5 100644 --- a/tests/CredentialsLoaderTest.php +++ b/tests/CredentialsLoaderTest.php @@ -35,7 +35,7 @@ public function testUpdateMetadataSkipsWhenAuthenticationisSet() /** @runInSeparateProcess */ public function testGetDefaultClientCertSource() { - putenv('HOME=' . __DIR__ . '/fixtures4/valid'); + setHomeEnv(__DIR__ . '/fixtures4/valid'); $callback = CredentialsLoader::getDefaultClientCertSource(); $this->assertNotNull($callback); @@ -47,7 +47,7 @@ public function testGetDefaultClientCertSource() /** @runInSeparateProcess */ public function testNonExistantDefaultClientCertSource() { - putenv('HOME='); + setHomeEnv(null); $callback = CredentialsLoader::getDefaultClientCertSource(); $this->assertNull($callback); @@ -61,7 +61,7 @@ public function testDefaultClientCertSourceInvalidJsonThrowsException() $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessage('Invalid client cert source JSON'); - putenv('HOME=' . __DIR__ . '/fixtures4/invalidjson'); + setHomeEnv(__DIR__ . '/fixtures4/invalidjson'); CredentialsLoader::getDefaultClientCertSource(); } @@ -74,7 +74,7 @@ public function testDefaultClientCertSourceInvalidKeyThrowsException() $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessage('cert source requires "cert_provider_command"'); - putenv('HOME=' . __DIR__ . '/fixtures4/invalidkey'); + setHomeEnv(__DIR__ . '/fixtures4/invalidkey'); CredentialsLoader::getDefaultClientCertSource(); } @@ -87,7 +87,7 @@ public function testDefaultClientCertSourceInvalidValueThrowsException() $this->expectException(UnexpectedValueException::class); $this->expectExceptionMessage('cert source expects "cert_provider_command" to be an array'); - putenv('HOME=' . __DIR__ . '/fixtures4/invalidvalue'); + setHomeEnv(__DIR__ . '/fixtures4/invalidvalue'); CredentialsLoader::getDefaultClientCertSource(); } @@ -115,7 +115,7 @@ public function testDefaultClientCertSourceInvalidCmdThrowsException() $this->expectException(RuntimeException::class); $this->expectExceptionMessage('"cert_provider_command" failed with a nonzero exit code'); - putenv('HOME=' . __DIR__ . '/fixtures4/invalidcmd'); + setHomeEnv(__DIR__ . '/fixtures4/invalidcmd'); $callback = CredentialsLoader::getDefaultClientCertSource(); diff --git a/tests/ExecutableHandler/ExecutableHandlerTest.php b/tests/ExecutableHandler/ExecutableHandlerTest.php index 16a3537db2..7561a4b546 100644 --- a/tests/ExecutableHandler/ExecutableHandlerTest.php +++ b/tests/ExecutableHandler/ExecutableHandlerTest.php @@ -26,17 +26,17 @@ class ExecutableHandlerTest extends TestCase public function testEnvironmentVariables() { $handler = new ExecutableHandler(['ENV_VAR_1' => 'foo', 'ENV_VAR_2' => 'bar']); - $this->assertEquals(0, $handler('echo $ENV_VAR_1')); + $this->assertEquals(0, $handler('bash -c "echo $ENV_VAR_1"')); $this->assertEquals("foo\n", $handler->getOutput()); - $this->assertEquals(0, $handler('echo $ENV_VAR_2')); + $this->assertEquals(0, $handler('bash -c "echo $ENV_VAR_2"')); $this->assertEquals("bar\n", $handler->getOutput()); } public function testTimeoutMs() { - $handler = new ExecutableHandler([], 300); - $this->assertEquals(0, $handler('sleep "0.2"')); + $handler = new ExecutableHandler([], 3000); + $this->assertEquals(0, $handler('bash -c \'sleep "0.1"\'')); } public function testTimeoutMsExceeded() @@ -51,7 +51,7 @@ public function testTimeoutMsExceeded() public function testErrorOutputIsReturnedAsOutput() { $handler = new ExecutableHandler(); - $this->assertEquals(0, $handler('echo "Bad Response." >&2')); + $this->assertEquals(0, $handler('bash -c \'echo "Bad Response." >&2\'')); $this->assertEquals("Bad Response.\n", $handler->getOutput()); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e15bbb6290..e62b42ee22 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -28,3 +28,15 @@ function getHandler(array $mockResponses = []) return new \Google\Auth\HttpHandler\Guzzle6HttpHandler($client); } + +function setHomeEnv(string|null $value): void +{ + $assigment = sprintf( + '%s%s%s', + PHP_OS_FAMILY === 'Windows' ? 'APPDATA' : 'HOME', + $value === null ? '' : '=', + (string) $value + ); + + putenv($assigment); +} diff --git a/tests/fixtures/.config/gcloud b/tests/fixtures/.config/gcloud new file mode 120000 index 0000000000..38d21f0a3c --- /dev/null +++ b/tests/fixtures/.config/gcloud @@ -0,0 +1 @@ +../gcloud/ \ No newline at end of file diff --git a/tests/fixtures/.config/gcloud/application_default_credentials.json b/tests/fixtures/gcloud/application_default_credentials.json similarity index 100% rename from tests/fixtures/.config/gcloud/application_default_credentials.json rename to tests/fixtures/gcloud/application_default_credentials.json diff --git a/tests/fixtures2/.config/gcloud b/tests/fixtures2/.config/gcloud new file mode 120000 index 0000000000..38d21f0a3c --- /dev/null +++ b/tests/fixtures2/.config/gcloud @@ -0,0 +1 @@ +../gcloud/ \ No newline at end of file diff --git a/tests/fixtures2/.config/gcloud/application_default_credentials.json b/tests/fixtures2/gcloud/application_default_credentials.json similarity index 100% rename from tests/fixtures2/.config/gcloud/application_default_credentials.json rename to tests/fixtures2/gcloud/application_default_credentials.json diff --git a/tests/fixtures5/.config/gcloud b/tests/fixtures5/.config/gcloud new file mode 120000 index 0000000000..38d21f0a3c --- /dev/null +++ b/tests/fixtures5/.config/gcloud @@ -0,0 +1 @@ +../gcloud/ \ No newline at end of file diff --git a/tests/fixtures5/.config/gcloud/application_default_credentials.json b/tests/fixtures5/gcloud/application_default_credentials.json similarity index 100% rename from tests/fixtures5/.config/gcloud/application_default_credentials.json rename to tests/fixtures5/gcloud/application_default_credentials.json