Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for queued jobs in Laravel 9 #501

Merged
merged 2 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion features/fixtures/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
version: '3.4'
version: '3.8'

services:
laravel51:
build:
Expand Down Expand Up @@ -96,6 +97,7 @@ services:
published: 61280

laravel9:
init: true
build:
context: laravel9
args:
Expand Down
9 changes: 2 additions & 7 deletions features/fixtures/laravel9/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@ LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
DB_CONNECTION=sqlite

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
QUEUE_CONNECTION=database
SESSION_DRIVER=file
SESSION_LIFETIME=120

Expand Down
6 changes: 5 additions & 1 deletion features/fixtures/laravel9/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ RUN cp .env.example .env
RUN composer install
RUN php artisan key:generate

CMD php -S 0.0.0.0:8000 -t public
# apply database migrations
# --force is required to create the database
RUN php artisan migrate --force --no-interaction

CMD ./run-fixture
27 changes: 27 additions & 0 deletions features/fixtures/laravel9/app/Jobs/HandledJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Jobs;

use Bugsnag\BugsnagLaravel\Facades\Bugsnag;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class HandledJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Bugsnag::notifyException(new Exception('Handled :)'));
}
}
33 changes: 33 additions & 0 deletions features/fixtures/laravel9/app/Jobs/UnhandledJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use RuntimeException;

class UnhandledJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public int $tries;

public function __construct(int $tries)
{
$this->tries = $tries;
}

/**
* Execute the job.
*
* @return void
*/
public function handle()
{
throw new RuntimeException('uh oh :o');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
};
9 changes: 9 additions & 0 deletions features/fixtures/laravel9/routes/web.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
Expand Down Expand Up @@ -52,6 +53,14 @@
Route::view('/handled_view_exception', 'handledexception');
Route::view('/handled_view_error', 'handlederror');

Route::get('/queue/unhandled', function (Request $request) {
\App\Jobs\UnhandledJob::dispatch((int) $request->query('tries', '1'));
});

Route::get('/queue/handled', function (Request $request) {
\App\Jobs\HandledJob::dispatch((int) $request->query('tries', '1'));
});

