diff --git a/.gitignore b/.gitignore
index cdb0146..54f20ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ vendor/
.phpunit.result.cache
clover.xml
composer.lock
+laravel
phpunit.xml
psalm.xml
tests.*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e99758..f86f7d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,23 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+
+## [5.1.0] - 2025-03-21
+### Code Refactoring
+- **Channel:** Improve error handling and structure
+- **baselines:** Remove obsolete baseline error files
+- **channels:** Replace method calls with Utils functions
+- **collector:** Improve request header exclusion handling
+- **collectors:** Remove PhpInfoCollector and update ApplicationCollector
+- **collectors:** Improve exception trace filtering and naming
+
+### Style
+- Refactor code for improved readability and simplicity
+
+### Tests
+- Add inspections for null pointer and void function usage
+
+
## [5.0.0] - 2025-03-20
### Bug Fixes
@@ -1186,7 +1203,8 @@ All notable changes to this project will be documented in this file.
- Merge pull request [#1](https://github.com/guanguans/laravel-exception-notify/issues/1) from guanguans/imgbot
-[Unreleased]: https://github.com/guanguans/laravel-exception-notify/compare/5.0.0...HEAD
+[Unreleased]: https://github.com/guanguans/laravel-exception-notify/compare/5.1.0...HEAD
+[5.1.0]: https://github.com/guanguans/laravel-exception-notify/compare/5.0.0...5.1.0
[5.0.0]: https://github.com/guanguans/laravel-exception-notify/compare/5.0.0-rc1...5.0.0
[5.0.0-rc1]: https://github.com/guanguans/laravel-exception-notify/compare/5.0.0-beta2...5.0.0-rc1
[5.0.0-beta2]: https://github.com/guanguans/laravel-exception-notify/compare/5.0.0-beta1...5.0.0-beta2
diff --git a/README.md b/README.md
index 3275837..20e9677 100644
--- a/README.md
+++ b/README.md
@@ -59,9 +59,11 @@ php artisan exception-notify:test --ansi -v
### Notification examples
-| discord | lark | mail |
-|:----------------------------:|:----------------------:|:----------------------:|
-|  |  |  |
+| discord | slack | telegram |
+|:----------------------------:|:------------------------:|:------------------------------:|
+|  |  |  |
+| lark | mail | weWork |
+|  |  |  |
### Skip report
diff --git a/composer.json b/composer.json
index b96ae68..e00224f 100644
--- a/composer.json
+++ b/composer.json
@@ -102,7 +102,6 @@
"guanguans/ai-commit": "dev-main",
"guanguans/monorepo-builder-worker": "^1.4",
"laravel/facade-documenter": "dev-main",
- "maglnet/composer-require-checker": "^4.4",
"mockery/mockery": "^1.6",
"nette/utils": "^4.0",
"orchestra/testbench": "^7.53 || ^8.0 || ^9.0 || ^10.0",
@@ -119,6 +118,7 @@
"rector/type-perfect": "^2.0",
"shipmonk/composer-dependency-analyser": "^1.8",
"shipmonk/phpstan-baseline-per-identifier": "^2.1",
+ "spatie/pest-plugin-snapshots": "^1.1 || ^2.0",
"spaze/phpstan-disallowed-calls": "^4.4",
"symplify/phpstan-extensions": "^12.0",
"symplify/phpstan-rules": "^14.4",
diff --git a/docs/mail.jpg b/docs/mail.jpg
index ed42501..1e21e3c 100644
Binary files a/docs/mail.jpg and b/docs/mail.jpg differ
diff --git a/phpstan.neon b/phpstan.neon
index 5a38216..d82f415 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -28,6 +28,8 @@ includes:
parameters:
paths:
- src/
+ scanFiles:
+ - vendor/composer/InstalledVersions.php
excludePaths:
- tests/Fixtures/
level: max
diff --git a/rector.php b/rector.php
index 289254d..0fb83f9 100644
--- a/rector.php
+++ b/rector.php
@@ -18,10 +18,8 @@
use Carbon\Carbon;
use Composer\Autoload\ClassLoader;
use Ergebnis\Rector\Rules\Arrays\SortAssociativeArrayByKeyRector;
-use Guanguans\LaravelExceptionNotify\Channels\AbstractChannel;
use Guanguans\LaravelExceptionNotify\Support\Rectors\HydratePipeFuncCallToStaticCallRector;
use Guanguans\LaravelExceptionNotify\Support\Rectors\ToInternalExceptionRector;
-use Guanguans\LaravelExceptionNotify\Support\Utils;
use Guanguans\LaravelExceptionNotify\Template;
use Illuminate\Support\Carbon as IlluminateCarbon;
use Illuminate\Support\Collection;
@@ -47,12 +45,8 @@
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
-use Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector;
-use Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector;
use Rector\Transform\Rector\Scalar\ScalarValueToConstFetchRector;
use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector;
-use Rector\Transform\ValueObject\FuncCallToStaticCall;
-use Rector\Transform\ValueObject\MethodCallToStaticCall;
use Rector\Transform\ValueObject\ScalarValueToConstFetch;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
use Rector\ValueObject\PhpVersion;
@@ -153,19 +147,12 @@
'phpstan-ignore-next-line',
'psalm-suppress',
])
- ->withConfiguredRule(MethodCallToStaticCallRector::class, [
- new MethodCallToStaticCall(AbstractChannel::class, 'applyContentToConfiguration', Utils::class, 'applyContentToConfiguration'),
- new MethodCallToStaticCall(AbstractChannel::class, 'applyConfigurationToObject', Utils::class, 'applyConfigurationToObject'),
- ])
->withConfiguredRule(RenameClassRector::class, [
Carbon::class => IlluminateCarbon::class,
])
->withConfiguredRule(StaticCallToFuncCallRector::class, [
new StaticCallToFuncCall(Str::class, 'of', 'str'),
])
- // ->withConfiguredRule(FuncCallToStaticCallRector::class, [
- // new FuncCallToStaticCall('str', Str::class, 'of'),
- // ])
->withConfiguredRule(
ScalarValueToConstFetchRector::class,
collect([
@@ -225,16 +212,13 @@ static function (array $carry, mixed $value, string $name) use ($class): array {
'Guanguans\Notify\Foundation\Support\rescue' => 'Guanguans\LaravelExceptionNotify\Support\rescue',
'Pest\Faker\fake' => 'fake',
'Pest\Faker\faker' => 'faker',
- 'rescue' => 'Guanguans\LaravelExceptionNotify\Support\rescue',
'test' => 'it',
] + array_reduce(
[
- 'make',
'env_explode',
'json_pretty_encode',
- 'hydrate_pipe',
- 'human_bytes',
- 'human_milliseconds',
+ 'make',
+ 'rescue',
],
static function (array $carry, string $func): array {
/** @see https://github.com/laravel/framework/blob/11.x/src/Illuminate/Support/functions.php */
diff --git a/src/Collectors/AbstractCollector.php b/src/Collectors/AbstractCollector.php
index 8f89209..24acf99 100644
--- a/src/Collectors/AbstractCollector.php
+++ b/src/Collectors/AbstractCollector.php
@@ -28,8 +28,9 @@ public function name(): string
public static function fallbackName(): string
{
- return ucwords((string) str(class_basename(static::class))
- ->beforeLast(str(class_basename(self::class))->remove('Abstract'))
- ->snake(' '));
+ return str(class_basename(static::class))
+ ->beforeLast('Collector')
+ ->headline()
+ ->toString();
}
}
diff --git a/src/Collectors/ApplicationCollector.php b/src/Collectors/ApplicationCollector.php
index c21c196..b0275a4 100644
--- a/src/Collectors/ApplicationCollector.php
+++ b/src/Collectors/ApplicationCollector.php
@@ -13,9 +13,9 @@
namespace Guanguans\LaravelExceptionNotify\Collectors;
+use Guanguans\LaravelExceptionNotify\Support\Utils;
use Illuminate\Container\Container;
use Illuminate\Support\Carbon;
-use function Guanguans\LaravelExceptionNotify\Support\human_bytes;
class ApplicationCollector extends AbstractCollector
{
@@ -35,7 +35,7 @@ public function collect(): array
'debug' => $this->container->hasDebugModeEnabled(),
'locale' => $this->container->getLocale(),
'in console' => $this->container->runningInConsole(),
- 'memory' => human_bytes(memory_get_peak_usage(true)),
+ 'memory' => Utils::humanBytes(memory_get_peak_usage(true)),
];
}
}
diff --git a/src/Collectors/RequestBasicCollector.php b/src/Collectors/RequestBasicCollector.php
index 495f09f..34e4ff8 100644
--- a/src/Collectors/RequestBasicCollector.php
+++ b/src/Collectors/RequestBasicCollector.php
@@ -13,8 +13,8 @@
namespace Guanguans\LaravelExceptionNotify\Collectors;
+use Guanguans\LaravelExceptionNotify\Support\Utils;
use Illuminate\Http\Request;
-use function Guanguans\LaravelExceptionNotify\Support\human_milliseconds;
class RequestBasicCollector extends AbstractCollector
{
@@ -29,7 +29,7 @@ public function collect(): array
'controller action' => $this->request->route()?->getActionName(),
'duration' => blank($startTime = \defined('LARAVEL_START') ? LARAVEL_START : $this->request->server('REQUEST_TIME_FLOAT'))
? 'Unknown'
- : human_milliseconds((microtime(true) - $startTime) * 1000),
+ : Utils::humanMilliseconds((microtime(true) - $startTime) * 1000),
];
}
}
diff --git a/src/Collectors/RequestFileCollector.php b/src/Collectors/RequestFileCollector.php
index 0db7048..ccdd7ea 100644
--- a/src/Collectors/RequestFileCollector.php
+++ b/src/Collectors/RequestFileCollector.php
@@ -13,9 +13,9 @@
namespace Guanguans\LaravelExceptionNotify\Collectors;
+use Guanguans\LaravelExceptionNotify\Support\Utils;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
-use function Guanguans\LaravelExceptionNotify\Support\human_bytes;
class RequestFileCollector extends AbstractCollector
{
@@ -33,7 +33,7 @@ public function collect(): array
'name' => $uploadedFile->getClientOriginalName(),
'type' => $uploadedFile->getMimeType(),
'error' => $uploadedFile->getError(),
- 'size' => human_bytes($uploadedFile->getSize()),
+ 'size' => Utils::humanBytes($uploadedFile->getSize()),
];
});
diff --git a/src/ExceptionNotifyServiceProvider.php b/src/ExceptionNotifyServiceProvider.php
index 4f40216..0bb482d 100644
--- a/src/ExceptionNotifyServiceProvider.php
+++ b/src/ExceptionNotifyServiceProvider.php
@@ -13,9 +13,13 @@
namespace Guanguans\LaravelExceptionNotify;
+use Composer\InstalledVersions;
use Guanguans\LaravelExceptionNotify\Commands\TestCommand;
use Guanguans\LaravelExceptionNotify\Facades\ExceptionNotify;
use Illuminate\Contracts\Debug\ExceptionHandler;
+use Illuminate\Foundation\Console\AboutCommand;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\File;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Stringable;
@@ -103,11 +107,33 @@ private function registerCommands(): self
{
if ($this->app->runningInConsole()) {
$this->commands(TestCommand::class);
+ $this->addSectionToAboutCommand();
}
return $this;
}
+ private function addSectionToAboutCommand(): void
+ {
+ AboutCommand::add('Laravel Exception Notify', static function (): array {
+ $package = 'guanguans/laravel-exception-notify';
+
+ return collect(
+ json_decode(File::get(base_path("vendor/$package/composer.json")), true, 512, \JSON_THROW_ON_ERROR)
+ + Arr::get(InstalledVersions::getAllRawData(), "0.versions.$package", [])
+ )
+ ->filter(static fn (mixed $value): bool => \is_string($value))
+ ->except([
+ '$schema',
+ 'install_path',
+ 'readme',
+ 'reference',
+ ])
+ ->mapWithKeys(static fn (string $value, string $key): array => [str($key)->headline()->toString() => $value])
+ ->all();
+ });
+ }
+
/**
* @param class-string $class
*/
diff --git a/src/Pipes/SprintfMarkdownPipe.php b/src/Pipes/SprintfMarkdownPipe.php
index a5612c8..76dfb7c 100644
--- a/src/Pipes/SprintfMarkdownPipe.php
+++ b/src/Pipes/SprintfMarkdownPipe.php
@@ -25,7 +25,7 @@ public function handle(
Collection $collectors,
\Closure $next,
string $format = <<<'mark'
- ```json
+ ```
%s
```
mark
diff --git a/src/Support/Utils.php b/src/Support/Utils.php
index 4138ef9..1fc8871 100644
--- a/src/Support/Utils.php
+++ b/src/Support/Utils.php
@@ -19,19 +19,6 @@
class Utils
{
- public static function applyContentToConfiguration(array $configuration, string $content): array
- {
- array_walk_recursive($configuration, static function (mixed &$value) use ($content): void {
- \is_string($value) and $value = str_replace(
- [Template::TITLE, Template::CONTENT],
- [config('exception-notify.title'), $content],
- $value
- );
- });
-
- return $configuration;
- }
-
public static function applyConfigurationToObject(object $object, array $configuration, ?array $except = null): object
{
return collect($configuration)
@@ -92,4 +79,45 @@ public static function applyConfigurationToObject(object $object, array $configu
return $extender($object);
});
}
+
+ public static function applyContentToConfiguration(array $configuration, string $content): array
+ {
+ array_walk_recursive($configuration, static function (mixed &$value) use ($content): void {
+ \is_string($value) and $value = str_replace(
+ [Template::TITLE, Template::CONTENT],
+ [config('exception-notify.title'), $content],
+ $value
+ );
+ });
+
+ return $configuration;
+ }
+
+ /**
+ * @see https://stackoverflow.com/a/23888858/1580028
+ */
+ public static function humanBytes(int $bytes, int $decimals = 2): string
+ {
+ $size = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+ $factor = (int) floor((\strlen((string) $bytes) - 1) / 3);
+
+ if (0 === $factor) {
+ $decimals = 0;
+ }
+
+ return \sprintf("%.{$decimals}f %s", $bytes / (1024 ** $factor), $size[$factor]);
+ }
+
+ public static function humanMilliseconds(float $milliseconds, int $precision = 2): string
+ {
+ if (1 > $milliseconds) {
+ return \sprintf('%s μs', round($milliseconds * 1000, $precision));
+ }
+
+ if (1000 > $milliseconds) {
+ return \sprintf('%s ms', round($milliseconds, $precision));
+ }
+
+ return \sprintf('%s s', round($milliseconds / 1000, $precision));
+ }
}
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
index d458bfd..1895dbf 100644
--- a/src/Support/helpers.php
+++ b/src/Support/helpers.php
@@ -17,6 +17,38 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
+if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\env_explode')) {
+ /**
+ * @noinspection LaravelFunctionsInspection
+ */
+ function env_explode(string $key, mixed $default = null, string $delimiter = ',', int $limit = \PHP_INT_MAX): mixed
+ {
+ $env = env($key, $default);
+
+ if (\is_string($env)) {
+ return $env ? explode($delimiter, $env, $limit) : [];
+ }
+
+ return $env;
+ }
+}
+
+if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\json_pretty_encode')) {
+ /**
+ * @param int<1, 4194304> $depth
+ *
+ * @throws \JsonException
+ */
+ function json_pretty_encode(mixed $value, int $options = 0, int $depth = 512): string
+ {
+ return json_encode(
+ $value,
+ \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_THROW_ON_ERROR | \JSON_FORCE_OBJECT | $options,
+ $depth
+ );
+ }
+}
+
if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\make')) {
/**
* @see https://github.com/laravel/framework/blob/12.x/src/Illuminate/Foundation/helpers.php
@@ -80,67 +112,3 @@ function rescue(callable $callback, mixed $rescue = null, mixed $log = true): mi
}
}
}
-
-if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\env_explode')) {
- /**
- * @noinspection LaravelFunctionsInspection
- */
- function env_explode(string $key, mixed $default = null, string $delimiter = ',', int $limit = \PHP_INT_MAX): mixed
- {
- $env = env($key, $default);
-
- if (\is_string($env)) {
- return $env ? explode($delimiter, $env, $limit) : [];
- }
-
- return $env;
- }
-}
-
-if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\json_pretty_encode')) {
- /**
- * @param int<1, 4194304> $depth
- *
- * @throws \JsonException
- */
- function json_pretty_encode(mixed $value, int $options = 0, int $depth = 512): string
- {
- return json_encode(
- $value,
- \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_THROW_ON_ERROR | \JSON_FORCE_OBJECT | $options,
- $depth
- );
- }
-}
-
-if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\human_bytes')) {
- /**
- * @see https://stackoverflow.com/a/23888858/1580028
- */
- function human_bytes(int $bytes, int $decimals = 2): string
- {
- $size = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- $factor = (int) floor((\strlen((string) $bytes) - 1) / 3);
-
- if (0 === $factor) {
- $decimals = 0;
- }
-
- return \sprintf("%.{$decimals}f %s", $bytes / (1024 ** $factor), $size[$factor]);
- }
-}
-
-if (!\function_exists('Guanguans\LaravelExceptionNotify\Support\human_milliseconds')) {
- function human_milliseconds(float $milliseconds, int $precision = 2): string
- {
- if (1 > $milliseconds) {
- return \sprintf('%s μs', round($milliseconds * 1000, $precision));
- }
-
- if (1000 > $milliseconds) {
- return \sprintf('%s ms', round($milliseconds, $precision));
- }
-
- return \sprintf('%s s', round($milliseconds / 1000, $precision));
- }
-}
diff --git a/tests/Channels/AbstractChannelTest.php b/tests/Channels/AbstractChannelTest.php
index dc3f9ff..c50cbdd 100644
--- a/tests/Channels/AbstractChannelTest.php
+++ b/tests/Channels/AbstractChannelTest.php
@@ -23,7 +23,7 @@
it('can report', function (): void {
(fn (): bool => $this->isRunningInConsole = false)->call(app());
- expect($this->app->make(ExceptionNotifyManager::class)->driver('dump'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('dump'))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
diff --git a/tests/Channels/ChannelTest.php b/tests/Channels/ChannelTest.php
index 996819b..6f92955 100644
--- a/tests/Channels/ChannelTest.php
+++ b/tests/Channels/ChannelTest.php
@@ -27,7 +27,7 @@
it('can not report', function (): void {
config()->set('exception-notify.rate_limiter.max_attempts', 0);
- expect($this->app->make(ExceptionNotifyManager::class))
+ expect(resolve(ExceptionNotifyManager::class))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
@@ -41,7 +41,7 @@
Log::info($reportedEvent::class);
});
- expect($this->app->make(ExceptionNotifyManager::class))
+ expect(resolve(ExceptionNotifyManager::class))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
@@ -57,7 +57,11 @@
it('are same fingerprints for exceptions of throw in the same position', function (): void {
collect(range(1, 10))
- ->map(fn (): string => (fn () => $this->fingerprintFor(new RuntimeException(microtime())))->call(ExceptionNotify::driver('log')))
+ ->map(
+ fn (): string => (
+ fn () => $this->fingerprintFor(new RuntimeException(microtime()))
+ )->call(ExceptionNotify::driver('log'))
+ )
->reduce(static function (?string $previousFingerprint, string $fingerprint): string {
$previousFingerprint and expect($previousFingerprint)->toBe($fingerprint);
diff --git a/tests/Channels/DumpChannelTest.php b/tests/Channels/DumpChannelTest.php
index 0280e15..7952f24 100644
--- a/tests/Channels/DumpChannelTest.php
+++ b/tests/Channels/DumpChannelTest.php
@@ -22,7 +22,7 @@
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
it('can report', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class)->driver('dump'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('dump'))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
diff --git a/tests/Channels/LogChannelTest.php b/tests/Channels/LogChannelTest.php
index f6fbb47..2d78af4 100644
--- a/tests/Channels/LogChannelTest.php
+++ b/tests/Channels/LogChannelTest.php
@@ -22,7 +22,7 @@
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
it('can report', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class)->driver('log'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('log'))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
diff --git a/tests/Channels/MailChannelTest.php b/tests/Channels/MailChannelTest.php
index 81ac4bd..e072ad4 100644
--- a/tests/Channels/MailChannelTest.php
+++ b/tests/Channels/MailChannelTest.php
@@ -21,16 +21,10 @@
use Guanguans\LaravelExceptionNotify\ExceptionNotifyManager;
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
use Guanguans\LaravelExceptionNotify\Mail\ReportExceptionMail;
-use Guanguans\LaravelExceptionNotifyTests\Fixtures\MailableExtender;
use Illuminate\Support\Facades\Mail;
it('can report', function (): void {
- config([
- 'exception-notify.channels.mail.render' => 'value',
- 'exception-notify.channels.mail.extender' => MailableExtender::class,
- ]);
-
- expect($this->app->make(ExceptionNotifyManager::class)->driver('mail'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('mail'))
->report(new RuntimeException('testing'))
->toBeNull();
Mail::assertSent(ReportExceptionMail::class);
diff --git a/tests/Channels/NotifyChannelTest.php b/tests/Channels/NotifyChannelTest.php
index acb89a7..ca9d90d 100644
--- a/tests/Channels/NotifyChannelTest.php
+++ b/tests/Channels/NotifyChannelTest.php
@@ -22,7 +22,7 @@
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
it('can report', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class)->driver('bark'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('bark'))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
diff --git a/tests/Channels/StackChannelTest.php b/tests/Channels/StackChannelTest.php
index 6956035..64002ed 100644
--- a/tests/Channels/StackChannelTest.php
+++ b/tests/Channels/StackChannelTest.php
@@ -22,13 +22,13 @@
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
it('can report', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class)->driver('stack'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('stack'))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
it('can report content', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class)->driver('stack'))
+ expect(resolve(ExceptionNotifyManager::class)->driver('stack'))
->reportContent('testing')
->toBeArray();
})->group(__DIR__, __FILE__);
diff --git a/tests/Commands/TestCommandTest.php b/tests/Commands/TestCommandTest.php
index ce2d4dd..0e2a36c 100644
--- a/tests/Commands/TestCommandTest.php
+++ b/tests/Commands/TestCommandTest.php
@@ -21,6 +21,8 @@
use Guanguans\LaravelExceptionNotify\Commands\TestCommand;
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
use Guanguans\LaravelExceptionNotify\Facades\ExceptionNotify;
+use Guanguans\Notify\Foundation\Client;
+use GuzzleHttp\Psr7\Response;
use Symfony\Component\Console\Command\Command;
use function Pest\Laravel\artisan;
@@ -41,9 +43,28 @@
})->group(__DIR__, __FILE__);
it('will throws RuntimeException', function (): void {
- artisan(TestCommand::class, [
- '--channel' => 'stack',
- '--config' => "app.name={$this->faker()->name()}",
- '--verbose' => true,
- ]);
+ artisan(TestCommand::class);
})->group(__DIR__, __FILE__)->throws(RuntimeException::class, 'Testing for exception-notify.');
+
+it('will catch RuntimeException and can report it', function (): void {
+ try {
+ artisan(TestCommand::class, [
+ '--channel' => $channel = 'bark',
+ '--config' => "app.name={$this->faker()->name()}",
+ '--verbose' => true,
+ ]);
+ } catch (RuntimeException $runtimeException) {
+ ExceptionNotify::forgetDrivers();
+
+ $extender = config($configurationKey = "exception-notify.channels.$channel.client.extender");
+
+ config()->set(
+ $configurationKey,
+ static fn (Client $client): Client => $extender($client)->mock([
+ new Response(body: \sprintf('{"code":200,"message":"%s","timestamp":1708331409}', fake()->text())),
+ ])
+ );
+
+ expect(ExceptionNotify::report($runtimeException))->toBeNull();
+ }
+})->group(__DIR__, __FILE__);
diff --git a/tests/ExceptionNotifyManagerTest.php b/tests/ExceptionNotifyManagerTest.php
index f28e0e3..0a85296 100644
--- a/tests/ExceptionNotifyManagerTest.php
+++ b/tests/ExceptionNotifyManagerTest.php
@@ -36,13 +36,13 @@
})->group(__DIR__, __FILE__);
it('can report', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class))
+ expect(resolve(ExceptionNotifyManager::class))
->report(new RuntimeException('testing'))
->toBeNull();
})->group(__DIR__, __FILE__);
it('can report content', function (): void {
- expect($this->app->make(ExceptionNotifyManager::class))
+ expect(resolve(ExceptionNotifyManager::class))
->reportContent('testing')
->toBeArray();
})->group(__DIR__, __FILE__);
@@ -52,14 +52,17 @@
})->group(__DIR__, __FILE__)->throws(InvalidArgumentException::class);
it('can create custom driver', function (): void {
- resolve(ExceptionNotifyManager::class)->extend('foo', static fn (): ChannelContract => new class implements ChannelContract {
- public function report(Throwable $throwable): void {}
+ resolve(ExceptionNotifyManager::class)->extend(
+ 'foo',
+ static fn (): ChannelContract => new class implements ChannelContract {
+ public function report(Throwable $throwable): void {}
- public function reportContent(string $content): mixed
- {
- return null;
+ public function reportContent(string $content): mixed
+ {
+ return null;
+ }
}
- });
+ );
expect(resolve(ExceptionNotifyManager::class))->driver('foo')->toBeInstanceOf(ChannelContract::class);
})->group(__DIR__, __FILE__);
diff --git a/tests/ExceptionNotifyServiceProviderTest.php b/tests/ExceptionNotifyServiceProviderTest.php
index 8e3261c..cdd3c25 100644
--- a/tests/ExceptionNotifyServiceProviderTest.php
+++ b/tests/ExceptionNotifyServiceProviderTest.php
@@ -19,6 +19,12 @@
*/
use Guanguans\LaravelExceptionNotify\ExceptionNotifyServiceProvider;
+use Illuminate\Contracts\Filesystem\FileNotFoundException;
+use function Pest\Laravel\artisan;
+
+it('can add section to about command', function (): void {
+ artisan('about');
+})->group(__DIR__, __FILE__)->throws(FileNotFoundException::class);
it('can get provides', function (): void {
expect(new ExceptionNotifyServiceProvider(app()))
diff --git a/tests/Faker.php b/tests/Faker.php
index 4ab3757..6e56276 100644
--- a/tests/Faker.php
+++ b/tests/Faker.php
@@ -18,7 +18,7 @@
trait Faker
{
- final protected function faker(string $locale = 'en_US'): Generator
+ final protected function faker(string $locale = Factory::DEFAULT_LOCALE): Generator
{
/**
* @var array $fakers
diff --git a/tests/FeatureTest.php b/tests/FeatureTest.php
index e1fb74e..4493af2 100644
--- a/tests/FeatureTest.php
+++ b/tests/FeatureTest.php
@@ -37,5 +37,5 @@
'password' => 'password',
'file' => new UploadedFile(__FILE__, basename(__FILE__)),
])
- ->assertStatus(500);
+ ->assertServerError();
})->group(__DIR__, __FILE__);
diff --git a/tests/Fixtures/.gitkeep b/tests/Fixtures/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/Fixtures/Illuminate b/tests/Fixtures/Illuminate
deleted file mode 120000
index c937061..0000000
--- a/tests/Fixtures/Illuminate
+++ /dev/null
@@ -1 +0,0 @@
-/Users/yaozm/Documents/develop/laravel-exception-notify/tests/Fixtures/../../vendor/laravel/framework/src/Illuminate/
\ No newline at end of file
diff --git a/tests/Fixtures/laravel b/tests/Fixtures/laravel
deleted file mode 120000
index 33abf98..0000000
--- a/tests/Fixtures/laravel
+++ /dev/null
@@ -1 +0,0 @@
-/Users/yaozm/Documents/develop/laravel-exception-notify/tests/Fixtures/../../vendor/orchestra/testbench-core/laravel/
\ No newline at end of file
diff --git a/tests/Fixtures/ln.php b/tests/Fixtures/ln.php
deleted file mode 100644
index 10e0288..0000000
--- a/tests/Fixtures/ln.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- *
- * @see https://github.com/guanguans/laravel-exception-notify
- */
-
-use Illuminate\Console\OutputStyle;
-use Symfony\Component\Console\Input\ArgvInput;
-use Symfony\Component\Console\Output\ConsoleOutput;
-use Symfony\Component\Process\Process;
-
-require __DIR__.'/../../vendor/autoload.php';
-
-$process = new Process([
- 'ln', '-sf', $dir = __DIR__.'/../../vendor/orchestra/testbench-core/laravel/', basename($dir),
- // 'ln', '-sf', $dir = __DIR__.'/../../vendor/laravel/framework/src/Illuminate/', basename($dir),
-]);
-
-$outputStyle = new OutputStyle(new ArgvInput, new ConsoleOutput);
-
-$process->mustRun(static function ($type, $buffer) use ($outputStyle): void {
- $outputStyle->write($buffer);
-});
-
-$outputStyle->success('ok');
diff --git a/tests/Pest.php b/tests/Pest.php
index 4f3264c..98aeb9b 100644
--- a/tests/Pest.php
+++ b/tests/Pest.php
@@ -24,11 +24,16 @@
use Faker\Factory;
use Guanguans\LaravelExceptionNotifyTests\TestCase;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Artisan;
use Pest\Expectation;
uses(TestCase::class)
->beforeAll(function (): void {})
->beforeEach(function (): void {
+ links([
+ __DIR__.'/../'.basename($target = __DIR__.'/../vendor/orchestra/testbench-core/laravel') => $target,
+ ]);
+
/** @var TestCase $this */
$this->defineEnvironment(app());
})
@@ -66,6 +71,19 @@
|
*/
+function classes(): Collection
+{
+ return collect(spl_autoload_functions())
+ ->pipe(static fn (Collection $splAutoloadFunctions): Collection => collect(
+ $splAutoloadFunctions
+ ->firstOrFail(
+ static fn (mixed $loader): bool => \is_array($loader) && $loader[0] instanceof ClassLoader
+ )[0]
+ ->getClassMap()
+ ))
+ ->keys();
+}
+
/**
* @throws ReflectionException
*/
@@ -91,15 +109,23 @@ function faker(string $locale = Factory::DEFAULT_LOCALE): Generator
// return Factory::create($locale);
// }
-function classes(): Collection
+/**
+ * @see \Illuminate\Foundation\Console\StorageLinkCommand
+ */
+function links(array $links, array $parameters = []): int
{
- return collect(spl_autoload_functions())
- ->pipe(static fn (Collection $splAutoloadFunctions): Collection => collect(
- $splAutoloadFunctions
- ->firstOrFail(
- static fn (mixed $loader): bool => \is_array($loader) && $loader[0] instanceof ClassLoader
- )[0]
- ->getClassMap()
- ))
- ->keys();
+ $originalLinks = config('filesystems.links', []);
+
+ config()->set('filesystems.links', $links);
+
+ $status = Artisan::call('storage:link', $parameters + [
+ '--ansi' => true,
+ '--verbose' => true,
+ ]);
+
+ config()->set('filesystems.links', $originalLinks);
+
+ // echo Artisan::output();
+
+ return $status;
}
diff --git a/tests/Support/HeplersTest.php b/tests/Support/HeplersTest.php
index f46d3c2..22cc2f3 100644
--- a/tests/Support/HeplersTest.php
+++ b/tests/Support/HeplersTest.php
@@ -22,24 +22,9 @@
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
use Pest\Expectation;
use function Guanguans\LaravelExceptionNotify\Support\env_explode;
-use function Guanguans\LaravelExceptionNotify\Support\human_bytes;
-use function Guanguans\LaravelExceptionNotify\Support\human_milliseconds;
use function Guanguans\LaravelExceptionNotify\Support\make;
use function Guanguans\LaravelExceptionNotify\Support\rescue;
-it('will throw `InvalidArgumentException` when abstract is empty array', function (): void {
- make([]);
-})->group(__DIR__, __FILE__)->throws(InvalidArgumentException::class);
-
-it('can catch a potential exception and return a default value', function (): void {
- expect(rescue(
- function (): void {
- throw new RuntimeException('testing');
- },
- fn (Throwable $throwable): Throwable => $throwable
- ))->toBeInstanceOf(RuntimeException::class);
-})->group(__DIR__, __FILE__);
-
it('can explode env', function (): void {
expect([
env_explode('ENV_EXPLODE_STRING'),
@@ -58,30 +43,15 @@ function (): void {
);
})->group(__DIR__, __FILE__);
-it('can human bytes', function (): void {
- expect([
- human_bytes(0),
- human_bytes(10),
- human_bytes(10000),
- human_bytes(10000000),
- ])->sequence(
- static fn (Expectation $expectation): Expectation => $expectation->toBe('0 B'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('10 B'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('9.77 KB'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('9.54 MB')
- );
-})->group(__DIR__, __FILE__);
+it('will throw `InvalidArgumentException` when abstract is empty array', function (): void {
+ make([]);
+})->group(__DIR__, __FILE__)->throws(InvalidArgumentException::class);
-it('can human milliseconds', function (): void {
- expect([
- human_milliseconds(0),
- human_milliseconds(0.5),
- human_milliseconds(500),
- human_milliseconds(500000),
- ])->sequence(
- static fn (Expectation $expectation): Expectation => $expectation->toBe('0 μs'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('500 μs'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('500 ms'),
- static fn (Expectation $expectation): Expectation => $expectation->toBe('500 s')
- );
+it('can catch a potential exception and return a default value', function (): void {
+ expect(rescue(
+ function (): void {
+ throw new RuntimeException('testing');
+ },
+ fn (Throwable $throwable): Throwable => $throwable
+ ))->toBeInstanceOf(RuntimeException::class);
})->group(__DIR__, __FILE__);
diff --git a/tests/Support/UtilsTest.php b/tests/Support/UtilsTest.php
new file mode 100644
index 0000000..1d9deb3
--- /dev/null
+++ b/tests/Support/UtilsTest.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ *
+ * @see https://github.com/guanguans/laravel-exception-notify
+ */
+
+use Guanguans\LaravelExceptionNotify\Mail\ReportExceptionMail;
+use Guanguans\LaravelExceptionNotify\Support\Utils;
+use Guanguans\LaravelExceptionNotifyTests\Fixtures\MailableExtender;
+use Pest\Expectation;
+
+it('can apply configuration to object', function (): void {
+ expect(Utils::applyConfigurationToObject(
+ new ReportExceptionMail($this->faker()->title(), $this->faker()->text()),
+ [
+ 'render' => 'value',
+ 'extender' => MailableExtender::class,
+ ]
+ ))->toBeInstanceOf(ReportExceptionMail::class);
+})->group(__DIR__, __FILE__);
+
+it('can human bytes', function (): void {
+ expect([
+ Utils::humanBytes(0),
+ Utils::humanBytes(10),
+ Utils::humanBytes(10000),
+ Utils::humanBytes(10000000),
+ ])->sequence(
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('0 B'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('10 B'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('9.77 KB'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('9.54 MB')
+ );
+})->group(__DIR__, __FILE__);
+
+it('can human milliseconds', function (): void {
+ expect([
+ Utils::humanMilliseconds(0),
+ Utils::humanMilliseconds(0.5),
+ Utils::humanMilliseconds(500),
+ Utils::humanMilliseconds(500000),
+ ])->sequence(
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('0 μs'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('500 μs'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('500 ms'),
+ static fn (Expectation $expectation): Expectation => $expectation->toBe('500 s')
+ );
+})->group(__DIR__, __FILE__);
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 49b6e8f..5f7156f 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -17,6 +17,7 @@
namespace Guanguans\LaravelExceptionNotifyTests;
use Guanguans\LaravelExceptionNotify\Channels\Channel;
+use Guanguans\LaravelExceptionNotify\Channels\DumpChannel;
use Guanguans\LaravelExceptionNotify\Collectors\ApplicationCollector;
use Guanguans\LaravelExceptionNotify\Collectors\ChoreCollector;
use Guanguans\LaravelExceptionNotify\Collectors\ExceptionBasicCollector;
@@ -27,6 +28,7 @@
use Guanguans\LaravelExceptionNotify\Collectors\RequestHeaderCollector;
use Guanguans\LaravelExceptionNotify\Collectors\RequestPostCollector;
use Guanguans\LaravelExceptionNotify\Collectors\RequestQueryCollector;
+use Guanguans\LaravelExceptionNotify\Events\ReportingEvent;
use Guanguans\LaravelExceptionNotify\ExceptionNotifyServiceProvider;
use Guanguans\LaravelExceptionNotify\Exceptions\RuntimeException;
use Guanguans\LaravelExceptionNotify\Facades\ExceptionNotify;
@@ -45,6 +47,7 @@
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use phpmock\phpunit\PHPMock;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+use function Spatie\Snapshots\assertMatchesJsonSnapshot;
class TestCase extends \Orchestra\Testbench\TestCase
{
@@ -109,22 +112,6 @@ protected function defineEnvironment($app): void
LimitLengthPipe::with(512),
]);
- config()->set('exception-notify.channels.stack.channels', [
- 'dump',
- 'log',
- 'mail',
- 'bark',
- 'chanify',
- 'dingTalk',
- 'discord',
- 'lark',
- 'ntfy',
- 'pushDeer',
- 'slack',
- 'telegram',
- 'weWork',
- ]);
-
config([
'exception-notify.channels.bark.authenticator.token' => fake()->uuid(),
'exception-notify.channels.chanify.authenticator.token' => fake()->uuid(),
@@ -159,6 +146,28 @@ protected function defineRoutes($router): void
static fn () => tap(
response('proactive-report-exception'),
static function (): void {
+ config()->set('exception-notify.channels.stack.channels', [
+ 'dump',
+ 'log',
+ 'mail',
+ 'bark',
+ 'chanify',
+ 'dingTalk',
+ 'discord',
+ 'lark',
+ 'ntfy',
+ 'pushDeer',
+ 'slack',
+ 'telegram',
+ 'weWork',
+ ]);
+
+ ExceptionNotify::reporting(static function (ReportingEvent $reportingEvent): void {
+ if ($reportingEvent->channelContract instanceof DumpChannel) {
+ assertMatchesJsonSnapshot($reportingEvent->content);
+ }
+ });
+
ExceptionNotify::report(new RuntimeException('What happened?'));
}
)
diff --git a/tests/__snapshots__/FeatureTest__it_can_proactive_report_exception__1.json b/tests/__snapshots__/FeatureTest__it_can_proactive_report_exception__1.json
new file mode 100644
index 0000000..840e862
--- /dev/null
+++ b/tests/__snapshots__/FeatureTest__it_can_proactive_report_exception__1.json
@@ -0,0 +1,69 @@
+{
+ "Application": {
+ "time": "2025-03-22 08:20:39",
+ "name": "Laravel",
+ "version": "9.52.20",
+ "php version": "8.0.30",
+ "environment": "testing",
+ "debug": false,
+ "locale": "en",
+ "in console": true,
+ "memory": "52.50 MB"
+ },
+ "Chore": {},
+ "Exception Basic": {
+ "message": "What happened?",
+ "code": 0,
+ "class": "Guanguans\\LaravelExceptionNotify\\Exceptions\\RuntimeException",
+ "file": "\/Users\/yaozm\/Documents\/develop\/laravel-exception-notify\/tests\/TestCase.php",
+ "line": 171
+ },
+ "Exception Context": {
+ " 167": " assertMatchesJsonSnapshot($reportingEvent->content);\n",
+ " 168": " }\n",
+ " 169": " });\n",
+ " 170": "\n",
+ "\u27a4 171": " ExceptionNotify::report(new RuntimeException('What happened?'));\n",
+ " 172": " }\n",
+ " 173": " )\n",
+ " 174": " );\n",
+ " 175": "\n"
+ },
+ "Exception Trace": {
+ "1": "#1 \/Users\/yaozm\/Documents\/develop\/laravel-exception-notify\/tests\/TestCase.php(172): tap(Object(Illuminate\\Http\\Response), Object(Closure))",
+ "29": "#29 \/Users\/yaozm\/Documents\/develop\/laravel-exception-notify\/tests\/FeatureTest.php(28): Orchestra\\Testbench\\TestCase->post('proactive-repor...', Array)",
+ "30": "#30 [internal function]: P\\Tests\\FeatureTest->{closure}()",
+ "32": "#32 [internal function]: P\\Tests\\FeatureTest->Pest\\Factories\\{closure}()",
+ "49": "#49 {main}"
+ },
+ "Request Basic": {
+ "path": "proactive-report-exception",
+ "ip": "127.0.0.1",
+ "method": "POST",
+ "controller action": "Closure",
+ "duration": "15.69 ms"
+ },
+ "Request File": {
+ "file": {
+ "name": "FeatureTest.php",
+ "type": "text\/x-php",
+ "error": 0,
+ "size": "1.27 KB"
+ }
+ },
+ "Request Header": {
+ "host": "localhost",
+ "user-agent": "Symfony",
+ "accept": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
+ "accept-language": "en-us,en;q=0.5",
+ "accept-charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
+ "content-type": "application\/x-www-form-urlencoded"
+ },
+ "Request Post": {
+ "bar": "baz",
+ "password": "******"
+ },
+ "Request Query": {
+ "foo": "bar"
+ }
+}