dir = sys_get_temp_dir() . '/weerscherm-token-' . uniqid(); $this->file = $this->dir . '/netatmo-token.json'; } protected function tearDown(): void { array_map('unlink', glob($this->dir . '/*') ?: []); @rmdir($this->dir); } public function testWriteReadRoundTripAndMode(): void { $store = new TokenStore($this->file); $store->write(['access_token' => 'a', 'refresh_token' => 'r', 'expires_at' => 123]); self::assertSame(['access_token' => 'a', 'refresh_token' => 'r', 'expires_at' => 123], $store->read()); self::assertSame('0600', substr(sprintf('%o', fileperms($this->file)), -4)); self::assertSame([], glob($this->dir . '/*.tmp.*') ?: [], 'geen tijdelijke bestanden achtergelaten'); } public function testMissingOrInvalidFileReadsNull(): void { self::assertNull((new TokenStore($this->file))->read()); mkdir($this->dir, 0700, true); file_put_contents($this->file, '{"access_token":"a"}'); self::assertNull((new TokenStore($this->file))->read(), 'zonder refresh token is het bestand onbruikbaar'); } public function testWithLockReturnsValueAndReleasesAfterException(): void { $store = new TokenStore($this->file); self::assertSame(42, $store->withLock(fn() => 42)); try { $store->withLock(function (): void { throw new \RuntimeException('boem'); }); self::fail('exception verwacht'); } catch (\RuntimeException) { } $handle = fopen($this->file . '.lock', 'c'); self::assertTrue(flock($handle, LOCK_EX | LOCK_NB), 'lock is weer vrij'); fclose($handle); } public function testConcurrentProcessesAreSerialised(): void { $store = new TokenStore($this->file); $script = sprintf( 'withLock(function () { usleep(400000); });', var_export(__DIR__ . '/../vendor/autoload.php', true), var_export($this->file, true), ); $scriptFile = $this->dir . '/hold.php'; mkdir($this->dir, 0700, true); file_put_contents($scriptFile, $script); $proc = proc_open([PHP_BINARY, $scriptFile], [], $pipes); usleep(150000); // geef het andere proces tijd om de lock te pakken $start = microtime(true); $store->withLock(fn() => null); $waited = microtime(true) - $start; proc_close($proc); self::assertGreaterThan(0.15, $waited, 'tweede proces moest wachten op de lock'); } }