Skip to content

Commit ddfeec6

Browse files
authored
Remove usage of Str::replace (#762)
1 parent f8685aa commit ddfeec6

File tree

6 files changed

+177
-11
lines changed

6 files changed

+177
-11
lines changed

src/Sentry/Laravel/Features/ConsoleIntegration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private function buildCacheKey(string $mutex, string $slug): string
174174
private function makeSlugForScheduled(SchedulingEvent $scheduled): string
175175
{
176176
$generatedSlug = Str::slug(
177-
Str::replace(
177+
str_replace(
178178
// `:` is commonly used in the command name, so we replace it with `-` to avoid it being stripped out by the slug function
179179
':',
180180
'-',
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Sentry\Features;
4+
5+
use Illuminate\Console\Scheduling\Schedule;
6+
use RuntimeException;
7+
use Sentry\Laravel\Tests\TestCase;
8+
use Illuminate\Console\Scheduling\Event;
9+
10+
class ConsoleIntegrationTest extends TestCase
11+
{
12+
public function testScheduleMacro(): void
13+
{
14+
/** @var Event $scheduledEvent */
15+
$scheduledEvent = $this->getScheduler()->call(function () {})->sentryMonitor('test-monitor');
16+
17+
$scheduledEvent->run($this->app);
18+
19+
// We expect a total of 2 events to be sent to Sentry:
20+
// 1. The start check-in event
21+
// 2. The finish check-in event
22+
$this->assertEquals(2, $this->getEventsCount());
23+
24+
$finishCheckInEvent = $this->getLastEvent();
25+
26+
$this->assertNotNull($finishCheckInEvent->getCheckIn());
27+
$this->assertEquals('test-monitor', $finishCheckInEvent->getCheckIn()->getMonitorSlug());
28+
}
29+
30+
public function testScheduleMacroAutomaticSlug(): void
31+
{
32+
/** @var Event $scheduledEvent */
33+
$scheduledEvent = $this->getScheduler()->command('inspire')->sentryMonitor();
34+
35+
$scheduledEvent->run($this->app);
36+
37+
// We expect a total of 2 events to be sent to Sentry:
38+
// 1. The start check-in event
39+
// 2. The finish check-in event
40+
$this->assertEquals(2, $this->getEventsCount());
41+
42+
$finishCheckInEvent = $this->getLastEvent();
43+
44+
$this->assertNotNull($finishCheckInEvent->getCheckIn());
45+
$this->assertEquals('scheduled_artisan-inspire', $finishCheckInEvent->getCheckIn()->getMonitorSlug());
46+
}
47+
48+
public function testScheduleMacroWithoutSlugOrCommandName(): void
49+
{
50+
$this->expectException(RuntimeException::class);
51+
52+
$this->getScheduler()->call(function () {})->sentryMonitor();
53+
}
54+
55+
public function testScheduleMacroWithoutDsnSet(): void
56+
{
57+
$this->resetApplicationWithConfig([
58+
'sentry.dsn' => null,
59+
]);
60+
61+
/** @var Event $scheduledEvent */
62+
$scheduledEvent = $this->getScheduler()->call(function () {})->sentryMonitor('test-monitor');
63+
64+
$scheduledEvent->run($this->app);
65+
66+
$this->assertEquals(0, $this->getEventsCount());
67+
}
68+
69+
public function testScheduleMacroIsRegistered(): void
70+
{
71+
if (!method_exists(Event::class, 'flushMacros')) {
72+
$this->markTestSkipped('Macroable::flushMacros() is not available in this Laravel version.');
73+
}
74+
75+
Event::flushMacros();
76+
77+
$this->refreshApplication();
78+
79+
$this->assertTrue(Event::hasMacro('sentryMonitor'));
80+
}
81+
82+
public function testScheduleMacroIsRegisteredWithoutDsnSet(): void
83+
{
84+
if (!method_exists(Event::class, 'flushMacros')) {
85+
$this->markTestSkipped('Macroable::flushMacros() is not available in this Laravel version.');
86+
}
87+
88+
Event::flushMacros();
89+
90+
$this->resetApplicationWithConfig([
91+
'sentry.dsn' => null,
92+
]);
93+
94+
$this->assertTrue(Event::hasMacro('sentryMonitor'));
95+
}
96+
97+
private function getScheduler(): Schedule
98+
{
99+
return $this->app->make(Schedule::class);
100+
}
101+
}

test/Sentry/IntegrationTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ public function testExceptionReportedUsingReportHelperIsNotMarkedAsUnhandled():
153153

154154
report($testException);
155155

156-
$this->assertCount(1, $this->lastSentryEvents);
156+
$this->assertEquals(1, $this->getEventsCount());
157157

158-
[, $hint] = $this->lastSentryEvents[0];
158+
$hint = $this->getLastEventHint();
159159

160160
$this->assertEquals($testException, $hint->exception);
161161
$this->assertNotNull($hint->mechanism);
@@ -168,9 +168,9 @@ public function testExceptionIsNotMarkedAsUnhandled(): void
168168

169169
Integration::captureUnhandledException($testException);
170170

171-
$this->assertCount(1, $this->lastSentryEvents);
171+
$this->assertEquals(1, $this->getEventsCount());
172172

173-
[, $hint] = $this->lastSentryEvents[0];
173+
$hint = $this->getLastEventHint();
174174

175175
$this->assertEquals($testException, $hint->exception);
176176
$this->assertNotNull($hint->mechanism);

test/Sentry/ServiceProviderTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sentry\Laravel\Tests;
44

5+
use Illuminate\Support\Facades\Artisan;
56
use Orchestra\Testbench\TestCase;
67
use Sentry\Laravel\Facade;
78
use Sentry\Laravel\ServiceProvider;
@@ -68,4 +69,13 @@ public function testErrorTypesWasSetFromConfig(): void
6869
app('sentry')->getClient()->getOptions()->getErrorTypes()
6970
);
7071
}
72+
73+
/**
74+
* @depends testIsBound
75+
*/
76+
public function testArtisanCommandsAreRegistered(): void
77+
{
78+
$this->assertArrayHasKey('sentry:test', Artisan::all());
79+
$this->assertArrayHasKey('sentry:publish', Artisan::all());
80+
}
7181
}

test/Sentry/ServiceProviderWithoutDsnTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sentry\Laravel\Tests;
44

5+
use Illuminate\Support\Facades\Artisan;
56
use Sentry\Laravel\ServiceProvider;
67
use Illuminate\Routing\Events\RouteMatched;
78

@@ -39,4 +40,13 @@ public function testDidNotRegisterEvents(): void
3940
{
4041
$this->assertEquals(false, app('events')->hasListeners(RouteMatched::class));
4142
}
43+
44+
/**
45+
* @depends testIsBound
46+
*/
47+
public function testArtisanCommandsAreRegistered(): void
48+
{
49+
$this->assertArrayHasKey('sentry:test', Artisan::all());
50+
$this->assertArrayHasKey('sentry:publish', Artisan::all());
51+
}
4252
}

test/Sentry/TestCase.php

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Sentry\ClientInterface;
99
use Sentry\Event;
1010
use Sentry\EventHint;
11+
use Sentry\EventType;
1112
use Sentry\State\Scope;
1213
use ReflectionProperty;
1314
use Sentry\Laravel\Tracing;
@@ -17,20 +18,30 @@
1718

1819
abstract class TestCase extends LaravelTestCase
1920
{
21+
private static $hasSetupGlobalEventProcessor = false;
22+
2023
protected $setupConfig = [
2124
// Set config here before refreshing the app to set it in the container before Sentry is loaded
2225
// or use the `$this->resetApplicationWithConfig([ /* config */ ]);` helper method
2326
];
2427

2528
/** @var array<int, array{0: Event, 1: EventHint|null}> */
26-
protected $lastSentryEvents = [];
29+
protected static $lastSentryEvents = [];
2730

2831
protected function getEnvironmentSetUp($app): void
2932
{
30-
$this->lastSentryEvents = [];
33+
self::$lastSentryEvents = [];
34+
35+
$this->setupGlobalEventProcessor();
3136

32-
$app['config']->set('sentry.before_send', function (Event $event, ?EventHint $hint) {
33-
$this->lastSentryEvents[] = [$event, $hint];
37+
$app['config']->set('sentry.before_send', static function (Event $event, ?EventHint $hint) {
38+
self::$lastSentryEvents[] = [$event, $hint];
39+
40+
return null;
41+
});
42+
43+
$app['config']->set('sentry.before_send_transaction', static function (Event $event, ?EventHint $hint) {
44+
self::$lastSentryEvents[] = [$event, $hint];
3445

3546
return null;
3647
});
@@ -110,10 +121,44 @@ protected function getLastBreadcrumb(): ?Breadcrumb
110121

111122
protected function getLastEvent(): ?Event
112123
{
113-
if (empty($this->lastSentryEvents)) {
124+
if (empty(self::$lastSentryEvents)) {
114125
return null;
115126
}
116127

117-
return end($this->lastSentryEvents)[0];
128+
return end(self::$lastSentryEvents)[0];
129+
}
130+
131+
protected function getLastEventHint(): ?EventHint
132+
{
133+
if (empty(self::$lastSentryEvents)) {
134+
return null;
135+
}
136+
137+
return end(self::$lastSentryEvents)[1];
138+
}
139+
140+
protected function getEventsCount(): int
141+
{
142+
return count(self::$lastSentryEvents);
143+
}
144+
145+
private function setupGlobalEventProcessor(): void
146+
{
147+
if (self::$hasSetupGlobalEventProcessor) {
148+
return;
149+
}
150+
151+
Scope::addGlobalEventProcessor(static function (Event $event, ?EventHint $hint) {
152+
// Regular events and transactions are handled by the `before_send` and `before_send_transaction` callbacks
153+
if (in_array($event->getType(), [EventType::event(), EventType::transaction()], true)) {
154+
return $event;
155+
}
156+
157+
self::$lastSentryEvents[] = [$event, $hint];
158+
159+
return null;
160+
});
161+
162+
self::$hasSetupGlobalEventProcessor = true;
118163
}
119164
}

0 commit comments

Comments
 (0)