Skip to content

Commit 188143d

Browse files
committed
Add testable error handling to SDK version function
See #90
1 parent 2850ff7 commit 188143d

2 files changed

Lines changed: 129 additions & 7 deletions

File tree

src/ExpoPushConnector.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class ExpoPushConnector extends Connector
2525

2626
public const MAX_CONCURRENT_REQUESTS = 6;
2727

28+
private static bool $sdkVersionResolved = false;
29+
private static ?string $sdkVersion = null;
30+
2831
public function __construct(
2932
protected ?string $authToken = null,
3033
?RateLimitStore $rateLimitStore = null,
@@ -83,17 +86,30 @@ protected function resolveRateLimitStore(): RateLimitStore
8386
*/
8487
public function sdkVersion(): string
8588
{
86-
static $version = null;
89+
if (!self::$sdkVersionResolved) {
90+
self::$sdkVersion = self::resolveSdkVersion(dirname(__DIR__) . '/composer.json');
91+
self::$sdkVersionResolved = true;
92+
}
8793

88-
if (!$version) {
89-
$composer = json_decode(
90-
file_get_contents(dirname(__DIR__) . '/composer.json'),
91-
);
94+
return self::$sdkVersion;
95+
}
9296

93-
$version = InstalledVersions::getPrettyVersion($composer->name);
97+
/**
98+
* Resolve the installed version from composer.json, falling back to 'unknown' if it can't be determined
99+
*/
100+
private static function resolveSdkVersion(string $composerJsonPath): string
101+
{
102+
if (!is_readable($composerJsonPath)) {
103+
return 'unknown';
94104
}
95105

96-
return (string) $version;
106+
try {
107+
$composer = json_decode(file_get_contents($composerJsonPath), flags: JSON_THROW_ON_ERROR);
108+
109+
return InstalledVersions::getPrettyVersion($composer->name) ?? 'unknown';
110+
} catch (Throwable) {
111+
return 'unknown';
112+
}
97113
}
98114

99115
// Internals ----

tests/Unit/ExpoPushConnectorTest.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,48 @@
44

55
namespace Dru1x\ExpoPush\Tests\Unit;
66

7+
use Composer\InstalledVersions;
78
use Dru1x\ExpoPush\Config\RetryConfig;
89
use Dru1x\ExpoPush\ExpoPushConnector;
910
use PHPUnit\Framework\Attributes\Test;
1011
use PHPUnit\Framework\TestCase;
12+
use ReflectionMethod;
13+
use ReflectionProperty;
1114
use Saloon\Http\Auth\TokenAuthenticator;
1215
use Saloon\RateLimitPlugin\Exceptions\LimitException;
1316
use Saloon\RateLimitPlugin\Limit;
1417
use Saloon\RateLimitPlugin\Stores\MemoryStore;
1518

1619
class ExpoPushConnectorTest extends TestCase
1720
{
21+
private ?bool $originalSdkVersionResolved = null;
22+
private ?string $originalSdkVersion = null;
23+
24+
/** @var list<string> */
25+
private array $tempFiles = [];
26+
27+
protected function setUp(): void
28+
{
29+
parent::setUp();
30+
31+
$this->originalSdkVersionResolved = (new ReflectionProperty(ExpoPushConnector::class, 'sdkVersionResolved'))->getValue();
32+
$this->originalSdkVersion = (new ReflectionProperty(ExpoPushConnector::class, 'sdkVersion'))->getValue();
33+
}
34+
35+
protected function tearDown(): void
36+
{
37+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersionResolved'))->setValue(null, $this->originalSdkVersionResolved);
38+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersion'))->setValue(null, $this->originalSdkVersion);
39+
40+
foreach ($this->tempFiles as $tempFile) {
41+
is_file($tempFile) && unlink($tempFile);
42+
}
43+
44+
$this->tempFiles = [];
45+
46+
parent::tearDown();
47+
}
48+
1849
#[Test]
1950
public function resolves_the_expo_push_api_base_url(): void
2051
{
@@ -113,8 +144,83 @@ public function custom_retry_configuration_is_used_when_supplied(): void
113144
$this->assertFalse($connector->throwOnMaxTries);
114145
}
115146

147+
// SDK Version ----
148+
149+
#[Test]
150+
public function sdk_version_resolves_the_real_installed_version(): void
151+
{
152+
$composerJsonPath = dirname(__DIR__, 2) . '/composer.json';
153+
$composer = json_decode(file_get_contents($composerJsonPath));
154+
155+
$this->assertSame(
156+
InstalledVersions::getPrettyVersion($composer->name),
157+
$this->resolveSdkVersion($composerJsonPath),
158+
);
159+
}
160+
161+
#[Test]
162+
public function sdk_version_falls_back_to_unknown_when_composer_json_is_missing(): void
163+
{
164+
$this->assertSame('unknown', $this->resolveSdkVersion('/nonexistent/composer.json'));
165+
}
166+
167+
#[Test]
168+
public function sdk_version_falls_back_to_unknown_when_composer_json_is_malformed(): void
169+
{
170+
$path = $this->writeTempFile('{not valid json');
171+
172+
$this->assertSame('unknown', $this->resolveSdkVersion($path));
173+
}
174+
175+
#[Test]
176+
public function sdk_version_falls_back_to_unknown_when_the_package_is_not_installed(): void
177+
{
178+
$path = $this->writeTempFile(json_encode(['name' => 'not/a-real-package']));
179+
180+
$this->assertSame('unknown', $this->resolveSdkVersion($path));
181+
}
182+
183+
#[Test]
184+
public function sdk_version_does_not_recompute_once_resolved(): void
185+
{
186+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersion'))->setValue(null, 'cached-test-value');
187+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersionResolved'))->setValue(null, true);
188+
189+
$connector = new ExpoPushConnector();
190+
191+
$this->assertSame('cached-test-value', $connector->sdkVersion());
192+
}
193+
194+
#[Test]
195+
public function sdk_version_caches_a_falsy_resolved_value_instead_of_recomputing_it(): void
196+
{
197+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersion'))->setValue(null, '');
198+
(new ReflectionProperty(ExpoPushConnector::class, 'sdkVersionResolved'))->setValue(null, true);
199+
200+
$connector = new ExpoPushConnector();
201+
202+
$this->assertSame('', $connector->sdkVersion());
203+
}
204+
116205
// Internals ----
117206

207+
private function resolveSdkVersion(string $composerJsonPath): string
208+
{
209+
return (new ReflectionMethod(ExpoPushConnector::class, 'resolveSdkVersion'))
210+
->invoke(null, $composerJsonPath);
211+
}
212+
213+
private function writeTempFile(string $contents): string
214+
{
215+
$path = tempnam(sys_get_temp_dir(), 'expo-push-sdk-version-test-');
216+
217+
file_put_contents($path, $contents);
218+
219+
$this->tempFiles[] = $path;
220+
221+
return $path;
222+
}
223+
118224
private function findLimit(ExpoPushConnector $connector, string $name): Limit
119225
{
120226
try {

0 commit comments

Comments
 (0)