From 6536878562a398546198f8f94b0d2babccd2b9f2 Mon Sep 17 00:00:00 2001 From: Stephen Ball Date: Sun, 5 Mar 2017 20:36:13 +0000 Subject: [PATCH] Changing model events to use observer classes --- Makefile | 6 +- app/Channel.php | 15 -- app/CheckUrl.php | 37 +--- app/Deployment.php | 1 + app/{ => Events}/Listeners/ClearJwt.php | 2 +- app/{ => Events}/Listeners/CreateJwt.php | 2 +- .../Listeners/SendCheckUrlNotifications.php} | 4 +- .../Listeners/SendDeploymentNotifications.php | 3 +- .../Listeners/SendEmailChangeConfirmation.php | 2 +- .../Listeners/SendHeartbeatNotifications.php} | 5 +- .../Listeners/SendSignupEmail.php | 2 +- .../Listeners/TestProjectUrls.php | 2 +- app/Events/Observers/ChannelObserver.php | 36 ++++ app/Events/Observers/CheckUrlObserver.php | 60 ++++++ app/Events/Observers/HeartbeatObserver.php | 23 +++ app/Events/Observers/ProjectObserver.php | 46 +++++ app/Events/Observers/ServerLogObserver.php | 43 +++++ app/Heartbeat.php | 15 -- app/Jobs/RequestProjectCheckUrl.php | 4 +- .../Configurable/DeploymentFinished.php | 2 +- .../Configurable/HeartbeatChanged.php | 2 +- app/Notifications/Configurable/UrlChanged.php | 2 +- app/Project.php | 28 +-- app/Providers/EventServiceProvider.php | 48 +++-- app/ServerLog.php | 20 -- config/app.php | 1 + config/view.php | 2 +- database/seeds/DatabaseSeeder.php | 6 +- storage/framework/testing/.gitignore | 0 tests/Unit/ChannelTest.php | 115 +++++++++++ ...tion.yml => SendCheckUrlNotifications.yml} | 0 .../Listeners/SendDeploymentNotifications.yml | 2 +- ...ail.yml => SendHeartbeatNotifications.yml} | 0 tests/Unit/Database/ChannelTest.php | 126 ------------- tests/Unit/Database/CheckUrlTest.php | 105 ++--------- tests/Unit/Database/HeartbeatTest.php | 41 ---- tests/Unit/Database/ProjectTest.php | 159 ---------------- tests/Unit/Database/ServerLogTest.php | 49 ----- .../{ => Events}/Listeners/ClearJwtTest.php | 6 +- .../{ => Events}/Listeners/CreateJwtTest.php | 6 +- .../SendCheckUrlNotificationsTest.php} | 13 +- .../SendDeploymentNotificationsTest.php | 8 +- .../SendEmailChangeConfirmationTest.php | 6 +- .../SendHeartbeatNotificationsTest.php} | 13 +- .../Listeners/SendSignupEmailTest.php | 6 +- .../Listeners/TestProjectUrlsTest.php | 6 +- .../Events/Observers/ChannelObserverTest.php | 31 +++ .../Events/Observers/CheckUrlObserverTest.php | 141 ++++++++++++++ .../Observers/HeartbeatObserverTest.php | 63 +++++++ .../Events/Observers/ProjectObserverTest.php | 178 ++++++++++++++++++ .../Observers/ServerLogObserverTest.php | 70 +++++++ tests/Unit/HeartbeatTest.php | 3 +- .../Unit/Jobs/RequestProjectCheckUrlTest.php | 8 +- .../EloquentUserRepositoryTest.php | 5 +- 54 files changed, 925 insertions(+), 654 deletions(-) rename app/{ => Events}/Listeners/ClearJwt.php (90%) rename app/{ => Events}/Listeners/CreateJwt.php (97%) rename app/{Listeners/SendCheckUrlNotification.php => Events/Listeners/SendCheckUrlNotifications.php} (93%) rename app/{ => Events}/Listeners/SendDeploymentNotifications.php (96%) rename app/{ => Events}/Listeners/SendEmailChangeConfirmation.php (93%) rename app/{Listeners/SendHeartbeatNotification.php => Events/Listeners/SendHeartbeatNotifications.php} (93%) rename app/{ => Events}/Listeners/SendSignupEmail.php (93%) rename app/{ => Events}/Listeners/TestProjectUrls.php (94%) create mode 100644 app/Events/Observers/ChannelObserver.php create mode 100644 app/Events/Observers/CheckUrlObserver.php create mode 100644 app/Events/Observers/HeartbeatObserver.php create mode 100644 app/Events/Observers/ProjectObserver.php create mode 100644 app/Events/Observers/ServerLogObserver.php create mode 100644 storage/framework/testing/.gitignore rename tests/Unit/DataProvider/Listeners/{SendCheckUrlNotification.yml => SendCheckUrlNotifications.yml} (100%) rename tests/Unit/DataProvider/Listeners/{SendSignupEmail.yml => SendHeartbeatNotifications.yml} (100%) rename tests/Unit/{ => Events}/Listeners/ClearJwtTest.php (70%) rename tests/Unit/{ => Events}/Listeners/CreateJwtTest.php (90%) rename tests/Unit/{Listeners/SendCheckUrlNotificationTest.php => Events/Listeners/SendCheckUrlNotificationsTest.php} (80%) rename tests/Unit/{ => Events}/Listeners/SendDeploymentNotificationsTest.php (92%) rename tests/Unit/{ => Events}/Listeners/SendEmailChangeConfirmationTest.php (82%) rename tests/Unit/{Listeners/SendHeartbeatNotificationTest.php => Events/Listeners/SendHeartbeatNotificationsTest.php} (77%) rename tests/Unit/{ => Events}/Listeners/SendSignupEmailTest.php (80%) rename tests/Unit/{ => Events}/Listeners/TestProjectUrlsTest.php (88%) create mode 100644 tests/Unit/Events/Observers/ChannelObserverTest.php create mode 100644 tests/Unit/Events/Observers/CheckUrlObserverTest.php create mode 100644 tests/Unit/Events/Observers/HeartbeatObserverTest.php create mode 100644 tests/Unit/Events/Observers/ProjectObserverTest.php create mode 100644 tests/Unit/Events/Observers/ServerLogObserverTest.php diff --git a/Makefile b/Makefile index aceaabfaf..4ded625a5 100644 --- a/Makefile +++ b/Makefile @@ -96,10 +96,6 @@ coverage: ##@tests Test Coverage HTML --html storage/app/tmp/coverage/ --clover storage/app/tmp/coverage.xml @rm storage/app/tmp/*.cov -phpunit-fast: ##@tests Unit Tests - Excluding slow model tests which touch the database - @echo "${GREEN}Fast unit tests${RESET}" - @php vendor/bin/phpunit --no-coverage --testsuite "Unit Tests" --exclude-group slow - phpunit: ##@tests Unit Tests @echo "${GREEN}Unit tests${RESET}" @php vendor/bin/phpunit --no-coverage --testsuite "Unit Tests" @@ -109,7 +105,7 @@ integration: ##@tests Integration Tests @php vendor/bin/phpunit --no-coverage --testsuite "Integration Tests" quicktest: ##@shortcuts Runs fast tests; these exclude PHPMD, slow unit tests, integration & dusk tests -quicktest: install-dev lint phpcs phpdoc-check phpcpd phpunit-fast +quicktest: install-dev lint phpcs phpdoc-check phpcpd test: ##@shortcuts Runs most tests; but excludes integration & dusk tests test: install-dev lint phpcs phpdoc-check phpunit phpcpd phpmd phpstan diff --git a/app/Channel.php b/app/Channel.php index 068ac9278..146f76115 100644 --- a/app/Channel.php +++ b/app/Channel.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Notifications\Notifiable; -use REBELinBLUE\Deployer\Notifications\System\NewTestNotification; use REBELinBLUE\Deployer\Traits\BroadcastChanges; /** @@ -67,20 +66,6 @@ public function project() return $this->belongsTo(Project::class); } - /** - * Override the boot method to bind model event listeners. - */ - public static function boot() - { - parent::boot(); - - // When the notification has been saved queue a test - static::saved(function (Channel $model) { - // FIXME: Change to use an event listener - $model->notify(new NewTestNotification(app('translator'))); - }); - } - /** * Returns the email address to send the notification to. * diff --git a/app/CheckUrl.php b/app/CheckUrl.php index dd4650b76..f63edd0c5 100644 --- a/app/CheckUrl.php +++ b/app/CheckUrl.php @@ -4,10 +4,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Foundation\Bus\DispatchesJobs; -use REBELinBLUE\Deployer\Events\UrlDown; -use REBELinBLUE\Deployer\Events\UrlUp; -use REBELinBLUE\Deployer\Jobs\RequestProjectCheckUrl; use REBELinBLUE\Deployer\Traits\BroadcastChanges; /** @@ -15,7 +11,7 @@ */ class CheckUrl extends Model { - use SoftDeletes, BroadcastChanges, DispatchesJobs; + use SoftDeletes, BroadcastChanges; const ONLINE = 0; const UNTESTED = 1; @@ -55,21 +51,6 @@ class CheckUrl extends Model */ protected $dates = ['last_seen']; - /** - * Override the boot method to bind model event listeners. - */ - public static function boot() - { - parent::boot(); - - // When saving the model, if the URL has changed we need to test it - static::saved(function (CheckUrl $model) { - if ($model->status === self::UNTESTED) { - $model->dispatch(new RequestProjectCheckUrl(collect([$model]))); - } - }); - } - /** * Define a mutator to set the status to untested if the URL changes. * @@ -92,17 +73,9 @@ public function setUrlAttribute($value) */ public function online() { - $isCurrentlyHealthy = ($this->status === self::UNTESTED || $this->isHealthy()); - $this->status = self::ONLINE; $this->missed = 0; $this->last_seen = $this->freshTimestamp(); - - if (!$isCurrentlyHealthy) { - event(new UrlUp($this)); - } - - return $this->save(); } /** @@ -112,12 +85,8 @@ public function online() */ public function offline() { - $this->status = self::OFFLINE; - $this->missed = $this->missed + 1; - - event(new UrlDown($this)); - - return $this->save(); + $this->status = self::OFFLINE; + $this->missed = $this->missed + 1; } /** diff --git a/app/Deployment.php b/app/Deployment.php index 521d56fa3..0bf139667 100644 --- a/app/Deployment.php +++ b/app/Deployment.php @@ -78,6 +78,7 @@ public static function boot() { parent::boot(); + // FIXME: Change to use the trait static::saved(function (Deployment $model) { event(new ModelChanged($model, 'deployment')); }); diff --git a/app/Listeners/ClearJwt.php b/app/Events/Listeners/ClearJwt.php similarity index 90% rename from app/Listeners/ClearJwt.php rename to app/Events/Listeners/ClearJwt.php index 8a49948bc..78397629f 100644 --- a/app/Listeners/ClearJwt.php +++ b/app/Events/Listeners/ClearJwt.php @@ -1,6 +1,6 @@ isSuccessful()) { $notification = DeploymentSucceeded::class; $event = 'deployment_success'; diff --git a/app/Listeners/SendEmailChangeConfirmation.php b/app/Events/Listeners/SendEmailChangeConfirmation.php similarity index 93% rename from app/Listeners/SendEmailChangeConfirmation.php rename to app/Events/Listeners/SendEmailChangeConfirmation.php index 86228990f..20f65c0ca 100644 --- a/app/Listeners/SendEmailChangeConfirmation.php +++ b/app/Events/Listeners/SendEmailChangeConfirmation.php @@ -1,6 +1,6 @@ isHealthy()) { $notification = HeartbeatMissing::class; $event = 'heartbeat_missing'; diff --git a/app/Listeners/SendSignupEmail.php b/app/Events/Listeners/SendSignupEmail.php similarity index 93% rename from app/Listeners/SendSignupEmail.php rename to app/Events/Listeners/SendSignupEmail.php index b95172905..24a578d47 100644 --- a/app/Listeners/SendSignupEmail.php +++ b/app/Events/Listeners/SendSignupEmail.php @@ -1,6 +1,6 @@ translator = $translator; + } + + /** + * Called when the model is saved. + * + * @param Channel $channel + */ + public function saved(Channel $channel) + { + $channel->notify(new NewTestNotification($this->translator)); + } +} diff --git a/app/Events/Observers/CheckUrlObserver.php b/app/Events/Observers/CheckUrlObserver.php new file mode 100644 index 000000000..e20ea1373 --- /dev/null +++ b/app/Events/Observers/CheckUrlObserver.php @@ -0,0 +1,60 @@ +dispatcher = $dispatcher; + } + + /** + * Called when the model is saved. + * + * @param CheckUrl $url + */ + public function saved(CheckUrl $url) + { + if ($url->status === CheckUrl::UNTESTED) { + $collection = new Collection([$url]); + + $this->dispatch(new RequestProjectCheckUrl($collection)); + } + } + + /** + * Called when the model is updated. + * + * @param CheckUrl $url + */ + public function updated(CheckUrl $url) + { + if ($url->status === CheckUrl::OFFLINE) { + $this->dispatcher->dispatch(new UrlDownEvent($url)); + } elseif ($url->status === CheckUrl::ONLINE && $url->getOriginal('status') === CheckUrl::OFFLINE) { + $this->dispatcher->dispatch(new UrlUpEvent($url)); + } + } +} diff --git a/app/Events/Observers/HeartbeatObserver.php b/app/Events/Observers/HeartbeatObserver.php new file mode 100644 index 000000000..d564cc36c --- /dev/null +++ b/app/Events/Observers/HeartbeatObserver.php @@ -0,0 +1,23 @@ +hash)) { + $heartbeat->generateHash(); + } + } +} diff --git a/app/Events/Observers/ProjectObserver.php b/app/Events/Observers/ProjectObserver.php new file mode 100644 index 000000000..822527b0d --- /dev/null +++ b/app/Events/Observers/ProjectObserver.php @@ -0,0 +1,46 @@ +private_key)) { + $this->dispatch(new GenerateKey($project)); + } elseif (empty($project->public_key)) { + $this->dispatch(new RegeneratePublicKey($project)); + } + + if (empty($project->hash)) { + $project->generateHash(); + } + } + + /** + * Called when the model is being updated. + * + * @param Project $project + */ + public function updating(Project $project) + { + if (empty($project->public_key)) { + $this->dispatch(new RegeneratePublicKey($project)); + } + } +} diff --git a/app/Events/Observers/ServerLogObserver.php b/app/Events/Observers/ServerLogObserver.php new file mode 100644 index 000000000..456c30767 --- /dev/null +++ b/app/Events/Observers/ServerLogObserver.php @@ -0,0 +1,43 @@ +dispatcher = $dispatcher; + } + + /** + * Called when the model is updated. + * + * @param ServerLog $log + */ + public function updated(ServerLog $log) + { + $outputChanged = $log->isDirty('output'); + + $this->dispatcher->dispatch(new ServerLogChanged($log)); + + if ($outputChanged) { + $this->dispatcher->dispatch(new ServerOutputChanged($log)); + } + } +} diff --git a/app/Heartbeat.php b/app/Heartbeat.php index 010d20c28..f9efa48fc 100644 --- a/app/Heartbeat.php +++ b/app/Heartbeat.php @@ -69,21 +69,6 @@ public function project() return $this->belongsTo(Project::class); } - /** - * Override the boot method to bind model event listeners. - */ - public static function boot() - { - parent::boot(); - - // When first creating the model generate a webhook hash - static::creating(function (Heartbeat $model) { - if (!array_key_exists('hash', $model->attributes)) { - $model->generateHash(); - } - }); - } - /** * Generates a hash for use in the webhook URL. */ diff --git a/app/Jobs/RequestProjectCheckUrl.php b/app/Jobs/RequestProjectCheckUrl.php index 48951a7e2..ee5187d19 100644 --- a/app/Jobs/RequestProjectCheckUrl.php +++ b/app/Jobs/RequestProjectCheckUrl.php @@ -42,9 +42,11 @@ public function handle(Client $client) $client->get($link->url); $link->online(); - } catch (\Exception $error) { // FIXME: Change te exception + } catch (\Exception $error) { // FIXME: Change the exception $link->offline(); } + + $link->save(); }); } } diff --git a/app/Notifications/Configurable/DeploymentFinished.php b/app/Notifications/Configurable/DeploymentFinished.php index bb3b31950..c21a28021 100644 --- a/app/Notifications/Configurable/DeploymentFinished.php +++ b/app/Notifications/Configurable/DeploymentFinished.php @@ -37,7 +37,7 @@ abstract class DeploymentFinished extends Notification /** * @var Translator */ - private $translator; + protected $translator; /** * Create a new notification instance. diff --git a/app/Notifications/Configurable/HeartbeatChanged.php b/app/Notifications/Configurable/HeartbeatChanged.php index 84c6972d6..917086dd5 100644 --- a/app/Notifications/Configurable/HeartbeatChanged.php +++ b/app/Notifications/Configurable/HeartbeatChanged.php @@ -30,7 +30,7 @@ abstract class HeartbeatChanged extends Notification /** * @var Translator */ - private $translator; + protected $translator; /** * Create a new notification instance. diff --git a/app/Notifications/Configurable/UrlChanged.php b/app/Notifications/Configurable/UrlChanged.php index 66092f2a0..ac2929800 100644 --- a/app/Notifications/Configurable/UrlChanged.php +++ b/app/Notifications/Configurable/UrlChanged.php @@ -30,7 +30,7 @@ abstract class UrlChanged extends Notification /** * @var Translator */ - private $translator; + protected $translator; /** * Create a new notification instance. diff --git a/app/Project.php b/app/Project.php index 86a0a55a7..416da532f 100644 --- a/app/Project.php +++ b/app/Project.php @@ -4,10 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Foundation\Bus\DispatchesJobs; use McCool\LaravelAutoPresenter\HasPresenter; -use REBELinBLUE\Deployer\Jobs\GenerateKey; -use REBELinBLUE\Deployer\Jobs\RegeneratePublicKey; use REBELinBLUE\Deployer\Traits\BroadcastChanges; use REBELinBLUE\Deployer\Traits\ProjectRelations; use REBELinBLUE\Deployer\View\Presenters\ProjectPresenter; @@ -19,7 +16,7 @@ */ class Project extends Model implements HasPresenter { - use SoftDeletes, BroadcastChanges, ProjectRelations, DispatchesJobs; + use SoftDeletes, BroadcastChanges, ProjectRelations; const FINISHED = 0; const PENDING = 1; @@ -89,29 +86,6 @@ class Project extends Model implements HasPresenter */ protected $checkurlStatus = []; - /** - * Override the boot method to bind model event listeners. - */ - public static function boot() - { - parent::boot(); - - // When creating the model generate an SSH Key pair and a webhook hash - static::saving(function (Project $model) { - if (!array_key_exists('private_key', $model->attributes) || $model->private_key === '') { - $model->dispatch(new GenerateKey($model)); - } - - if (!array_key_exists('public_key', $model->attributes) || $model->public_key === '') { - $model->dispatch(new RegeneratePublicKey($model)); - } - - if (!array_key_exists('hash', $model->attributes)) { - $model->generateHash(); - } - }); - } - /** * Determines whether the project is currently being deployed. * diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 90e2212ce..007b86fb9 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -5,22 +5,32 @@ use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Logout; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use REBELinBLUE\Deployer\Channel; +use REBELinBLUE\Deployer\CheckUrl; use REBELinBLUE\Deployer\Events\DeploymentFinished; use REBELinBLUE\Deployer\Events\EmailChangeRequested; use REBELinBLUE\Deployer\Events\HeartbeatMissed; use REBELinBLUE\Deployer\Events\HeartbeatRecovered; use REBELinBLUE\Deployer\Events\JsonWebTokenExpired; +use REBELinBLUE\Deployer\Events\Listeners\ClearJwt; +use REBELinBLUE\Deployer\Events\Listeners\CreateJwt; +use REBELinBLUE\Deployer\Events\Listeners\SendCheckUrlNotifications; +use REBELinBLUE\Deployer\Events\Listeners\SendDeploymentNotifications; +use REBELinBLUE\Deployer\Events\Listeners\SendEmailChangeConfirmation; +use REBELinBLUE\Deployer\Events\Listeners\SendHeartbeatNotifications; +use REBELinBLUE\Deployer\Events\Listeners\SendSignupEmail; +use REBELinBLUE\Deployer\Events\Listeners\TestProjectUrls; +use REBELinBLUE\Deployer\Events\Observers\ChannelObserver; +use REBELinBLUE\Deployer\Events\Observers\CheckUrlObserver; +use REBELinBLUE\Deployer\Events\Observers\HeartbeatObserver; +use REBELinBLUE\Deployer\Events\Observers\ProjectObserver; +use REBELinBLUE\Deployer\Events\Observers\ServerLogObserver; use REBELinBLUE\Deployer\Events\UrlDown; use REBELinBLUE\Deployer\Events\UrlUp; use REBELinBLUE\Deployer\Events\UserWasCreated; -use REBELinBLUE\Deployer\Listeners\ClearJwt; -use REBELinBLUE\Deployer\Listeners\CreateJwt; -use REBELinBLUE\Deployer\Listeners\SendCheckUrlNotification; -use REBELinBLUE\Deployer\Listeners\SendDeploymentNotifications; -use REBELinBLUE\Deployer\Listeners\SendEmailChangeConfirmation; -use REBELinBLUE\Deployer\Listeners\SendHeartbeatNotification; -use REBELinBLUE\Deployer\Listeners\SendSignupEmail; -use REBELinBLUE\Deployer\Listeners\TestProjectUrls; +use REBELinBLUE\Deployer\Heartbeat; +use REBELinBLUE\Deployer\Project; +use REBELinBLUE\Deployer\ServerLog; /** * The event service provider. @@ -35,13 +45,27 @@ class EventServiceProvider extends ServiceProvider protected $listen = [ DeploymentFinished::class => [SendDeploymentNotifications::class, TestProjectUrls::class], EmailChangeRequested::class => [SendEmailChangeConfirmation::class], - HeartbeatMissed::class => [SendHeartbeatNotification::class], - HeartbeatRecovered::class => [SendHeartbeatNotification::class], + HeartbeatMissed::class => [SendHeartbeatNotifications::class], + HeartbeatRecovered::class => [SendHeartbeatNotifications::class], JsonWebTokenExpired::class => [CreateJwt::class], Login::class => [CreateJwt::class], Logout::class => [ClearJwt::class], - UrlDown::class => [SendCheckUrlNotification::class], - UrlUp::class => [SendCheckUrlNotification::class], + UrlDown::class => [SendCheckUrlNotifications::class], + UrlUp::class => [SendCheckUrlNotifications::class], UserWasCreated::class => [SendSignupEmail::class], ]; + + /** + * Register the application's event listeners. + */ + public function boot() + { + parent::boot(); + + Channel::observe(ChannelObserver::class); + CheckUrl::observe(CheckUrlObserver::class); + Heartbeat::observe(HeartbeatObserver::class); + Project::observe(ProjectObserver::class); + ServerLog::observe(ServerLogObserver::class); + } } diff --git a/app/ServerLog.php b/app/ServerLog.php index e085dd3da..a7434ede2 100644 --- a/app/ServerLog.php +++ b/app/ServerLog.php @@ -4,8 +4,6 @@ use Illuminate\Database\Eloquent\Model; use McCool\LaravelAutoPresenter\HasPresenter; -use REBELinBLUE\Deployer\Events\ServerLogChanged; -use REBELinBLUE\Deployer\Events\ServerOutputChanged; use REBELinBLUE\Deployer\View\Presenters\RuntimeInterface; use REBELinBLUE\Deployer\View\Presenters\ServerLogPresenter; @@ -46,24 +44,6 @@ class ServerLog extends Model implements HasPresenter, RuntimeInterface */ protected $dates = ['started_at', 'finished_at']; - /** - * Override the boot method to bind model event listeners. - */ - public static function boot() - { - parent::boot(); - - static::updated(function (ServerLog $model) { - $outputChanged = $model->isDirty('output'); - - event(new ServerLogChanged($model)); - - if ($outputChanged) { - event(new ServerOutputChanged($model)); - } - }); - } - /** * Belongs to association. * diff --git a/config/app.php b/config/app.php index 82c0ad213..a3bf6f680 100644 --- a/config/app.php +++ b/config/app.php @@ -78,6 +78,7 @@ */ 'locale' => env('APP_LOCALE', 'en'), + 'faker_locale' => 'en_GB', /* diff --git a/config/view.php b/config/view.php index e193ab61d..2acfd9cc9 100644 --- a/config/view.php +++ b/config/view.php @@ -14,7 +14,7 @@ */ 'paths' => [ - realpath(base_path('resources/views')), + resource_path('views'), ], /* diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index f03a4cae5..cc51b2e3a 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -14,12 +14,14 @@ public function run() $this->call(ServerTableSeeder::class); $this->call(DeploymentTableSeeder::class); $this->call(CommandTableSeeder::class); - $this->call(ChannelTableSeeder::class); $this->call(HeartbeatTableSeeder::class); - //$this->call(CheckUrlTableSeeder::class); + $this->call(CheckUrlTableSeeder::class); $this->call(TemplateTableSeeder::class); $this->call(VariableTableSeeder::class); $this->call(SharedFileTableSeeder::class); $this->call(ConfigFileTableSeeder::class); + + // Have this seeder last so that there are no channels defined when seeding! + $this->call(ChannelTableSeeder::class); } } diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Unit/ChannelTest.php b/tests/Unit/ChannelTest.php index 38f6adc8e..7bec91d4b 100644 --- a/tests/Unit/ChannelTest.php +++ b/tests/Unit/ChannelTest.php @@ -45,4 +45,119 @@ public function provideEvents() { return array_chunk($this->fixture('Channel')['events'], 1); } + + /** + * @dataProvider provideTypes + * @covers ::routeNotificationForMail + */ + public function testRouteNotificationForMail($type, $expected = null, array $config = []) + { + if ($type === Channel::EMAIL) { + $expected = 'admin@example.com'; + $config = ['email' => $expected]; + } + + /** @var Channel $channel */ + $channel = factory(Channel::class)->make([ + 'type' => $type, + 'config' => $config, + 'project_id' => 1, + ]); + $actual = $channel->routeNotificationForMail(); + + $this->assertSame($expected, $actual); + } + + /** + * @covers ::routeNotificationForSlack + * @dataProvider provideTypes + */ + public function testRouteNotificationForSlack($type, $expected = null, array $config = []) + { + if ($type === Channel::SLACK) { + $expected = 'http://slack.example.com/webhook'; + $config = ['webhook' => $expected]; + } + + /** @var Channel $channel */ + $channel = factory(Channel::class)->make([ + 'type' => $type, + 'config' => $config, + 'project_id' => 1, + ]); + $actual = $channel->routeNotificationForSlack(); + + $this->assertSame($expected, $actual); + } + + /** + * @covers ::routeNotificationForWebhook + * @dataProvider provideTypes + */ + public function testRouteNotificationForWebhook($type, $expected = null, array $config = []) + { + if ($type === Channel::WEBHOOK) { + $expected = 'http://www.example.com/webhook'; + $config = ['url' => $expected]; + } + + /** @var Channel $channel */ + $channel = factory(Channel::class)->make([ + 'type' => $type, + 'config' => $config, + 'project_id' => 1, + ]); + $actual = $channel->routeNotificationForWebhook(); + + $this->assertSame($expected, $actual); + } + + /** + * @covers ::routeNotificationForTwilio + * @dataProvider provideTypes + */ + public function testRouteNotificationForTwilio($type, $expected = null, array $config = []) + { + if ($type === Channel::TWILIO) { + $expected = '+4477012345671'; + $config = ['telephone' => $expected]; + } + + /** @var Channel $channel */ + $channel = factory(Channel::class)->make([ + 'type' => $type, + 'config' => $config, + 'project_id' => 1, + ]); + $actual = $channel->routeNotificationForTwilio(); + + $this->assertSame($expected, $actual); + } + + /** + * @covers ::routeNotificationForHipchat + * @dataProvider provideTypes + */ + public function testRouteNotificationForHipchat($type, $expected = null, array $config = []) + { + if ($type === Channel::HIPCHAT) { + $expected = '#channel'; + $config = ['room' => $expected]; + } + + /** @var Channel $channel */ + $channel = factory(Channel::class)->make([ + 'type' => $type, + 'config' => $config, + 'project_id' => 1, + ]); + $actual = $channel->routeNotificationForHipchat(); + + $this->assertSame($expected, $actual); + } + + public function provideTypes() + { + return array_chunk($this->fixture('Channel')['types'], 1); + } } diff --git a/tests/Unit/DataProvider/Listeners/SendCheckUrlNotification.yml b/tests/Unit/DataProvider/Listeners/SendCheckUrlNotifications.yml similarity index 100% rename from tests/Unit/DataProvider/Listeners/SendCheckUrlNotification.yml rename to tests/Unit/DataProvider/Listeners/SendCheckUrlNotifications.yml diff --git a/tests/Unit/DataProvider/Listeners/SendDeploymentNotifications.yml b/tests/Unit/DataProvider/Listeners/SendDeploymentNotifications.yml index 6debbcf54..c4fbfa414 100644 --- a/tests/Unit/DataProvider/Listeners/SendDeploymentNotifications.yml +++ b/tests/Unit/DataProvider/Listeners/SendDeploymentNotifications.yml @@ -1,5 +1,5 @@ fixture: - notification: + notifications: - notification: REBELinBLUE\Deployer\Notifications\Configurable\DeploymentFailed successful: false diff --git a/tests/Unit/DataProvider/Listeners/SendSignupEmail.yml b/tests/Unit/DataProvider/Listeners/SendHeartbeatNotifications.yml similarity index 100% rename from tests/Unit/DataProvider/Listeners/SendSignupEmail.yml rename to tests/Unit/DataProvider/Listeners/SendHeartbeatNotifications.yml diff --git a/tests/Unit/Database/ChannelTest.php b/tests/Unit/Database/ChannelTest.php index 4006afde7..5a534093b 100644 --- a/tests/Unit/Database/ChannelTest.php +++ b/tests/Unit/Database/ChannelTest.php @@ -3,9 +3,7 @@ namespace REBELinBLUE\Deployer\Tests\Unit\Database; use Illuminate\Foundation\Testing\DatabaseMigrations; -use Illuminate\Support\Facades\Notification; use REBELinBLUE\Deployer\Channel; -use REBELinBLUE\Deployer\Notifications\System\NewTestNotification; use REBELinBLUE\Deployer\Tests\TestCase; use REBELinBLUE\Deployer\Tests\Unit\Traits\BroadcastChanges; @@ -17,130 +15,6 @@ class ChannelTest extends TestCase { use DatabaseMigrations, BroadcastChanges; - /** - * @covers ::boot - */ - public function testBoot() - { - Notification::fake(); - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make(); - $channel->save(); - - Notification::assertSentTo($channel, NewTestNotification::class); - } - - /** - * @dataProvider provideTypes - * @covers ::routeNotificationForMail - */ - public function testRouteNotificationForMail($type, $expected = null, array $config = []) - { - if ($type === Channel::EMAIL) { - $expected = 'admin@example.com'; - $config = ['email' => $expected]; - } - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make([ - 'type' => $type, - 'config' => $config, - ]); - $actual = $channel->routeNotificationForMail(); - - $this->assertSame($expected, $actual); - } - - /** - * @covers ::routeNotificationForSlack - * @dataProvider provideTypes - */ - public function testRouteNotificationForSlack($type, $expected = null, array $config = []) - { - if ($type === Channel::SLACK) { - $expected = 'http://slack.example.com/webhook'; - $config = ['webhook' => $expected]; - } - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make([ - 'type' => $type, - 'config' => $config, - ]); - $actual = $channel->routeNotificationForSlack(); - - $this->assertSame($expected, $actual); - } - - /** - * @covers ::routeNotificationForWebhook - * @dataProvider provideTypes - */ - public function testRouteNotificationForWebhook($type, $expected = null, array $config = []) - { - if ($type === Channel::WEBHOOK) { - $expected = 'http://www.example.com/webhook'; - $config = ['url' => $expected]; - } - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make([ - 'type' => $type, - 'config' => $config, - ]); - $actual = $channel->routeNotificationForWebhook(); - - $this->assertSame($expected, $actual); - } - - /** - * @covers ::routeNotificationForTwilio - * @dataProvider provideTypes - */ - public function testRouteNotificationForTwilio($type, $expected = null, array $config = []) - { - if ($type === Channel::TWILIO) { - $expected = '+4477012345671'; - $config = ['telephone' => $expected]; - } - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make([ - 'type' => $type, - 'config' => $config, - ]); - $actual = $channel->routeNotificationForTwilio(); - - $this->assertSame($expected, $actual); - } - - /** - * @covers ::routeNotificationForHipchat - * @dataProvider provideTypes - */ - public function testRouteNotificationForHipchat($type, $expected = null, array $config = []) - { - if ($type === Channel::HIPCHAT) { - $expected = '#channel'; - $config = ['room' => $expected]; - } - - /** @var Channel $channel */ - $channel = factory(Channel::class)->make([ - 'type' => $type, - 'config' => $config, - ]); - $actual = $channel->routeNotificationForHipchat(); - - $this->assertSame($expected, $actual); - } - - public function provideTypes() - { - return array_chunk($this->fixture('Channel')['types'], 1); - } - /** * @covers \REBELinBLUE\Deployer\Traits\BroadcastChanges::bootBroadcastChanges */ diff --git a/tests/Unit/Database/CheckUrlTest.php b/tests/Unit/Database/CheckUrlTest.php index 55342f5e8..ff4f3f6a9 100644 --- a/tests/Unit/Database/CheckUrlTest.php +++ b/tests/Unit/Database/CheckUrlTest.php @@ -3,14 +3,8 @@ namespace REBELinBLUE\Deployer\Tests\Unit\Database; use Carbon\Carbon; -use GuzzleHttp\Client as HttpClient; use Illuminate\Foundation\Testing\DatabaseMigrations; -use Illuminate\Support\Facades\App; -use Mockery as m; use REBELinBLUE\Deployer\CheckUrl; -use REBELinBLUE\Deployer\Events\UrlDown; -use REBELinBLUE\Deployer\Events\UrlUp; -use REBELinBLUE\Deployer\Jobs\RequestProjectCheckUrl; use REBELinBLUE\Deployer\Tests\TestCase; use REBELinBLUE\Deployer\Tests\Unit\Traits\BroadcastChanges; @@ -27,11 +21,11 @@ class CheckUrlTest extends TestCase */ public function testOffline() { - $this->expectsEvents(UrlDown::class); - /** @var CheckUrl $url */ - $url = factory(CheckUrl::class)->create([ - 'status' => CheckUrl::ONLINE, + $url = factory(CheckUrl::class)->make([ + 'project_id' => 1, + 'status' => CheckUrl::ONLINE, + 'missed' => 0, ]); $url->offline(); @@ -45,12 +39,11 @@ public function testOffline() */ public function testOfflineIncreasesMissed() { - $this->expectsEvents(UrlDown::class); - /** @var CheckUrl $url */ - $url = factory(CheckUrl::class)->create([ - 'status' => CheckUrl::OFFLINE, - 'missed' => 5, + $url = factory(CheckUrl::class)->make([ + 'project_id' => 1, + 'status' => CheckUrl::OFFLINE, + 'missed' => 5, ]); $url->offline(); @@ -64,62 +57,10 @@ public function testOfflineIncreasesMissed() */ public function testOnline() { - $this->doesntExpectEvents(UrlUp::class); - - /** @var CheckUrl $url */ - $url = factory(CheckUrl::class)->create([ - 'status' => CheckUrl::ONLINE, - ]); - - Carbon::setTestNow(Carbon::create(2016, 1, 1, 12, 15, 00, 'UTC')); - - $url->online(); - - $this->assertSame(CheckUrl::ONLINE, $url->status); - $this->assertSame(0, $url->missed); - $this->assertSameTimestamp('2016-01-01 12:15:00', $url->last_seen); - } - - /** - * @covers ::online - */ - public function testOnlineDoesNotDispatchEventWhenPreviouslyUntested() - { - $this->doesntExpectEvents(UrlUp::class); - - $url = 'http://www.example.com'; - - $client = m::mock(HttpClient::class); - $client->shouldReceive('get')->once()->with($url); - - $this->app->instance(HttpClient::class, $client); - - /** @var CheckUrl $url */ - $url = factory(CheckUrl::class)->create([ - 'status' => CheckUrl::UNTESTED, - 'url' => $url, - ]); - - Carbon::setTestNow(Carbon::create(2016, 1, 1, 12, 15, 00, 'UTC')); - - $url->online(); - - $this->assertSame(CheckUrl::ONLINE, $url->status); - $this->assertSame(0, $url->missed); - $this->assertSameTimestamp('2016-01-01 12:15:00', $url->last_seen); - } - - /** - * @covers ::online - */ - public function testOnlineDispatchesEventPreviouslyOffline() - { - $this->expectsEvents(UrlUp::class); - /** @var CheckUrl $url */ - $url = factory(CheckUrl::class)->create([ - 'status' => CheckUrl::OFFLINE, - 'missed' => 3, + $url = factory(CheckUrl::class)->make([ + 'project_id' => 1, + 'status' => CheckUrl::ONLINE, ]); Carbon::setTestNow(Carbon::create(2016, 1, 1, 12, 15, 00, 'UTC')); @@ -171,30 +112,6 @@ public function testSetUrlAttributeDoesNotChangeStatusWhenSame() $this->assertSameTimestamp('2016-01-01 12:15:00', $url->last_seen); } - /** - * @covers ::boot - */ - public function testBoot() - { - $this->expectsJobs(RequestProjectCheckUrl::class); - $this->withoutEvents(); - - factory(CheckUrl::class)->create(); - } - - /** - * @covers ::boot - */ - public function testBootDoesNotDispatchJobIfTested() - { - $this->doesntExpectJobs(RequestProjectCheckUrl::class); - $this->withoutEvents(); - - factory(CheckUrl::class)->create([ - 'status' => CheckUrl::ONLINE, - ]); - } - /** * @covers \REBELinBLUE\Deployer\Traits\BroadcastChanges::bootBroadcastChanges */ diff --git a/tests/Unit/Database/HeartbeatTest.php b/tests/Unit/Database/HeartbeatTest.php index 61e2d0996..3e3ec5e1b 100644 --- a/tests/Unit/Database/HeartbeatTest.php +++ b/tests/Unit/Database/HeartbeatTest.php @@ -81,47 +81,6 @@ public function testPingedDispatchesEventPreviouslyOffline() $this->assertSameTimestamp('2016-01-01 12:15:00', $heartbeat->last_activity); } - /** - * @covers ::boot - */ - public function testBoot() - { - $expected = 'my-fake-token'; - - $this->mockTokenGenerator($expected); - - /** @var Heartbeat $heartbeat */ - $heartbeat = factory(Heartbeat::class)->make(); - - $this->assertEmpty($heartbeat->hash); - - $heartbeat->save(); - - $this->assertSame($expected, $heartbeat->hash); - } - - /** - * @covers ::boot - */ - public function testBootShouldNotRegenerateHashIfSet() - { - $expected = 'my-fake-token'; - - // Can not mock the token generator and assert that it doesn't receive a call as the - // project class is a dependency of heartbeat and it will - - /** @var Heartbeat $heartbeat */ - $heartbeat = factory(Heartbeat::class)->make(); - - $heartbeat->hash = $expected; // Not "fillable" - - $this->assertSame($expected, $heartbeat->hash); - - $heartbeat->save(); - - $this->assertSame($expected, $heartbeat->hash); - } - /** * @covers \REBELinBLUE\Deployer\Traits\BroadcastChanges::bootBroadcastChanges */ diff --git a/tests/Unit/Database/ProjectTest.php b/tests/Unit/Database/ProjectTest.php index 0ddcc30fb..860600687 100644 --- a/tests/Unit/Database/ProjectTest.php +++ b/tests/Unit/Database/ProjectTest.php @@ -3,16 +3,11 @@ namespace REBELinBLUE\Deployer\Tests\Unit\Database; use Illuminate\Foundation\Testing\DatabaseMigrations; -use Illuminate\Support\Facades\App; -use Mockery as m; use REBELinBLUE\Deployer\CheckUrl; use REBELinBLUE\Deployer\Group; use REBELinBLUE\Deployer\Heartbeat; use REBELinBLUE\Deployer\Project; use REBELinBLUE\Deployer\Ref; -use REBELinBLUE\Deployer\Services\Filesystem\Filesystem; -use REBELinBLUE\Deployer\Services\Scripts\Runner as Process; -use REBELinBLUE\Deployer\Services\Token\TokenGeneratorInterface; use REBELinBLUE\Deployer\Tests\TestCase; use REBELinBLUE\Deployer\Tests\Unit\Traits\BroadcastChanges; @@ -154,160 +149,6 @@ public function testGetTagsAttribute() $this->assertSame($expected, array_values($project->tags->toArray())); } - /** - * @covers ::boot - */ - public function testBootBindsSavingEventToGenerateHash() - { - $expected = 'my-fake-token'; - - $this->mockTokenGenerator($expected); - - /** @var Process $process */ - $process = m::mock(Process::class); - $process->shouldNotReceive('setScript')->withAnyArgs(); - - /** @var Project $project */ - $project = factory(Project::class)->make([ - 'private_key' => 'a-private-key', - ]); - - $project->public_key = 'a-public-key'; // Not "fillable" - - $this->assertEmpty($project->hash); - - $project->save(); - - $this->assertSame($expected, $project->hash); - } - - /** - * @covers ::boot - */ - public function testBootShouldNotRegenerateFieldsIfSet() - { - $expectedHash = 'my-fake-token'; - $expectedPrivateKey = 'a-private-key'; - $expectedPublicKey = 'a-public-key'; - - /** @var Process $process */ - $process = m::mock(Process::class); - $process->shouldNotReceive('setScript')->withAnyArgs(); - - /** @var Filesystem $filesystem */ - $filesystem = m::mock(Filesystem::class); - $filesystem->shouldNotReceive('tempnam')->withAnyArgs(); - - /** @var TokenGeneratorInterface $generator */ - $generator = m::mock(TokenGeneratorInterface::class); - $generator->shouldNotReceive('generateRandom')->withAnyArgs(); - - $this->app->instance(Process::class, $process); - $this->app->instance(TokenGeneratorInterface::class, $generator); - $this->app->instance(Filesystem::class, $filesystem); - - /** @var Project $project */ - $project = factory(Project::class)->make([ - 'private_key' => $expectedPrivateKey, - ]); - - // Not "fillable" - $project->hash = $expectedHash; - $project->public_key = $expectedPublicKey; - - $this->assertSame($expectedHash, $project->hash); - $this->assertSame($expectedPrivateKey, $project->private_key); - $this->assertSame($expectedPublicKey, $project->public_key); - - $project->save(); - - $this->assertSame($expectedHash, $project->hash); - $this->assertSame($expectedPrivateKey, $project->private_key); - $this->assertSame($expectedPublicKey, $project->public_key); - } - - /** - * @covers ::boot - */ - public function testBootBindsSavingEventToGenerateKeypair() - { - $expectedPrivateKey = 'a-private-key'; - $expectedPublicKey = 'a-private-key'; - - /** @var Project $project */ - $project = factory(Project::class)->make([ - 'hash' => 'a-fake-hash', - ]); - - $project->private_key = ''; - $project->public_key = ''; - - /** @var Process $process */ - $process = m::mock(Process::class); - $process->shouldReceive('setScript->run'); - $process->shouldReceive('isSuccessful')->andReturn(true); - - /** @var Filesystem $filesystem */ - $filesystem = m::mock(Filesystem::class); - $filesystem->shouldReceive('tempnam')->andReturn('a-key-file'); - $filesystem->shouldReceive('get')->with('a-key-file')->andReturn($expectedPrivateKey); - $filesystem->shouldReceive('get')->with('a-key-file.pub')->andReturn($expectedPublicKey); - $filesystem->shouldReceive('delete'); - - // Override the dependencies from the job so that it doesn't actually run a process - $this->app->instance(Process::class, $process); - $this->app->instance(Filesystem::class, $filesystem); - - $this->assertEmpty($project->private_key); - $this->assertEmpty($project->public_key); - - $project->save(); - - $this->assertSame($expectedPrivateKey, $project->private_key); - $this->assertSame($expectedPublicKey, $project->public_key); - } - - /** - * @covers ::boot - */ - public function testBootBindsSavingEventToRegeneratePublicKeyWhenPrivateKeyProvided() - { - $expectedPrivateKey = 'a-private-key'; - $expectedPublicKey = 'a-public-key'; - - /** @var Project $project */ - $project = factory(Project::class)->make([ - 'hash' => 'a-fake-hash', - 'private_key' => $expectedPrivateKey, - 'public_key' => '', - ]); - - $this->assertSame($expectedPrivateKey, $project->private_key); - $this->assertEmpty($project->public_key); - - /** @var Process $process */ - $process = m::mock(Process::class); - $process->shouldReceive('setScript->run'); - $process->shouldReceive('isSuccessful')->andReturn(true); - - /** @var Filesystem $filesystem */ - $filesystem = m::mock(Filesystem::class); - $filesystem->shouldReceive('tempnam')->andReturn('a-key-file'); - $filesystem->shouldReceive('put')->with('a-key-file', $expectedPrivateKey); - $filesystem->shouldReceive('chmod'); - $filesystem->shouldReceive('get')->with('a-key-file.pub')->andReturn($expectedPublicKey); - $filesystem->shouldReceive('delete'); - - // Override the dependencies from the job so that it doesn't actually run a process - $this->app->instance(Process::class, $process); - $this->app->instance(Filesystem::class, $filesystem); - - $project->save(); - - $this->assertSame($expectedPrivateKey, $project->private_key); - $this->assertSame($expectedPublicKey, $project->public_key); - } - private function assertStatusArray($healthy, $missing, $actual) { $this->assertInternalType('array', $actual); diff --git a/tests/Unit/Database/ServerLogTest.php b/tests/Unit/Database/ServerLogTest.php index 58f5155ae..e014c34d2 100644 --- a/tests/Unit/Database/ServerLogTest.php +++ b/tests/Unit/Database/ServerLogTest.php @@ -4,8 +4,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Foundation\Testing\DatabaseMigrations; -use REBELinBLUE\Deployer\Events\ServerLogChanged; -use REBELinBLUE\Deployer\Events\ServerOutputChanged; use REBELinBLUE\Deployer\Server; use REBELinBLUE\Deployer\ServerLog; use REBELinBLUE\Deployer\Tests\TestCase; @@ -47,51 +45,4 @@ public function testServerIsCorrectRelationship() $this->assertInstanceOf(BelongsTo::class, $actual); $this->assertSame('server', $actual->getRelation()); } - - /** - * @covers ::boot - */ - public function testBoot() - { - $this->doesntExpectEvents(ServerLogChanged::class); - $this->doesntExpectEvents(ServerOutputChanged::class); - - factory(ServerLog::class)->create(); - } - - /** - * @covers ::boot - */ - public function testBootDoesNotFireOutputChangedEventWhenOutputNotChanged() - { - $this->expectsEvents(ServerLogChanged::class); - $this->doesntExpectEvents(ServerOutputChanged::class); - - /** @var ServerLog $log */ - $log = factory(ServerLog::class)->create([ - 'status' => ServerLog::RUNNING, - 'output' => 'lorem ipsum', - ]); - - $log->status = ServerLog::COMPLETED; - $log->save(); - } - - /** - * @covers ::boot - */ - public function testBootFiresOutputChangedEventWhenOutputChanged() - { - $this->expectsEvents(ServerLogChanged::class); - $this->expectsEvents(ServerOutputChanged::class); - - /** @var ServerLog $log */ - $log = factory(ServerLog::class)->create([ - 'status' => ServerLog::RUNNING, - ]); - - $log->status = ServerLog::COMPLETED; - $log->output = 'lorem ipsum'; - $log->save(); - } } diff --git a/tests/Unit/Listeners/ClearJwtTest.php b/tests/Unit/Events/Listeners/ClearJwtTest.php similarity index 70% rename from tests/Unit/Listeners/ClearJwtTest.php rename to tests/Unit/Events/Listeners/ClearJwtTest.php index c3cb4198e..f77111cff 100644 --- a/tests/Unit/Listeners/ClearJwtTest.php +++ b/tests/Unit/Events/Listeners/ClearJwtTest.php @@ -1,14 +1,14 @@ handle(new $event($url)); Notification::assertSentTo($channel, $notification); @@ -58,6 +59,6 @@ public function testHandleSendsNotification( public function provideTestNotificationData() { - return $this->fixture('Listeners/SendCheckUrlNotification')['notifications']; + return $this->fixture('Listeners/SendCheckUrlNotifications')['notifications']; } } diff --git a/tests/Unit/Listeners/SendDeploymentNotificationsTest.php b/tests/Unit/Events/Listeners/SendDeploymentNotificationsTest.php similarity index 92% rename from tests/Unit/Listeners/SendDeploymentNotificationsTest.php rename to tests/Unit/Events/Listeners/SendDeploymentNotificationsTest.php index d00d20141..df5defe5e 100644 --- a/tests/Unit/Listeners/SendDeploymentNotificationsTest.php +++ b/tests/Unit/Events/Listeners/SendDeploymentNotificationsTest.php @@ -1,6 +1,6 @@ fixture('Listeners/SendDeploymentNotifications')['notification']; + return $this->fixture('Listeners/SendDeploymentNotifications')['notifications']; } public function testHandleDoesNotSendNotificationWhenAborted() diff --git a/tests/Unit/Listeners/SendEmailChangeConfirmationTest.php b/tests/Unit/Events/Listeners/SendEmailChangeConfirmationTest.php similarity index 82% rename from tests/Unit/Listeners/SendEmailChangeConfirmationTest.php rename to tests/Unit/Events/Listeners/SendEmailChangeConfirmationTest.php index 917063738..69c004036 100644 --- a/tests/Unit/Listeners/SendEmailChangeConfirmationTest.php +++ b/tests/Unit/Events/Listeners/SendEmailChangeConfirmationTest.php @@ -1,18 +1,18 @@ handle(new $event($heartbeat)); Notification::assertSentTo($channel, $notification); @@ -58,6 +59,6 @@ public function testHandleSendsNotification( public function provideTestNotificationData() { - return $this->fixture('Listeners/SendSignupEmail')['notifications']; + return $this->fixture('Listeners/SendHeartbeatNotifications')['notifications']; } } diff --git a/tests/Unit/Listeners/SendSignupEmailTest.php b/tests/Unit/Events/Listeners/SendSignupEmailTest.php similarity index 80% rename from tests/Unit/Listeners/SendSignupEmailTest.php rename to tests/Unit/Events/Listeners/SendSignupEmailTest.php index 047d4e005..bb6e9c186 100644 --- a/tests/Unit/Listeners/SendSignupEmailTest.php +++ b/tests/Unit/Events/Listeners/SendSignupEmailTest.php @@ -1,18 +1,18 @@ shouldReceive('notify')->with(m::type(NewTestNotification::class)); + + $observer = new ChannelObserver($translator); + $observer->saved($channel); + } +} diff --git a/tests/Unit/Events/Observers/CheckUrlObserverTest.php b/tests/Unit/Events/Observers/CheckUrlObserverTest.php new file mode 100644 index 000000000..3675f3268 --- /dev/null +++ b/tests/Unit/Events/Observers/CheckUrlObserverTest.php @@ -0,0 +1,141 @@ +withoutEvents(); + + $this->dispatcher = $this->app->make('events'); + } + + /** + * @covers ::__construct + * @covers ::saved + */ + public function testSaved() + { + $this->expectsJobs(RequestProjectCheckUrl::class); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::UNTESTED, + 'project_id' => 1, + ]); + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->saved($url); + } + + /** + * @covers ::__construct + * @covers ::saved + */ + public function testSavedDoesNotDispatchJobIfTested() + { + $this->doesntExpectJobs(RequestProjectCheckUrl::class); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::ONLINE, + 'project_id' => 1, + ]); + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->saved($url); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedDispatchesEventWhenOffline() + { + $this->expectsEvents(UrlDown::class); + $this->doesntExpectEvents(UrlUp::class); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::OFFLINE, + 'project_id' => 1, + ]); + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->updated($url); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedDoesNotDispatchEventWhenAlreadyOnline() + { + $this->doesntExpectEvents([UrlUp::class, UrlDown::class]); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::ONLINE, + 'project_id' => 1, + ])->syncOriginal(); + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->updated($url); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedDoesNotDispatchEventWhenPreviouslyUntested() + { + $this->doesntExpectEvents([UrlUp::class, UrlDown::class]); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::UNTESTED, + 'project_id' => 1, + ])->syncOriginal(); + + $url->status = CheckUrl::ONLINE; + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->updated($url); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedDispatchesEventWhenOnline() + { + $this->expectsEvents(UrlUp::class); + $this->doesntExpectEvents(UrlDown::class); + + /** @var CheckUrl $url */ + $url = factory(CheckUrl::class)->make([ + 'status' => CheckUrl::OFFLINE, + 'project_id' => 1, + ])->syncOriginal(); + + $url->status = CheckUrl::ONLINE; + + $observer = new CheckUrlObserver($this->dispatcher); + $observer->updated($url); + } +} diff --git a/tests/Unit/Events/Observers/HeartbeatObserverTest.php b/tests/Unit/Events/Observers/HeartbeatObserverTest.php new file mode 100644 index 000000000..5c4129d5b --- /dev/null +++ b/tests/Unit/Events/Observers/HeartbeatObserverTest.php @@ -0,0 +1,63 @@ +mockTokenGenerator($expected); + + /** @var Heartbeat $heartbeat */ + $heartbeat = factory(Heartbeat::class)->make([ + 'project_id' => 1, + ]); + + $this->assertEmpty($heartbeat->hash); + + $observer = new HeartbeatObserver(); + $observer->creating($heartbeat); + + $this->assertSame($expected, $heartbeat->hash); + } + + /** + * @covers ::creating + */ + public function testBootShouldNotRegenerateHashIfSet() + { + $expected = 'my-fake-token'; + + $generator = m::mock(TokenGeneratorInterface::class); + $generator->shouldNotReceive('generateRandom'); + + $this->app->instance(TokenGeneratorInterface::class, $generator); + + /** @var Heartbeat $heartbeat */ + $heartbeat = factory(Heartbeat::class)->make([ + 'project_id' => 1, + 'hash' => $expected, + ]); + + $this->assertSame($expected, $heartbeat->hash); + + $observer = new HeartbeatObserver(); + $observer->creating($heartbeat); + + $this->assertSame($expected, $heartbeat->hash); + } +} diff --git a/tests/Unit/Events/Observers/ProjectObserverTest.php b/tests/Unit/Events/Observers/ProjectObserverTest.php new file mode 100644 index 000000000..38494b624 --- /dev/null +++ b/tests/Unit/Events/Observers/ProjectObserverTest.php @@ -0,0 +1,178 @@ +make([ + 'group_id' => 1, + 'hash' => 'a-fake-hash', + 'private_key' => '', + 'public_key' => '', + ]); + + $this->expectsJobs(GenerateKey::class); + $this->doesntExpectJobs(RegeneratePublicKey::class); + + $observer = new ProjectObserver(); + $observer->creating($project); + } + + /** + * @covers ::creating + */ + public function testCreatingRegeneratesPublicKeyWhenPrivateKeyProvided() + { + $expectedPrivateKey = 'a-private-key'; + + /** @var Project $project */ + $project = factory(Project::class)->make([ + 'group_id' => 1, + 'hash' => 'a-fake-hash', + 'private_key' => $expectedPrivateKey, + 'public_key' => '', + ]); + + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertEmpty($project->public_key); + + $this->expectsJobs(RegeneratePublicKey::class); + $this->doesntExpectJobs(GenerateKey::class); + + $observer = new ProjectObserver(); + $observer->creating($project); + + $this->assertSame($expectedPrivateKey, $project->private_key); + } + + /** + * @covers ::creating + */ + public function testCreatingGeneratesHash() + { + $expected = 'my-fake-token'; + + $this->mockTokenGenerator($expected); + + /** @var Project $project */ + $project = factory(Project::class)->make([ + 'group_id' => 1, + 'private_key' => 'a-private-key', + 'public_key' => 'a-public-key', + ]); + + $this->assertEmpty($project->hash); + + $observer = new ProjectObserver(); + $observer->creating($project); + + $this->assertSame($expected, $project->hash); + } + + /** + * @covers ::creating + */ + public function testCreatingShouldNotRegenerateFieldsIfSet() + { + $expectedHash = 'my-fake-token'; + $expectedPrivateKey = 'a-private-key'; + $expectedPublicKey = 'a-public-key'; + + /** @var TokenGeneratorInterface $generator */ + $generator = m::mock(TokenGeneratorInterface::class); + $generator->shouldNotReceive('generateRandom'); + + $this->app->instance(TokenGeneratorInterface::class, $generator); + + /** @var Project $project */ + $project = factory(Project::class)->make([ + 'group_id' => 1, + 'private_key' => $expectedPrivateKey, + 'hash' => $expectedHash, + 'public_key' => $expectedPublicKey, + ]); + + $this->assertSame($expectedHash, $project->hash); + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertSame($expectedPublicKey, $project->public_key); + + $this->doesntExpectJobs([GenerateKey::class, RegeneratePublicKey::class]); + + $observer = new ProjectObserver(); + $observer->creating($project); + + $this->assertSame($expectedHash, $project->hash); + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertSame($expectedPublicKey, $project->public_key); + } + + /** + * @covers ::updating + */ + public function testUpdatingRegeneratesPublicKeyWhenPrivateKeyProvided() + { + $expectedPrivateKey = 'a-private-key'; + + /** @var Project $project */ + $project = factory(Project::class)->make([ + 'group_id' => 1, + 'hash' => 'a-fake-hash', + 'private_key' => $expectedPrivateKey, + 'public_key' => '', + ]); + + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertEmpty($project->public_key); + + $this->expectsJobs(RegeneratePublicKey::class); + + $observer = new ProjectObserver(); + $observer->updating($project); + + $this->assertSame($expectedPrivateKey, $project->private_key); + } + + /** + * @covers ::updating + */ + public function testUpdatingShouldNotRegeneratePublicKeyIfSet() + { + $expectedPrivateKey = 'a-private-key'; + $expectedPublicKey = 'a-public-key'; + + /** @var Project $project */ + $project = factory(Project::class)->make([ + 'group_id' => 1, + 'hash' => 'a-fake-hash', + 'private_key' => $expectedPrivateKey, + 'public_key' => $expectedPublicKey, + ]); + + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertSame($expectedPublicKey, $project->public_key); + + $this->doesntExpectJobs(RegeneratePublicKey::class); + + $observer = new ProjectObserver(); + $observer->updating($project); + + $this->assertSame($expectedPrivateKey, $project->private_key); + $this->assertSame($expectedPublicKey, $project->public_key); + } +} diff --git a/tests/Unit/Events/Observers/ServerLogObserverTest.php b/tests/Unit/Events/Observers/ServerLogObserverTest.php new file mode 100644 index 000000000..88cb857c2 --- /dev/null +++ b/tests/Unit/Events/Observers/ServerLogObserverTest.php @@ -0,0 +1,70 @@ +withoutEvents(); + + $this->dispatcher = $this->app->make('events'); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedNotFireOutputChangedEventWhenOutputNotChanged() + { + /** @var ServerLog $log */ + $log = factory(ServerLog::class)->make([ + 'server_id' => 1, + 'deploy_step_id' => 1, + 'status' => ServerLog::RUNNING, + 'output' => 'Lorem ipsum', + ])->syncOriginal(); + + $this->expectsEvents(ServerLogChanged::class); + $this->doesntExpectEvents(ServerOutputChanged::class); + + $observer = new ServerLogObserver($this->dispatcher); + $observer->updated($log); + } + + /** + * @covers ::__construct + * @covers ::updated + */ + public function testUpdatedFiresOutputChangedEventWhenOutputChanged() + { + /** @var ServerLog $log */ + $log = factory(ServerLog::class)->make([ + 'server_id' => 1, + 'deploy_step_id' => 1, + 'status' => ServerLog::RUNNING, + 'output' => 'Lorem ipsum', + ])->syncOriginal(); + + $this->expectsEvents([ServerLogChanged::class, ServerOutputChanged::class]); + + $log->status = ServerLog::COMPLETED; + $log->output = 'lorem ipsum'; + + $observer = new ServerLogObserver($this->dispatcher); + $observer->updated($log); + } +} diff --git a/tests/Unit/HeartbeatTest.php b/tests/Unit/HeartbeatTest.php index 933cd20a1..842b2e98e 100644 --- a/tests/Unit/HeartbeatTest.php +++ b/tests/Unit/HeartbeatTest.php @@ -4,7 +4,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Routing\UrlGenerator; -use Illuminate\Support\Facades\App; use Mockery as m; use REBELinBLUE\Deployer\Heartbeat; use REBELinBLUE\Deployer\Tests\TestCase; @@ -78,7 +77,7 @@ public function testGetCallbackUrlAttribute() ->with('heartbeats', $hash, true) ->andReturn($expected); - App::instance('url', $mock); + $this->app->instance('url', $mock); $this->mockTokenGenerator($hash); diff --git a/tests/Unit/Jobs/RequestProjectCheckUrlTest.php b/tests/Unit/Jobs/RequestProjectCheckUrlTest.php index 4d99ff2f3..0b0152087 100644 --- a/tests/Unit/Jobs/RequestProjectCheckUrlTest.php +++ b/tests/Unit/Jobs/RequestProjectCheckUrlTest.php @@ -29,6 +29,7 @@ public function testHandlesOnline() $url = $this->mockCheckUrl($expected); $url->shouldReceive('online')->once(); $url->shouldNotReceive('offline'); + $url->shouldReceive('save'); $links = new Collection(); $links->push($url); @@ -51,6 +52,7 @@ public function testHandlesOffline() $url = $this->mockCheckUrl($expected); $url->shouldReceive('offline')->once(); $url->shouldNotReceive('online'); + $url->shouldReceive('save'); $links = new Collection(); $links->push($url); @@ -75,14 +77,14 @@ public function testHandlesMultiple() $url1 = $this->mockCheckUrl($expected1); $url1->shouldReceive('online')->once(); $url1->shouldNotReceive('offline'); + $url1->shouldReceive('save'); $url2 = $this->mockCheckUrl($expected2); $url2->shouldReceive('offline')->once(); $url2->shouldNotReceive('online'); + $url2->shouldReceive('save'); - $links = new Collection(); - $links->push($url1); - $links->push($url2); + $links = new Collection([$url1, $url2]); $job = new RequestProjectCheckUrl($links); $job->handle($client); diff --git a/tests/Unit/Repositories/EloquentUserRepositoryTest.php b/tests/Unit/Repositories/EloquentUserRepositoryTest.php index e07fa3748..182814d8d 100644 --- a/tests/Unit/Repositories/EloquentUserRepositoryTest.php +++ b/tests/Unit/Repositories/EloquentUserRepositoryTest.php @@ -3,7 +3,6 @@ namespace REBELinBLUE\Deployer\Tests\Unit\Repositories; use Illuminate\Contracts\Hashing\Hasher; -use Illuminate\Support\Facades\App; use Mockery as m; use REBELinBLUE\Deployer\Repositories\Contracts\UserRepositoryInterface; use REBELinBLUE\Deployer\Repositories\EloquentUserRepository; @@ -48,7 +47,7 @@ public function testCreate() // Replace the hasher so that we can ensure the password is encrypted but that a known value is returned $mock = m::mock(Hasher::class); $mock->shouldReceive('make')->andReturn($expectedPassword); - App::instance('hash', $mock); + $this->app->instance('hash', $mock); $model = m::mock(User::class); $model->shouldReceive('create')->once()->with($create)->andReturn($expected); @@ -112,7 +111,7 @@ public function testUpdateByIdEncryptsPassword() $mock = m::mock(Hasher::class); $mock->shouldReceive('make')->andReturn($expectedPassword); - App::instance('hash', $mock); + $this->app->instance('hash', $mock); $expected = m::mock(User::class); $expected->shouldReceive('update')->once()->with($update);