From c7bf18875a0084e5d039da0590ffa3dff781c4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ewilan=20Rivi=C3=A8re?= Date: Fri, 2 Feb 2024 16:31:28 +0100 Subject: [PATCH] Remove notify and notifier --- src/Commands/NotifierCommand.php | 79 --------- src/Commands/NotifyCommand.php | 57 ------- src/Services/Notify/DiscordNotify.php | 41 ----- src/Services/Notify/Notifying.php | 179 -------------------- src/Services/Notify/SlackNotify.php | 43 ----- src/Services/NotifyService.php | 88 ---------- src/StewardConfig.php | 15 -- src/StewardServiceProvider.php | 2 - src/Utils/Discord.php | 73 --------- src/Utils/Journal.php | 124 -------------- src/Utils/Notifier.php | 104 ------------ src/Utils/Notifier/INotifier.php | 23 --- src/Utils/Notifier/NotifierDiscord.php | 82 --------- src/Utils/Notifier/NotifierMail.php | 219 ------------------------- src/Utils/Notifier/NotifierSlack.php | 49 ------ tests/Utils/NotifierTest.php | 72 -------- 16 files changed, 1250 deletions(-) delete mode 100644 src/Commands/NotifierCommand.php delete mode 100644 src/Commands/NotifyCommand.php delete mode 100644 src/Services/Notify/DiscordNotify.php delete mode 100644 src/Services/Notify/Notifying.php delete mode 100644 src/Services/Notify/SlackNotify.php delete mode 100644 src/Services/NotifyService.php delete mode 100644 src/Utils/Discord.php delete mode 100644 src/Utils/Journal.php delete mode 100644 src/Utils/Notifier.php delete mode 100644 src/Utils/Notifier/INotifier.php delete mode 100644 src/Utils/Notifier/NotifierDiscord.php delete mode 100644 src/Utils/Notifier/NotifierMail.php delete mode 100644 src/Utils/Notifier/NotifierSlack.php delete mode 100644 tests/Utils/NotifierTest.php diff --git a/src/Commands/NotifierCommand.php b/src/Commands/NotifierCommand.php deleted file mode 100644 index 65b1d6b5..00000000 --- a/src/Commands/NotifierCommand.php +++ /dev/null @@ -1,79 +0,0 @@ -title(); - - $this->message = (string) $this->argument('message'); - $this->type = (string) $this->option('type'); - $this->webhook = (string) $this->option('webhook'); - - $this->info("Sending notification to {$this->type}..."); - - if ($this->type === 'mail') { - Notifier::mail() - ->auto() - ->message($this->message) - ->send(); - - return Command::SUCCESS; - } - - if ($this->type === 'discord') { - $this->info("Webhook: {$this->webhook}"); - Notifier::discord($this->webhook) - ->message($this->message) - ->send(); - - return Command::SUCCESS; - } - - if ($this->type === 'slack') { - $this->info("Webhook: {$this->webhook}"); - Notifier::slack($this->webhook) - ->message($this->message) - ->send(); - - return Command::SUCCESS; - } - - $this->error('Type not found.'); - - return Command::FAILURE; - } -} diff --git a/src/Commands/NotifyCommand.php b/src/Commands/NotifyCommand.php deleted file mode 100644 index 5e967956..00000000 --- a/src/Commands/NotifyCommand.php +++ /dev/null @@ -1,57 +0,0 @@ -title(); - - $this->message = strval($this->argument('message')) ?: 'Hello world!'; - $this->application = strval($this->option('application')) ?: 'discord'; - $options = $this->option('options') ?: null; - $options = explode(':', $options); - $this->options = $options; - - $notify = NotifyService::make() - ->message($this->message) - ->send(); - - $this->newLine(); - - $this->info('Done!'); - - return $notify->isSuccess(); - } -} diff --git a/src/Services/Notify/DiscordNotify.php b/src/Services/Notify/DiscordNotify.php deleted file mode 100644 index bc128217..00000000 --- a/src/Services/Notify/DiscordNotify.php +++ /dev/null @@ -1,41 +0,0 @@ -options[0] ?? null; - // $options['token'] = $self->options[1] ?? null; - - // $self->options = $options; - - if (! $self->options['id'] || ! $self->options['token']) { - throw new \Exception("Missing ID or token for server {$self->options['id']}:{$self->options['token']}"); - } - - return $self; - } - - protected function setDefaultOptions(): self - { - if (empty($this->options)) { - $options = $this->defaultOptions; - $this->options = explode(':', $options); - } - - $options = []; - $options['id'] = $this->options[0] ?? null; - $options['token'] = $this->options[1] ?? null; - - $this->options = $options; - - return $this; - } -} diff --git a/src/Services/Notify/Notifying.php b/src/Services/Notify/Notifying.php deleted file mode 100644 index 0aba71b3..00000000 --- a/src/Services/Notify/Notifying.php +++ /dev/null @@ -1,179 +0,0 @@ -url; - } - - public function message(): ?string - { - return $this->message; - } - - public function options(): array - { - return $this->options; - } - - public function isSuccess(): bool - { - return $this->success; - } - - public function response(): ?ResponseInterface - { - return $this->response; - } - - protected static function prepare(Notifying $instance, NotifyApplication $application) - { - $self = $instance; - $self->application = $application; - - $self->defaultOptions = match ($self->application) { - NotifyApplication::discord => StewardConfig::notifyDiscord(), - NotifyApplication::slack => StewardConfig::notifySlack(), - }; - - $self->setDefaultOptions(); - - $baseUrl = match ($self->application) { - NotifyApplication::discord => 'https://discord.com/api/webhooks/', - NotifyApplication::slack => 'https://hooks.slack.com/services/', - }; - - $config = match ($self->application) { - NotifyApplication::discord => [ - 'body' => 'content', - 'code' => 204, - ], - NotifyApplication::slack => [ - 'body' => 'text', - 'code' => 200, - ], - }; - - $params = implode('/', $self->options); - $self->url = "{$baseUrl}{$params}"; - - $self->guzzle($config); - - return $self; - } - - protected function guzzle(array $config): self - { - $client = new \GuzzleHttp\Client(); - $this->response = $client->request('POST', $this->url, [ - 'headers' => [ - 'Accept' => 'application/json', - ], - 'json' => [ - $config['body'] => $this->message, - ], - 'http_errors' => false, - ]); - - $code = $this->response->getStatusCode(); - - if ($code === $config['code']) { - $this->success = true; - } - - return $this; - } - - // protected function parseLocalConfig(): array - // { - // $servers = []; - // $config = StewardConfig::notifyDiscordServers(); - - // foreach ($config as $key => $value) { - // $value = explode(':', $value); - // $id = $value[0] ?? null; - // $token = $value[1] ?? null; - - // $servers[$key] = [ - // 'name' => $key, - // 'id' => $id, - // 'token' => $token, - // ]; - - // if (! $id || ! $token) { - // throw new \Exception("Missing ID or token for server {$key}={$id}:{$token}"); - // } - // } - - // return $servers; - // } - - // protected function parseInlineConfig(?string $inlineConfig = null): array - // { - // if (! $inlineConfig) { - // return []; - // } - - // $servers = []; - // $config = explode(',', $inlineConfig); - - // foreach ($config as $key => $value) { - // $value = explode(':', $value); - // $name = $value[0] ?? null; - // $id = $value[1] ?? null; - // $token = $value[2] ?? null; - - // $servers[$name] = [ - // 'name' => $name, - // 'id' => $id, - // 'token' => $token, - // ]; - // } - - // return $servers; - // } - - protected function handleSendTo(?string $sendto = null): array - { - if (! $sendto) { - return []; - } - - $data = explode(':', $sendto); - $app = $data[0] ?? null; - $id = $data[1] ?? null; - $token = $data[2] ?? null; - - if (! $app || ! $id || ! $token) { - throw new \Exception('Invalid sendto pattern.'); - } - - return [ - 'app' => $app, - 'id' => $id, - 'token' => $token, - ]; - } -} diff --git a/src/Services/Notify/SlackNotify.php b/src/Services/Notify/SlackNotify.php deleted file mode 100644 index 219d6653..00000000 --- a/src/Services/Notify/SlackNotify.php +++ /dev/null @@ -1,43 +0,0 @@ -options[0] ?? null; - // $options['token'] = $self->options[1] ?? null; - // $options['channel'] = $self->options[2] ?? null; - - // $self->options = $options; - - if (! $self->options['id'] || ! $self->options['token'] || ! $self->options['channel']) { - throw new \Exception("Missing ID or token for server {$self->options['id']}:{$self->options['token']}"); - } - - return $self; - } - - protected function setDefaultOptions(): self - { - if (empty($this->options)) { - $options = $this->defaultOptions; - $this->options = explode(':', $options); - } - - $options = []; - $options['id'] = $this->options[0] ?? null; - $options['token'] = $this->options[1] ?? null; - $options['channel'] = $this->options[2] ?? null; - - $this->options = $options; - - return $this; - } -} diff --git a/src/Services/NotifyService.php b/src/Services/NotifyService.php deleted file mode 100644 index 62076190..00000000 --- a/src/Services/NotifyService.php +++ /dev/null @@ -1,88 +0,0 @@ -application = NotifyApplication::tryFrom(StewardConfig::notifyDefault()); - - return $self; - } - - public function to(array $options): self - { - $this->options = $options; - - return $this; - } - - /** - * @param NotifyApplication|string $application default `NotifyApplication::discord`, can be string of application like `discord` - */ - public function application(NotifyApplication|string|null $application = null): self - { - if (! $application) { - $application = StewardConfig::notifyDefault(); - } - - if (is_string($application)) { - $application = match ($application) { - 'discord' => NotifyApplication::discord, - 'slack' => NotifyApplication::slack, - default => throw new \Exception("Unknown app {$application}"), - }; - } - - $this->application = $application; - - return $this; - } - - public function message(string $message): self - { - $this->message = $message; - - return $this; - } - - public function send() - { - $this->notify = match ($this->application) { - NotifyApplication::discord => DiscordNotify::send($this->options, $this->message), - NotifyApplication::slack => SlackNotify::send($this->options, $this->message), - }; - - $this->success = $this->notify->isSuccess(); - - return $this; - } - - public function isSuccess(): bool - { - return $this->success; - } -} - -enum NotifyApplication: string -{ - case discord = 'discord'; - - case slack = 'slack'; -} diff --git a/src/StewardConfig.php b/src/StewardConfig.php index 94bb8239..0a53943b 100644 --- a/src/StewardConfig.php +++ b/src/StewardConfig.php @@ -193,21 +193,6 @@ public static function gdprMatomoSiteId(): ?string return config('steward.gdpr.matomo.site_id') ?? null; } - public static function notifyDefault(): ?string - { - return config('steward.notify.default') ?? 'discord'; - } - - public static function notifyDiscord(): ?string - { - return config('steward.notify.discord') ?? null; - } - - public static function notifySlack(): ?string - { - return config('steward.notify.slack') ?? null; - } - public static function livewirePaginationTheme(): string { return config('steward.livewire.pagination.theme') ?? 'tailwind'; diff --git a/src/StewardServiceProvider.php b/src/StewardServiceProvider.php index d34c7946..62b8f5e0 100644 --- a/src/StewardServiceProvider.php +++ b/src/StewardServiceProvider.php @@ -28,8 +28,6 @@ public function configurePackage(Package $package): void \Kiwilan\Steward\Commands\LighthouseCommand::class, \Kiwilan\Steward\Commands\Log\LogClearCommand::class, \Kiwilan\Steward\Commands\MediaCleanCommand::class, - \Kiwilan\Steward\Commands\NotifyCommand::class, - \Kiwilan\Steward\Commands\NotifierCommand::class, \Kiwilan\Steward\Commands\Publish\PublishCommand::class, \Kiwilan\Steward\Commands\Publish\PublishScheduledCommand::class, \Kiwilan\Steward\Commands\RoutePrintCommand::class, diff --git a/src/Utils/Discord.php b/src/Utils/Discord.php deleted file mode 100644 index 889c5f8a..00000000 --- a/src/Utils/Discord.php +++ /dev/null @@ -1,73 +0,0 @@ -username = $username; - - return $this; - } - - public function message(array|string $message): static - { - if (is_array($message)) { - $message = implode(PHP_EOL, $message); - } - - $this->message = $message; - - return $this; - } - - public function send(): bool - { - $data = [ - 'content' => $this->message, - ]; - - if ($this->username) { - $data['username'] = $this->username; - } - - $options = [ - 'http' => [ - 'header' => "Content-Type: application/json\r\n", - 'method' => 'POST', - 'content' => json_encode($data), - ], - ]; - - $context = stream_context_create($options); - file_get_contents($this->url, false, $context); - - if ($http_response_header[0] !== 'HTTP/1.1 204 No Content') { - Log::error("Discord webhook not send {$this->url}", [ - 'http_response_header' => $http_response_header, - ]); - - return false; - } - - return true; - } -} diff --git a/src/Utils/Journal.php b/src/Utils/Journal.php deleted file mode 100644 index 582642ca..00000000 --- a/src/Utils/Journal.php +++ /dev/null @@ -1,124 +0,0 @@ -log(); - } - - public static function info(string $message, array $data = []): self - { - - return new self($message, 'info', $data); - } - - public static function debug(string $message, array $data = []): self - { - return new self($message, 'debug', $data); - } - - public static function warning(string $message, array $data = []): self - { - return new self($message, 'warning', $data); - } - - public static function error(string $message, array $data = []): self - { - return new self($message, 'error', $data); - } - - /** - * Handle exception, log as error and send notification to database. - * - * In `local` environment, it will return null, to not send notification to database. - */ - public static function handler(\Throwable $e): ?self - { - if (config('app.env') === 'local') { - return null; - } - - return new self($e->getMessage(), 'error', [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTraceAsString(), - ]); - } - - private function log(): void - { - Log::log($this->level, $this->message, $this->data); - } - - /** - * Send notification to database for Users with access to Filament admin panel with `filament/notifications` package. - * - * @param Model|Authenticatable|Collection|array|null $users To send notification to. - */ - public function toDatabase(Model|Authenticatable|Collection|array|null $users = null): self - { - if (! class_exists('\Filament\Notifications\Notification')) { - Log::warning('Journal: Filament notifications is not installed, check https://filamentphp.com/docs/3.x/notifications/installation'); - - return $this; - } - - if (! class_exists('\App\Models\User')) { - Log::warning('Journal: Filament notifications is installed, but User model is not found, check https://filamentphp.com/docs/3.x/notifications/installation'); - - return $this; - } - - try { - $filamentUsers = $this->users; - - if (! $filamentUsers) { - $users = '\App\Models\User'; - $filamentUsers = $users::all()->filter(fn ($user) => $user->canAccessPanel()); - } - - \Filament\Notifications\Notification::make() - ->title(ucfirst($this->level)) - ->body($this->message) - ->sendToDatabase($filamentUsers); - } catch (\Throwable $th) { - Log::error("Journal: {$th->getMessage()}"); - } - - return $this; - } - - /** - * Send notification to email, Slack or Discord. - * - * @param string $type `mail`, `slack` or `discord` - */ - public function notifier(string $type): self - { - $this->notifier = match ($type) { - 'mail' => Notifier::mail()->auto(), - 'slack' => Notifier::slack(config('steward.slack.webhook')), - 'discord' => Notifier::discord(config('steward.discord.webhook')), - default => null, - }; - - $this->notifier->message($this->message) - ->send(); - - return $this; - } -} diff --git a/src/Utils/Notifier.php b/src/Utils/Notifier.php deleted file mode 100644 index 1d3526b4..00000000 --- a/src/Utils/Notifier.php +++ /dev/null @@ -1,104 +0,0 @@ -type = 'mail'; - - return NotifierMail::make(); - } - - /** - * Send notification to Slack channel via webhook. - * - * @param string $webhook Slack webhook URL, like `https://hooks.slack.com/services/X/Y/Z` - * - * @see https://api.slack.com/messaging/webhooks - */ - public static function slack(string $webhook): NotifierSlack - { - $self = new self(); - $self->type = 'slack'; - - return NotifierSlack::make($webhook); - } - - /** - * Send notification to Discord channel via webhook. - * - * @param string $webhook Discord webhook URL, like `https://discord.com/api/webhooks/X/Y` - * - * @see https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks - */ - public static function discord(string $webhook): NotifierDiscord - { - $self = new self(); - $self->type = 'discord'; - - return NotifierDiscord::make($webhook); - } - - protected function logSending(string $message): void - { - if (config('app.debug') === true) { - Log::debug("Notifier: sending {$this->type} notification: {$message}..."); - } - } - - protected function logError(string $reason): void - { - Log::error("Notifier: notification failed: {$reason}"); - } - - protected function logSent(): void - { - if (config('app.debug') === true) { - Log::debug("Notifier: {$this->type} notification sent"); - } - } - - /** - * @param string|string[] $array - */ - protected function arrayToString(array|string $array): string - { - return implode(PHP_EOL, $array); - } - - protected function sendRequest( - string $url, - array $headers = [ - 'Accept' => 'application/json', - ], - array $bodyJson = [], - ): \Psr\Http\Message\ResponseInterface { - $client = new \GuzzleHttp\Client(); - $response = $client->request('POST', $url, [ - 'headers' => $headers, - 'json' => $bodyJson, - 'http_errors' => false, - ]); - - return $response; - } -} diff --git a/src/Utils/Notifier/INotifier.php b/src/Utils/Notifier/INotifier.php deleted file mode 100644 index 01321ec2..00000000 --- a/src/Utils/Notifier/INotifier.php +++ /dev/null @@ -1,23 +0,0 @@ -message = $this->arrayToString($message); - - return $this; - } - - /** - * Set username, different from default webhook username. - */ - public function username(string $username): self - { - $this->username = $username; - - return $this; - } - - /** - * Set avatar url, different from default webhook avatar url. - */ - public function avatarUrl(string $avatarUrl): self - { - $this->avatarUrl = $avatarUrl; - - return $this; - } - - public function send(): bool - { - $body = [ - 'content' => $this->message ?? '', - ]; - - if ($this->username) { - $body['username'] = $this->username; - } - - if ($this->avatarUrl) { - $body['avatar_url'] = $this->avatarUrl; - } - - $res = $this->sendRequest($this->webhook, bodyJson: $body); - - if ($res->getStatusCode() !== 204) { - $this->logError("status code {$res->getStatusCode()}, {$res->getBody()->getContents()}"); - - return false; - } - - return true; - } - - public function toArray(): array - { - return [ - 'webhook' => $this->webhook, - 'message' => $this->message, - 'username' => $this->username, - ]; - } -} diff --git a/src/Utils/Notifier/NotifierMail.php b/src/Utils/Notifier/NotifierMail.php deleted file mode 100644 index f01399de..00000000 --- a/src/Utils/Notifier/NotifierMail.php +++ /dev/null @@ -1,219 +0,0 @@ -mailer = $mailer; - - return $this; - } - - /** - * @param string $host Mailer host, default `mailpit` - */ - public function host(string $host): self - { - $this->host = $host; - - return $this; - } - - /** - * @param int $port Mailer port, default `1025` - */ - public function port(int $port): self - { - $this->port = $port; - - return $this; - } - - /** - * @param string $encryption Mailer encryption, default `tls` - */ - public function encryption(string $encryption): self - { - $this->encryption = $encryption; - - return $this; - } - - public function credentials(string $username, string $password): self - { - $this->username = $username; - $this->password = $password; - - return $this; - } - - /** - * Use default mailer from `.env` file. - */ - public function auto(): self - { - $this->mailer = config('mail.mailer'); - $this->host = config('mail.host'); - $this->port = config('mail.port'); - $this->encryption = config('mail.encryption'); - $this->username = config('mail.username'); - $this->password = config('mail.password'); - $this->from = new Address(config('mail.from.address'), config('mail.from.name')); - - return $this; - } - - /** - * @param Address[]|string $to Array of `Address` object - * @param string|null $name Useful if `$to` is a string - */ - public function to(array|string $to, ?string $name = null): self - { - if (is_string($to)) { - $to = [new Address($to, $name)]; - } - - $this->to = $to; - - return $this; - } - - public function from(string $from, ?string $name = null): self - { - $this->from = new Address($from, $name ?? ''); - - return $this; - } - - public function replyTo(string $replyTo, ?string $name = null): self - { - $this->replyTo = new Address($replyTo, $name); - - return $this; - } - - public function subject(string $subject): self - { - $this->subject = $subject; - - return $this; - } - - public function message(array|string $message): self - { - $this->message = $this->arrayToString($message); - - return $this; - } - - /** - * @param string|string[] $html - */ - public function html(array|string $html): self - { - $this->html = $this->arrayToString($html); - - return $this; - } - - public function send(): bool - { - $this->mailTransport = Transport::fromDsn("{$this->mailer}://{$this->host}:{$this->port}"); - $this->mailMailer = new Mailer($this->mailTransport); - - $this->logSending("{$this->mailer}://{$this->host}:{$this->port}"); - - $this->mailEmail = (new Email()) - ->to(...$this->to) - ->from($this->from); - - if ($this->replyTo) { - $this->mailEmail->replyTo($this->replyTo); - } - - if ($this->subject) { - $this->mailEmail->subject($this->subject); - } - - if ($this->message) { - $this->mailEmail->text($this->message); - } - - if ($this->html) { - $this->mailEmail->html($this->html); - } - - if (! $this->html) { - $this->mailEmail->html($this->message); - } - - try { - $this->mailMailer->send($this->mailEmail); - } catch (\Throwable $th) { - $this->logError($th->getMessage()); - - return false; - } - - $this->logSent(); - - return true; - } - - public function toArray(): array - { - return [ - 'mailer' => $this->mailer, - 'host' => $this->host, - 'port' => $this->port, - 'encryption' => $this->encryption, - 'username' => $this->username, - 'password' => $this->password, - 'to' => $this->to, - 'from' => $this->from, - 'replyTo' => $this->replyTo, - 'subject' => $this->subject, - 'message' => $this->message, - 'html' => $this->html, - ]; - } -} diff --git a/src/Utils/Notifier/NotifierSlack.php b/src/Utils/Notifier/NotifierSlack.php deleted file mode 100644 index fb1f98f0..00000000 --- a/src/Utils/Notifier/NotifierSlack.php +++ /dev/null @@ -1,49 +0,0 @@ -message = $this->arrayToString($message); - - return $this; - } - - public function send(): bool - { - $res = $this->sendRequest($this->webhook, bodyJson: [ - 'text' => $this->message, - ]); - - if ($res->getStatusCode() !== 200) { - $this->logError("status code {$res->getStatusCode()}, {$res->getBody()->getContents()}"); - - return false; - } - - return true; - } - - public function toArray(): array - { - return [ - 'webhook' => $this->webhook, - 'message' => $this->message, - ]; - } -} diff --git a/tests/Utils/NotifierTest.php b/tests/Utils/NotifierTest.php deleted file mode 100644 index 0feca308..00000000 --- a/tests/Utils/NotifierTest.php +++ /dev/null @@ -1,72 +0,0 @@ -skipOnLinux(); - -it('can use mail', function () { - $notifier = Notifier::mail() - ->to([dotenv()['MAIL_FROM_ADDRESS']]) - ->from(dotenv()['MAIL_FROM_ADDRESS']) - ->mailer(dotenv()['MAIL_MAILER']) - ->host(dotenv()['MAIL_HOST']) - ->port(dotenv()['MAIL_PORT']) - ->credentials(dotenv()['MAIL_USERNAME'], dotenv()['MAIL_PASSWORD']) - ->encryption(dotenv()['MAIL_ENCRYPTION']) - ->subject('Notify test') - ->message('Notify test') - ->send(); - - expect($notifier)->toBeTrue(); -})->skipOnLinux(); - -it('can use mail auto', function () { - $notifier = Notifier::mail() - ->auto() - ->to([dotenv()['MAIL_FROM_ADDRESS']]) - ->message('Notify test') - ->send(); - - expect($notifier)->toBeTrue(); -}); - -it('can use slack', function () { - $notifier = Notifier::slack(dotenv()['STEWARD_SLACK_WEBHOOK']) - ->message('Notify test') - ->send(); - - expect($notifier)->toBeTrue(); -}); - -it('can use discord', function () { - $notifier = Notifier::discord(dotenv()['STEWARD_DISCORD_WEBHOOK']) - ->username('Steward') - ->avatarUrl('https://ewilan-riviere.com/images/ewilan-riviere.webp') - ->message('Notify test') - ->send(); - - expect($notifier)->toBeTrue(); -}); - -it('can use command', function () { - $command = Artisan::call('notifier', [ - 'message' => 'Notify test from command', - '--type' => 'discord', - '--webhook' => dotenv()['STEWARD_DISCORD_WEBHOOK'], - ]); - - expect($command)->toBe(0); -});