-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Testing sub-component * Fix system info detection * Fix system info detection * Disable service client test * Add readme
- Loading branch information
1 parent
dd7f3ee
commit 22b5ecd
Showing
16 changed files
with
360 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,4 @@ Thumbs.db | |
*.exe | ||
rr | ||
.rr.yaml | ||
temporal-test-server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
## Testing framework | ||
|
||
### Quick start | ||
1. Create `bootstrap.php` in `tests` folder with the following contents: | ||
```php | ||
declare(strict_types=1); | ||
|
||
require __DIR__ . '/../vendor/autoload.php'; | ||
|
||
use Temporal\Testing\Environment; | ||
|
||
$environment = Environment::create(); | ||
$environment->start(); | ||
register_shutdown_function(fn () => $environment->stop()); | ||
``` | ||
|
||
2. Add `bootstrap.php` to your `phpunit.xml`: | ||
```xml | ||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" | ||
bootstrap="tests/bootstrap.php" | ||
> | ||
</phpunit> | ||
``` | ||
|
||
3. Add test server executable to `.gitignore`: | ||
```gitignore | ||
temporal-test-server | ||
``` | ||
|
||
### How it works | ||
For testing workflows there is no need to run a full Temporal server (with storage and ui interface). | ||
Instead, we can use a light-weight test server. | ||
|
||
The code in `bootstrap.php` will start/stop (and download if it doesn't exist) Temporal test | ||
server and RoadRunner for every phpunit run. Test server runs as a regular server on 7233 port. | ||
Thus, if you use default connection settings, there is no need to change them. | ||
|
||
Under the hood RoadRunner is started with `rr serve` command. You can specify your own command in `bootstrap.php`: | ||
```php | ||
$environment->start('./rr serve -c .rr.test.yaml -w tests'); | ||
``` | ||
|
||
The snippet above will start Temporal test server and RoadRunner with `.rr.test.yaml` config and `tests` working | ||
directory. Having a separate RoadRunner config file for tests can be useful to mock you activities. For | ||
example, you can create a separate *worker* that registers activity implementations mocks: | ||
|
||
```yaml | ||
# test/.rr.test.yaml | ||
server: | ||
command: "php worker.test.php" | ||
``` | ||
And within the worker you register your workflows and mock activities: | ||
```php | ||
// worker.test.php | ||
$factory = WorkerFactory::create(); | ||
|
||
$worker = $factory->newWorker(); | ||
$worker->registerWorkflowTypes(MyWorkflow::class); | ||
$worker->registerActivity(MyActvivityMock::class); | ||
$factory->run(); | ||
``` | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Temporal\Testing; | ||
|
||
use Symfony\Component\Filesystem\Filesystem; | ||
use Symfony\Contracts\HttpClient\HttpClientInterface; | ||
|
||
final class Downloader | ||
{ | ||
private const LATEST_JAVA_SDK_RELEASE = 'https://api.github.com/repos/temporalio/sdk-java/releases/latest'; | ||
private Filesystem $filesystem; | ||
private HttpClientInterface $httpClient; | ||
|
||
public function __construct(Filesystem $filesystem, HttpClientInterface $httpClient) | ||
{ | ||
$this->filesystem = $filesystem; | ||
$this->httpClient = $httpClient; | ||
} | ||
|
||
private function findAsset(array $assets, string $systemPlatform, string $systemArch): array | ||
{ | ||
foreach ($assets as $asset) { | ||
preg_match('/^temporal-test-server_[^_]+_([^_]+)_([^.]+)\.(?:zip|tar.gz)$/', $asset['name'], $match); | ||
[, $assetPlatform, $assetArch] = $match; | ||
|
||
if ($assetPlatform === $systemPlatform) { | ||
// TODO: assetArch === systemArch (no arm builds for test server yet) | ||
return $asset; | ||
} | ||
} | ||
|
||
throw new \RuntimeException("Asset for $systemPlatform not found"); | ||
} | ||
|
||
public function download(SystemInfo $systemInfo): void | ||
{ | ||
$asset = $this->getAsset($systemInfo->platform, $systemInfo->arch); | ||
$assetUrl = $asset['browser_download_url']; | ||
$pathToExtractedAsset = $this->downloadAsset($assetUrl); | ||
|
||
$targetPath = getcwd() . DIRECTORY_SEPARATOR . $systemInfo->temporalServerExecutable; | ||
$this->filesystem->copy($pathToExtractedAsset . DIRECTORY_SEPARATOR . $systemInfo->temporalServerExecutable, $targetPath); | ||
$this->filesystem->chmod($targetPath, 755); | ||
$this->filesystem->remove($pathToExtractedAsset); | ||
} | ||
|
||
public function check(string $filename): bool | ||
{ | ||
return $this->filesystem->exists($filename); | ||
} | ||
|
||
private function downloadAsset(string $assetUrl): string | ||
{ | ||
$response = $this->httpClient->request('GET', $assetUrl); | ||
$assetPath = getcwd() . DIRECTORY_SEPARATOR . basename($assetUrl); | ||
|
||
if ($this->filesystem->exists($assetPath)) { | ||
$this->filesystem->remove($assetPath); | ||
} | ||
$this->filesystem->touch($assetPath); | ||
$this->filesystem->appendToFile($assetPath, $response->getContent()); | ||
|
||
$phar = new \PharData($assetPath); | ||
$extractedPath = getcwd() . DIRECTORY_SEPARATOR . $phar->getFilename(); | ||
if (!$this->filesystem->exists($extractedPath)) { | ||
$phar->extractTo(getcwd()); | ||
} | ||
$this->filesystem->remove($phar->getPath()); | ||
|
||
return $extractedPath; | ||
} | ||
|
||
private function getAsset(string $systemPlatform, string $systemArch): array | ||
{ | ||
$response = $this->httpClient->request('GET', self::LATEST_JAVA_SDK_RELEASE); | ||
$assets = $response->toArray()['assets']; | ||
|
||
return $this->findAsset($assets, $systemPlatform, $systemArch); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Temporal\Testing; | ||
|
||
use Symfony\Component\Console\Output\ConsoleOutput; | ||
use Symfony\Component\Console\Output\Output; | ||
use Symfony\Component\Filesystem\Filesystem; | ||
use Symfony\Component\HttpClient\HttpClient; | ||
use Symfony\Component\Process\Process; | ||
|
||
final class Environment | ||
{ | ||
private Downloader $downloader; | ||
private Output $output; | ||
private SystemInfo $systemInfo; | ||
private ?Process $temporalServerProcess = null; | ||
private ?Process $roadRunnerProcess = null; | ||
|
||
public function __construct(Output $output, Downloader $downloader, SystemInfo $systemInfo) { | ||
$this->downloader = $downloader; | ||
$this->systemInfo = $systemInfo; | ||
$this->output = $output; | ||
} | ||
|
||
public static function create(): self | ||
{ | ||
return new self( | ||
new ConsoleOutput(), | ||
new Downloader(new Filesystem(), HttpClient::create()), | ||
SystemInfo::detect(), | ||
); | ||
} | ||
|
||
public function start(string $rrCommand = null): void | ||
{ | ||
if (!$this->downloader->check($this->systemInfo->temporalServerExecutable)) { | ||
$this->output->write('Download temporal test server... '); | ||
$this->downloader->download($this->systemInfo); | ||
$this->output->writeln('<info>done.</info>'); | ||
} | ||
|
||
$this->output->write('Starting Temporal test server... '); | ||
$this->temporalServerProcess = new Process([$this->systemInfo->temporalServerExecutable, 7233,]); | ||
$this->temporalServerProcess->setTimeout(10); | ||
$this->temporalServerProcess->start(); | ||
$this->output->writeln('<info>done.</info>'); | ||
sleep(1); | ||
|
||
$this->roadRunnerProcess = new Process( | ||
$rrCommand ? explode(' ', $rrCommand) : [$this->systemInfo->rrExecutable, 'serve'] | ||
); | ||
$this->roadRunnerProcess->setTimeout(10); | ||
|
||
$this->output->write('Starting RoadRunner... '); | ||
$this->roadRunnerProcess->start(); | ||
|
||
if (!$this->roadRunnerProcess->isRunning()) { | ||
$this->output->writeln('<error>error</error>'); | ||
$this->output->writeln('Error starting RoadRunner: ' . $this->roadRunnerProcess->getErrorOutput()); | ||
exit(1); | ||
} | ||
|
||
$this->roadRunnerProcess->waitUntil( | ||
fn($type, $output) => strpos($output, 'RoadRunner server started') !== false | ||
); | ||
$this->output->writeln('<info>done.</info>'); | ||
} | ||
|
||
public function stop(): void | ||
{ | ||
if ($this->temporalServerProcess !== null && $this->temporalServerProcess->isRunning()) { | ||
$this->output->write('Stopping Temporal server... '); | ||
$this->temporalServerProcess->stop(); | ||
$this->output->writeln('<info>done.</info>'); | ||
} | ||
|
||
if ($this->roadRunnerProcess !== null && $this->roadRunnerProcess->isRunning()) { | ||
$this->output->write('Stopping RoadRunner... '); | ||
$this->roadRunnerProcess->stop(); | ||
$this->output->writeln('<info>done.</info>'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Temporal\Testing; | ||
|
||
use Spiral\RoadRunner\Console\Environment\Architecture; | ||
use Spiral\RoadRunner\Console\Environment\OperatingSystem; | ||
|
||
final class SystemInfo | ||
{ | ||
private const PLATFORM_MAPPINGS = [ | ||
'darwin' => 'macOS', | ||
'linux' => 'linux', | ||
'windows' => 'windows', | ||
]; | ||
|
||
private const ARCHITECTURE_MAPPINGS = [ | ||
'x64' => 'amd64', | ||
'amd64' => 'amd64', | ||
'arm64' => 'aarch64' | ||
]; | ||
|
||
private const TEMPORAL_EXECUTABLE_MAP = [ | ||
'darwin' => './temporal-test-server', | ||
'linux' => './temporal-test-server', | ||
'windows' => 'temporal-test-server.exe', | ||
]; | ||
|
||
private const RR_EXECUTABLE_MAP = [ | ||
'darwin' => './rr', | ||
'linux' => './rr', | ||
'windows' => 'rr.exe', | ||
]; | ||
|
||
public string $arch; | ||
public string $platform; | ||
public string $os; | ||
public string $temporalServerExecutable; | ||
public string $rrExecutable; | ||
|
||
private function __construct( | ||
string $arch, | ||
string $platform, | ||
string $os, | ||
string $temporalServerExecutable, | ||
string $rrExecutable | ||
) { | ||
$this->arch = $arch; | ||
$this->platform = $platform; | ||
$this->os = $os; | ||
$this->temporalServerExecutable = $temporalServerExecutable; | ||
$this->rrExecutable = $rrExecutable; | ||
} | ||
|
||
public static function detect(): self | ||
{ | ||
$os = OperatingSystem::createFromGlobals(); | ||
|
||
return new self( | ||
$os, | ||
self::PLATFORM_MAPPINGS[$os], | ||
self::ARCHITECTURE_MAPPINGS[Architecture::createFromGlobals()], | ||
self::TEMPORAL_EXECUTABLE_MAP[$os], | ||
self::RR_EXECUTABLE_MAP[$os], | ||
); | ||
} | ||
} |
Oops, something went wrong.