- Add‑on Checkout auf Transactions + Transaction‑ID speichern: app/Services/Addons/EventAddonCheckoutService.php
- Paket/Marketing Checkout auf Transactions: app/Services/Paddle/PaddleCheckoutService.php
- Gift‑Voucher Checkout: Customer anlegen/finden + Transactions: app/Services/GiftVouchers/
GiftVoucherCheckoutService.php
- Tests aktualisiert: tests/Feature/Tenant/EventAddonCheckoutTest.php, tests/Unit/PaddleCheckoutServiceTest.php,
tests/Unit/GiftVoucherCheckoutServiceTest.php
215 lines
5.2 KiB
PHP
215 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use Illuminate\Console\Scheduling\Schedule;
|
|
use Illuminate\Queue\Events\JobFailed;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
use Tests\TestCase;
|
|
|
|
class SentryReportingTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config(['sentry.dsn' => 'https://example@sentry.test/1']);
|
|
}
|
|
|
|
public function test_reports_server_errors_to_sentry(): void
|
|
{
|
|
$fake = new class
|
|
{
|
|
public int $captured = 0;
|
|
|
|
public function captureException(mixed $exception = null): void
|
|
{
|
|
$this->captured++;
|
|
}
|
|
};
|
|
|
|
$this->app->instance('sentry', $fake);
|
|
|
|
$handler = $this->app->make(\App\Exceptions\Handler::class);
|
|
|
|
$handler->report(new \RuntimeException('boom'));
|
|
|
|
$this->assertSame(1, $fake->captured, 'Sentry should receive server errors');
|
|
}
|
|
|
|
public function test_ignores_client_errors_for_sentry(): void
|
|
{
|
|
$fake = new class
|
|
{
|
|
public int $captured = 0;
|
|
|
|
public function captureException(mixed $exception = null): void
|
|
{
|
|
$this->captured++;
|
|
}
|
|
};
|
|
|
|
$this->app->instance('sentry', $fake);
|
|
|
|
$handler = $this->app->make(\App\Exceptions\Handler::class);
|
|
|
|
$handler->report(new HttpException(404));
|
|
|
|
$this->assertSame(0, $fake->captured, 'Sentry should ignore 4xx errors');
|
|
}
|
|
|
|
public function test_reports_scheduled_task_failures_to_sentry(): void
|
|
{
|
|
$fake = new class
|
|
{
|
|
public int $captured = 0;
|
|
|
|
public function captureException(mixed $exception = null): void
|
|
{
|
|
$this->captured++;
|
|
}
|
|
};
|
|
|
|
$this->app->instance('sentry', $fake);
|
|
|
|
Artisan::call('schedule:list', [
|
|
'--json' => true,
|
|
'--no-interaction' => true,
|
|
]);
|
|
|
|
$schedule = $this->app->make(Schedule::class);
|
|
$task = collect($schedule->events())
|
|
->first(fn ($event) => str_contains((string) $event->command, 'storage:monitor'));
|
|
|
|
$this->assertNotNull($task, 'Expected scheduled storage:monitor task');
|
|
|
|
$task->finish($this->app, 1);
|
|
|
|
$this->assertSame(1, $fake->captured, 'Sentry should receive scheduled task failures');
|
|
}
|
|
|
|
public function test_reports_queue_failures_to_sentry(): void
|
|
{
|
|
$fake = new class
|
|
{
|
|
public int $captured = 0;
|
|
|
|
public function captureException(mixed $exception = null): void
|
|
{
|
|
$this->captured++;
|
|
}
|
|
};
|
|
|
|
$this->app->instance('sentry', $fake);
|
|
|
|
$job = new class implements \Illuminate\Contracts\Queue\Job
|
|
{
|
|
public function uuid(): ?string
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function getJobId(): string
|
|
{
|
|
return 'job-id';
|
|
}
|
|
|
|
public function payload(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function fire(): void {}
|
|
|
|
public function release($delay = 0): void {}
|
|
|
|
public function isReleased(): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function delete(): void {}
|
|
|
|
public function isDeleted(): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function isDeletedOrReleased(): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function attempts(): int
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
public function hasFailed(): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function markAsFailed(): void {}
|
|
|
|
public function fail($e = null): void {}
|
|
|
|
public function maxTries(): ?int
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function maxExceptions(): ?int
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function timeout(): ?int
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function retryUntil(): ?int
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return 'FakeJob';
|
|
}
|
|
|
|
public function resolveName(): string
|
|
{
|
|
return 'FakeJob';
|
|
}
|
|
|
|
public function resolveQueuedJobClass(): string
|
|
{
|
|
return 'FakeJob';
|
|
}
|
|
|
|
public function getConnectionName(): string
|
|
{
|
|
return 'redis';
|
|
}
|
|
|
|
public function getQueue(): string
|
|
{
|
|
return 'default';
|
|
}
|
|
|
|
public function getRawBody(): string
|
|
{
|
|
return '{}';
|
|
}
|
|
};
|
|
|
|
Event::dispatch(new JobFailed('redis', $job, new \RuntimeException('Queue failure')));
|
|
|
|
$this->assertSame(1, $fake->captured, 'Sentry should receive queue failures');
|
|
}
|
|
}
|