Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use environment variables only for integration test dependencies #901

Merged
merged 3 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/vendor
/docs/_build
/docs/*.pyc
/tests/_fixtures/test_*
/tests/.env

/.firebaserc
/.php-cs-fixer.cache
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"phpunit/phpunit": "^10.5.20",
"rector/rector": "^1.0.5",
"shipmonk/composer-dependency-analyser": "^1.5.3",
"symfony/var-dumper": "^6.3.5 || ^7.0.7"
"symfony/var-dumper": "^6.3.5 || ^7.0.7",
"vlucas/phpdotenv": "^5.6"
},
"suggest": {
"google/cloud-firestore": "^1.0 to use the Firestore component"
Expand Down Expand Up @@ -103,6 +104,9 @@
"rector-fix": [
"vendor/bin/rector"
],
"reset-project": [
"tests/bin/reset-project"
],
"test": [
"@analyze",
"@test-dependencies",
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
beStrictAboutOutputDuringTests="true"
bootstrap="tests/bootstrap.php"
colors="true"
>
<testsuites>
Expand Down
6 changes: 6 additions & 0 deletions tests/.env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GOOGLE_APPLICATION_CREDENTIALS=
FIREBASE_TENANT_ID=
TEST_FIREBASE_APP_ID=
TEST_FIREBASE_RTDB_URI=
TEST_FIREBASE_TENANT_ID=
TEST_REGISTRATION_TOKENS=
48 changes: 17 additions & 31 deletions tests/Integration/ServiceAccountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,44 @@

namespace Kreait\Firebase\Tests\Integration;

use Beste\Json;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Tests\IntegrationTestCase;
use Kreait\Firebase\Util;
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

use function assert;

/**
* @internal
*/
final class ServiceAccountTest extends TestCase
final class ServiceAccountTest extends IntegrationTestCase
{
/**
* @var non-empty-string
*/
private static string $credentialsPath;
private static bool $credentialsPathIsTemporary = false;
private static ?string $originalCredentials;

public static function setUpBeforeClass(): void
{
$credentialsFromEnvironment = Util::getenv('GOOGLE_APPLICATION_CREDENTIALS');
parent::setUpBeforeClass();

if ($credentialsFromEnvironment !== null && str_starts_with($credentialsFromEnvironment, '{')) {
// Don't overwrite the fixtures file
$credentialsPath = __DIR__.'/test_credentials.json';
self::$credentialsPathIsTemporary = true;
self::$originalCredentials = Util::getenv('GOOGLE_APPLICATION_CREDENTIALS');

$result = file_put_contents($credentialsPath, $credentialsFromEnvironment);

if ($result === false) {
self::fail("Unable to write credentials to file `{$credentialsPath}`");
}

Util::putenv('GOOGLE_APPLICATION_CREDENTIALS', $credentialsPath);
} elseif (!file_exists($credentialsPath = __DIR__.'/../_fixtures/test_credentials.json')) {
self::markTestSkipped('The integration tests require credentials');
}

self::$credentialsPath = $credentialsPath;
self::$credentialsPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'test_credentials.json';
file_put_contents(self::$credentialsPath, json_encode(self::$serviceAccount));
Util::putenv('GOOGLE_APPLICATION_CREDENTIALS', self::$credentialsPath);
}

public static function tearDownAfterClass(): void
{
if (self::$credentialsPathIsTemporary) {
unlink(self::$credentialsPath);
}
unlink(self::$credentialsPath);
Util::putenv('GOOGLE_APPLICATION_CREDENTIALS', self::$originalCredentials);
}

#[Test]
#[DoesNotPerformAssertions]
public function withPathToServiceAccount(): void
{
$factory = (new Factory())->withServiceAccount(self::$credentialsPath);
Expand All @@ -62,6 +50,7 @@ public function withPathToServiceAccount(): void
}

#[Test]
#[DoesNotPerformAssertions]
public function withJsonString(): void
{
$json = file_get_contents(self::$credentialsPath);
Expand All @@ -73,19 +62,16 @@ public function withJsonString(): void
}

#[Test]
#[DoesNotPerformAssertions]
public function withArray(): void
{
$json = file_get_contents(self::$credentialsPath);
assert($json !== false && $json !== '');

$array = Json::decode($json, true);

$factory = (new Factory())->withServiceAccount($array);
$factory = (new Factory())->withServiceAccount(self::$serviceAccount);

$this->assertFunctioningConnection($factory);
}

#[Test]
#[DoesNotPerformAssertions]
public function withGoogleApplicationCredentialsAsFilePath(): void
{
Util::putenv('GOOGLE_APPLICATION_CREDENTIALS', self::$credentialsPath);
Expand All @@ -94,6 +80,7 @@ public function withGoogleApplicationCredentialsAsFilePath(): void
}

#[Test]
#[DoesNotPerformAssertions]
public function withGoogleApplicationCredentialsAsJsonString(): void
{
$json = file_get_contents(self::$credentialsPath);
Expand All @@ -111,7 +98,6 @@ private function assertFunctioningConnection(Factory $factory): void

try {
$user = $auth->createAnonymousUser();
$this->addToAssertionCount(1);
} finally {
if ($user !== null) {
$auth->deleteUser($user->uid);
Expand Down
155 changes: 12 additions & 143 deletions tests/IntegrationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
use Beste\Json;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Util;
use Throwable;

use function array_rand;
use function bin2hex;
use function file_exists;
use function file_get_contents;
use function is_array;
use function mb_strtolower;
use function random_bytes;

Expand Down Expand Up @@ -54,23 +50,19 @@ abstract class IntegrationTestCase extends FirebaseTestCase

public static function setUpBeforeClass(): void
{
$credentials = self::credentialsFromEnvironment() ?? self::credentialsFromFile();
$credentials = self::credentialsFromEnvironment();

if (!$credentials) {
self::markTestSkipped('The integration tests require credentials');
}

if (str_starts_with($credentials, '{')) {
self::$serviceAccount = Json::decode($credentials, true);
} else {
self::$serviceAccount = Json::decodeFile($credentials, true);
}
self::$serviceAccount = Json::decode($credentials, true);

self::$factory = (new Factory())->withServiceAccount(self::$serviceAccount);
self::$registrationTokens = self::registrationTokens();
self::$rtdbUrl = self::rtdbUrl();
self::$tenantId = self::tenantId();
self::$appId = self::appId();
self::$registrationTokens = self::registrationTokensFromEnvironment();
self::$rtdbUrl = Util::getenv('TEST_FIREBASE_RTDB_URI');
self::$tenantId = Util::getenv('TEST_FIREBASE_TENANT_ID');
self::$appId = Util::getenv('TEST_FIREBASE_APP_ID');
}

/**
Expand Down Expand Up @@ -112,18 +104,6 @@ protected static function databaseIsEmulated(): bool
return Util::rtdbEmulatorHost() !== null;
}

/**
* @return non-empty-string|null
*/
private static function credentialsFromFile(): ?string
{
$credentialsPath = self::$fixturesDir.'/test_credentials.json';

return file_exists($credentialsPath)
? $credentialsPath
: null;
}

/**
* @return non-empty-string|null
*/
Expand All @@ -137,124 +117,13 @@ private static function credentialsFromEnvironment(): ?string
/**
* @return list<non-empty-string>
*/
private static function registrationTokens(): array
{
$registrationTokens = self::registrationTokensFromEnvironment() ?? self::registrationTokensFromFile();
$registrationTokens = array_map(trim(...), $registrationTokens);
$registrationTokens = array_filter($registrationTokens);

return array_values($registrationTokens);
}

/**
* @return array<string>
*/
private static function registrationTokensFromFile(): array
{
$tokens = [];

$path = self::$fixturesDir.'/test_devices.json';

if (!file_exists($path)) {
return $tokens;
}

try {
if ($contents = file_get_contents($path)) {
$tokens = Json::decode($contents, true);
}
} catch (Throwable) {
}

return array_map(strval(...), $tokens);
}

/**
* @return array<string>|null
*/
private static function registrationTokensFromEnvironment(): ?array
{
if (!($tokens = Util::getenv('TEST_REGISTRATION_TOKENS'))) {
return null;
}

try {
$tokens = Json::decode($tokens, true);
} catch (Throwable) {
return null;
}

if (!is_array($tokens)) {
return null;
}

return array_map(strval(...), $tokens);
}

/**
* @return non-empty-string|null
*/
private static function rtdbUrl(): ?string
{
return self::setting('TEST_FIREBASE_RTDB_URI', 'test_rtdb.json');
}

/**
* @return non-empty-string|null
*/
private static function tenantId(): ?string
{
return self::setting('TEST_FIREBASE_TENANT_ID', 'test_tenant.json');
}

/**
* @return non-empty-string|null
*/
private static function appId(): ?string
{
return self::setting('TEST_FIREBASE_APP_ID', 'test_app.json');
}

/**
* @param non-empty-string $envName
* @param non-empty-string $envFile
*
* @return non-empty-string|null
*/
private static function setting(string $envName, string $envFile): ?string
private static function registrationTokensFromEnvironment(): array
{
return self::settingFromEnv($envName) ?? self::settingFromFile($envFile);
}

/**
* @return non-empty-string|null
*/
private static function settingFromFile(string $envFile): ?string
{
$path = self::$fixturesDir.'/'.$envFile;

if (!file_exists($path)) {
return null;
}

try {
if ($contents = file_get_contents($path)) {
return Json::decode($contents, true);
}

return null;
} catch (Throwable) {
return null;
}
}
$tokens = Json::decode(Util::getenv('TEST_REGISTRATION_TOKENS') ?? '', true);
$tokens = array_map(strval(...), $tokens);
$tokens = array_map(trim(...), $tokens);
$tokens = array_filter($tokens, fn($token) => $token !== '');

/**
* @param non-empty-string $envKey
*
* @return non-empty-string|null
*/
private static function settingFromEnv(string $envKey): ?string
{
return Util::getenv($envKey);
return array_values($tokens);
}
}
40 changes: 40 additions & 0 deletions tests/bin/reset-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env php
<?php

declare(strict_types=1);

require __DIR__ . '/../../vendor/autoload.php';

use Kreait\Firebase\Auth\UserRecord;
use Kreait\Firebase\Database\RuleSet;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Util;

// Unsafe is needed because google/auth uses getenv/putenv to determine the Application Credentials
$dotenv = Dotenv\Dotenv::createUnsafeImmutable(__DIR__.'/..');
$dotenv->safeLoad();
$dotenv->required('GOOGLE_APPLICATION_CREDENTIALS');

$factory = (new Factory())->withDatabaseUri(Util::getenv('TEST_FIREBASE_RTDB_URI'));

$auth = $factory->createAuth();
$db = $factory->createDatabase();

echo 'Resetting database rules... ';
$factory->createDatabase()->updateRules(RuleSet::private());
echo 'DONE'.PHP_EOL;

echo 'Purging database... ';
$factory->createDatabase()->getReference('/')->remove();
echo 'DONE'.PHP_EOL;

echo 'Purging auth users... ';
do {
$users = iterator_to_array($auth->listUsers());
$uids = array_map(fn(UserRecord $record) => $record->uid, $users);

if (count($uids) > 0) {
$auth->deleteUsers($uids, true);
}
} while (count($uids) > 0);
echo 'DONE'.PHP_EOL;
Loading