/**
* Return some diagnostics if an OOM did not happen when it should have.
*
Expand Down
10 changes: 10 additions & 0 deletions features/fixtures/laravel9/run-fixture
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

# run the PHP webserver
php -S 0.0.0.0:8000 -t public &

# wait for any process to exit
wait -n

# exit with the status of the first process to exit
exit $?
99 changes: 99 additions & 0 deletions features/queues.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
Feature: Queue support

@not-laravel-latest @not-laravel51 @not-laravel56 @not-laravel58 @not-laravel66 @not-laravel8 @not-lumen8
imjoehaines marked this conversation as resolved.
Show resolved Hide resolved
Scenario: Unhandled exceptions are delivered from queues
Given I start the laravel fixture
And I start the laravel queue worker
When I navigate to the route "/queue/unhandled"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Bugsnag Laravel" notifier
And the exception "errorClass" equals "RuntimeException"
And the exception "message" equals "uh oh :o"
And the event "metaData.job.name" equals "Illuminate\Queue\CallQueuedHandler@call"
And the event "metaData.job.queue" equals "default"
And the event "metaData.job.attempts" equals 1
And the event "metaData.job.connection" equals "database"
And the event "metaData.job.resolved" equals "App\Jobs\UnhandledJob"
And the event "app.type" equals "Queue"
And the event "context" equals "App\Jobs\UnhandledJob"
And the event "severity" equals "error"
And the event "unhandled" is true
And the event "severityReason.type" equals "unhandledExceptionMiddleware"
And the event "severityReason.attributes.framework" equals "Laravel"

@not-laravel-latest @not-laravel51 @not-laravel56 @not-laravel58 @not-laravel66 @not-laravel8 @not-lumen8
Scenario: Unhandled exceptions are delivered from queued jobs with multiple attmpts
Given I start the laravel fixture
And I start the laravel queue worker
When I navigate to the route "/queue/unhandled?tries=3"
And I wait to receive 3 errors

# attempt 1
Then the error is valid for the error reporting API version "4.0" for the "Bugsnag Laravel" notifier
And the exception "errorClass" equals "RuntimeException"
And the exception "message" equals "uh oh :o"
And the event "metaData.job.name" equals "Illuminate\Queue\CallQueuedHandler@call"
And the event "metaData.job.queue" equals "default"
And the event "metaData.job.attempts" equals 1
And the event "metaData.job.connection" equals "database"
And the event "metaData.job.resolved" equals "App\Jobs\UnhandledJob"
And the event "app.type" equals "Queue"
And the event "context" equals "App\Jobs\UnhandledJob"
And the event "severity" equals "error"
And the event "unhandled" is true
And the event "severityReason.type" equals "unhandledExceptionMiddleware"
And the event "severityReason.attributes.framework" equals "Laravel"

# attempt 2
When I discard the oldest error
Then the error is valid for the error reporting API version "4.0" for the "Bugsnag Laravel" notifier
And the exception "errorClass" equals "RuntimeException"
And the exception "message" equals "uh oh :o"
And the event "metaData.job.name" equals "Illuminate\Queue\CallQueuedHandler@call"
And the event "metaData.job.queue" equals "default"
And the event "metaData.job.attempts" equals 2
And the event "metaData.job.connection" equals "database"
And the event "metaData.job.resolved" equals "App\Jobs\UnhandledJob"
And the event "app.type" equals "Queue"
And the event "context" equals "App\Jobs\UnhandledJob"
And the event "severity" equals "error"
And the event "unhandled" is true
And the event "severityReason.type" equals "unhandledExceptionMiddleware"
And the event "severityReason.attributes.framework" equals "Laravel"

# attempt 3
When I discard the oldest error
Then the error is valid for the error reporting API version "4.0" for the "Bugsnag Laravel" notifier
And the exception "errorClass" equals "RuntimeException"
And the exception "message" equals "uh oh :o"
And the event "metaData.job.name" equals "Illuminate\Queue\CallQueuedHandler@call"
And the event "metaData.job.queue" equals "default"
And the event "metaData.job.attempts" equals 3
And the event "metaData.job.connection" equals "database"
And the event "metaData.job.resolved" equals "App\Jobs\UnhandledJob"
And the event "app.type" equals "Queue"
And the event "context" equals "App\Jobs\UnhandledJob"
And the event "severity" equals "error"
And the event "unhandled" is true
And the event "severityReason.type" equals "unhandledExceptionMiddleware"
And the event "severityReason.attributes.framework" equals "Laravel"

@not-laravel-latest @not-laravel51 @not-laravel56 @not-laravel58 @not-laravel66 @not-laravel8 @not-lumen8
Scenario: Handled exceptions are delivered from queues
Given I start the laravel fixture
And I start the laravel queue worker
When I navigate to the route "/queue/handled"
And I wait to receive an error
Then the error is valid for the error reporting API version "4.0" for the "Bugsnag Laravel" notifier
And the exception "errorClass" equals "Exception"
And the exception "message" equals "Handled :)"
And the event "metaData.job.name" equals "Illuminate\Queue\CallQueuedHandler@call"
And the event "metaData.job.queue" equals "default"
And the event "metaData.job.attempts" equals 1
And the event "metaData.job.connection" equals "database"
And the event "metaData.job.resolved" equals "App\Jobs\HandledJob"
And the event "app.type" equals "Queue"
And the event "context" equals "App\Jobs\HandledJob"
And the event "severity" equals "warning"
And the event "unhandled" is false
And the event "severityReason.type" equals "handledException"
18 changes: 18 additions & 0 deletions features/steps/laravel_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@
}
end

# TODO: contribute this back to Maze Runner
# https://github.com/bugsnag/maze-runner/pull/425
module Maze
class Docker
class << self
def exec(service, command, detach: false)
flags = detach ? "--detach" : ""

run_docker_compose_command("exec #{flags} #{service} #{command}")
end
end
end
end

When("I start the laravel queue worker") do
Maze::Docker.exec(Laravel.fixture, "php artisan queue:work", detach: true)
end

When("I navigate to the route {string}") do |route|
Laravel.navigate_to(route)
end
Expand Down
9 changes: 9 additions & 0 deletions features/support/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,12 @@
Before("@requires-sessions") do
skip_this_scenario unless Laravel.supports_sessions?
end

# add a '@not-X' tag for each fixture
fixtures = Dir.each_child(File.realpath("#{PROJECT_ROOT}/features/fixtures")) do |name|
next unless name.match?(/^(laravel|lumen)/)

Before("@not-#{name}") do
skip_this_scenario if Laravel.fixture == name
end
end