diff --git a/src/ChildProcess.php b/src/ChildProcess.php index fd77621..ff80a07 100644 --- a/src/ChildProcess.php +++ b/src/ChildProcess.php @@ -52,16 +52,17 @@ public function all(): array } public function start( + string|array $cmd, string $alias, - array $cmd, ?string $cwd = null, ?array $env = null, bool $persistent = false ): static { + $process = $this->client->post('child-process/start', [ 'alias' => $alias, - 'cmd' => $cmd, - 'cwd' => base_path(), + 'cmd' => (array) $cmd, + 'cwd' => $cwd ?? base_path(), 'env' => $env, 'persistent' => $persistent, ])->json(); @@ -69,6 +70,20 @@ public function start( return $this->fromRuntimeProcess($process); } + public function php(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self + { + $cmd = [PHP_BINARY, ...(array) $cmd]; + + return $this->start($cmd, $alias, env: $env, persistent: $persistent); + } + + public function artisan(string|array $cmd, string $alias, ?array $env = null, ?bool $persistent = false): self + { + $cmd = ['artisan', ...(array) $cmd]; + + return $this->php($cmd, $alias, env: $env, persistent: $persistent); + } + public function stop(?string $alias = null): void { $this->client->post('child-process/stop', [ @@ -99,7 +114,7 @@ public function message(string $message, ?string $alias = null): static return $this; } - private function fromRuntimeProcess($process): static + protected function fromRuntimeProcess($process): static { if (isset($process['pid'])) { $this->pid = $process['pid']; diff --git a/src/Facades/ChildProcess.php b/src/Facades/ChildProcess.php index c0731de..89b7236 100644 --- a/src/Facades/ChildProcess.php +++ b/src/Facades/ChildProcess.php @@ -3,19 +3,24 @@ namespace Native\Laravel\Facades; use Illuminate\Support\Facades\Facade; +use Native\Laravel\ChildProcess as Implement; /** * @method static \Native\Laravel\ChildProcess[] all() - * @method static \Native\Laravel\ChildProcess get(string $alias) + * @method static \Native\Laravel\ChildProcess get(string $alias = null) * @method static \Native\Laravel\ChildProcess message(string $message, string $alias = null) - * @method static \Native\Laravel\ChildProcess restart(string $alias) - * @method static \Native\Laravel\ChildProcess start(string $alias, array $cmd, string $cwd = null, array $env = null, bool $persistent = false) - * @method static void stop(string $alias) + * @method static \Native\Laravel\ChildProcess restart(string $alias = null) + * @method static \Native\Laravel\ChildProcess start(string|array $cmd, string $alias, string $cwd = null, array $env = null, bool $persistent = false) + * @method static \Native\Laravel\ChildProcess php(string|array $cmd, string $alias, array $env = null, bool $persistent = false) + * @method static \Native\Laravel\ChildProcess artisan(string|array $cmd, string $alias, array $env = null, bool $persistent = false) + * @method static void stop(string $alias = null) */ class ChildProcess extends Facade { protected static function getFacadeAccessor() { - return \Native\Laravel\ChildProcess::class; + self::clearResolvedInstance(Implement::class); + + return Implement::class; } } diff --git a/tests/ChildProcess/ChildProcessTest.php b/tests/ChildProcess/ChildProcessTest.php new file mode 100644 index 0000000..a2d9259 --- /dev/null +++ b/tests/ChildProcess/ChildProcessTest.php @@ -0,0 +1,127 @@ +makePartial() + ->shouldAllowMockingProtectedMethods(); + + $this->instance(ChildProcessImplement::class, $mock->allows([ + 'fromRuntimeProcess' => $mock, + ])); +}); + +it('can start a child process', function () { + ChildProcess::start('foo bar', 'some-alias', 'path/to/dir', ['baz' => 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === ['foo bar'] && + $request['cwd'] === 'path/to/dir' && + $request['env'] === ['baz' => 'zah']; + }); +}); + +it('can start a php command', function () { + ChildProcess::php("-r 'sleep(5);'", 'some-alias', ['baz' => 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === [PHP_BINARY, "-r 'sleep(5);'"] && + $request['cwd'] === base_path() && + $request['env'] === ['baz' => 'zah']; + }); +}); + +it('can start a artisan command', function () { + ChildProcess::artisan('foo:bar --verbose', 'some-alias', ['baz' => 'zah']); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/start' && + $request['alias'] === 'some-alias' && + $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar --verbose'] && + $request['cwd'] === base_path() && + $request['env'] === ['baz' => 'zah']; + }); +}); + +it('accepts either a string or a array as start command argument', function () { + ChildProcess::start('foo bar', 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo bar']); + + ChildProcess::start(['foo', 'baz'], 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === ['foo', 'baz']); +}); + +it('accepts either a string or a array as php command argument', function () { + ChildProcess::php("-r 'sleep(5);'", 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, "-r 'sleep(5);'"]); + + ChildProcess::php(['-r', "'sleep(5);'"], 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, '-r', "'sleep(5);'"]); +}); + +it('accepts either a string or a array as artisan command argument', function () { + ChildProcess::artisan('foo:bar', 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:bar']); + + ChildProcess::artisan(['foo:baz'], 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cmd'] === [PHP_BINARY, 'artisan', 'foo:baz']); +}); + +it('sets the cwd to the base path if none was given', function () { + ChildProcess::start(['foo', 'bar'], 'some-alias', cwd: 'path/to/dir'); + Http::assertSent(fn (Request $request) => $request['cwd'] === 'path/to/dir'); + + ChildProcess::start(['foo', 'bar'], 'some-alias'); + Http::assertSent(fn (Request $request) => $request['cwd'] === base_path()); +}); + +it('can stop a child process', function () { + ChildProcess::stop('some-alias'); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/stop' && + $request['alias'] === 'some-alias'; + }); +}); + +it('can send messages to a child process', function () { + ChildProcess::message('some-message', 'some-alias'); + + Http::assertSent(function (Request $request) { + return $request->url() === 'http://localhost:4000/api/child-process/message' && + $request['alias'] === 'some-alias' && + $request['message'] === 'some-message'; + }); +}); + +it('can mark a process as persistent', function () { + ChildProcess::start('foo bar', 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + +it('can mark a php command as persistent', function () { + ChildProcess::php("-r 'sleep(5);'", 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + +it('can mark a artisan command as persistent', function () { + ChildProcess::artisan('foo:bar', 'some-alias', persistent: true); + Http::assertSent(fn (Request $request) => $request['persistent'] === true); +}); + +it('marks the process as non-persistent by default', function () { + ChildProcess::start('foo bar', 'some-alias'); + Http::assertSent(fn (Request $request) => $request['persistent'] === false); +});