text:
Script completed
Wall time 4.9 seconds
Output:

text:
{"result":"fixed","files":[{"path":"app\/Services\/OrderService.php","fixers":["unary_operator_spaces","braces_position","no_unused_imports","not_operator_with_successor_space","single_line_empty_body"]},{"path":"tests\/Feature\/CommerceFlowTest.php","fixers":["ordered_imports"]}]}
  ............⨯.⨯.....
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [403] but received 500.
Failed asserting that 500 is identical to 403.

The following exception occurred during the last request:

ErrorException: Undefined variable $user in /Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php:31
Stack trace:
#0 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php(258): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'Undefined varia...', '/Users/wesner/H...', 31)
#1 /Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php(31): Illuminate\Foundation\Bootstrap\HandleExceptions->{closure:Illuminate\Foundation\Bootstrap\HandleExceptions::forwardsTo():257}(2, 'Undefined varia...', '/Users/wesner/H...', 31)
#2 /Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php(18): App\Http\Middleware\EnsureApiAbility->abilityFor(Object(Illuminate\Http\Request))
#3 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): App\Http\Middleware\EnsureApiAbility->handle(Object(Illuminate\Http\Request), Object(Closure))
#4 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#5 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Routing\Middleware\SubstituteBindings->handle(Object(Illuminate\Http\Request), Object(Closure))
#6 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(166): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#7 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(129): Illuminate\Routing\Middleware\ThrottleRequests->handleRequest(Object(Illuminate\Http\Request), Object(Closure), Array)
#8 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(90): Illuminate\Routing\Middleware\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\Http\Request), Object(Closure), 'api.admin', Object(Closure))
#9 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Routing\Middleware\ThrottleRequests->handle(Object(Illuminate\Http\Request), Object(Closure), 'api.admin')
#10 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(63): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#11 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Auth\Middleware\Authenticate->handle(Object(Illuminate\Http\Request), Object(Closure), 'sanctum')
#12 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#13 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(821): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#14 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(800): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#15 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(764): Illuminate\Routing\Router->runRoute(Object(Illuminate\Http\Request), Object(Illuminate\Routing\Route))
#16 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(753): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#17 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#18 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Kernel->{closure:Illuminate\Foundation\Http\Kernel::dispatchToRouter():197}(Object(Illuminate\Http\Request))
#19 /Users/wesner/Herd/shop/vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php(19): Illuminate\Pipeline\Pipeline->{closure:Illuminate\Pipeline\Pipeline::prepareDestination():178}(Object(Illuminate\Http\Request))
#20 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Livewire\Features\SupportDisablingBackButtonCache\DisableBackButtonCacheMiddleware->handle(Object(Illuminate\Http\Request), Object(Closure))
#21 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#22 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#23 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull->handle(Object(Illuminate\Http\Request), Object(Closure))
#24 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#25 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#26 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\TrimStrings->handle(Object(Illuminate\Http\Request), Object(Closure))
#27 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#28 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\ValidatePostSize->handle(Object(Illuminate\Http\Request), Object(Closure))
#29 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(109): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#30 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance->handle(Object(Illuminate\Http\Request), Object(Closure))
#31 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(74): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#32 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\HandleCors->handle(Object(Illuminate\Http\Request), Object(Closure))
#33 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(58): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#34 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\TrustProxies->handle(Object(Illuminate\Http\Request), Object(Closure))
#35 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php(22): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#36 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks->handle(Object(Illuminate\Http\Request), Object(Closure))
#37 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php(26): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#38 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\ValidatePathEncoding->handle(Object(Illuminate\Http\Request), Object(Closure))
#39 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#40 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#41 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#42 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(607): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#43 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(573): Illuminate\Foundation\Testing\TestCase->call('POST', 'http://shop.tes...', Array, Array, Array, Array, '{"name":"Blocke...')
#44 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(411): Illuminate\Foundation\Testing\TestCase->json('POST', 'http://shop.tes...', Array, Array, 0)
#45 /Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php(59): Illuminate\Foundation\Testing\TestCase->postJson('http://shop.tes...', Array)
#46 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseMethodFactory.php(168): P\Tests\Feature\ApiTokenTest->{closure:/Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php:53}()
#47 [internal function]: P\Tests\Feature\ApiTokenTest->{closure:Pest\Factories\TestCaseMethodFactory::getClosure():158}()
#48 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(429): call_user_func_array(Object(Closure), Array)
#49 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Support/ExceptionTrace.php(26): P\Tests\Feature\ApiTokenTest->{closure:Pest\Concerns\Testable::__callClosure():429}()
#50 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(429): Pest\Support\ExceptionTrace::ensure(Object(Closure))
#51 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(331): P\Tests\Feature\ApiTokenTest->__callClosure(Object(Closure), Array)
#52 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()'d code(53): P\Tests\Feature\ApiTokenTest->__runTest(Object(Closure))
#53 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(1332): P\Tests\Feature\ApiTokenTest->__pest_evaluable_platform_management_is_restricted_to_platform_administrators()
#54 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(519): PHPUnit\Framework\TestCase->runTest()
#55 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(99): PHPUnit\Framework\TestCase->runBare()
#56 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(359): PHPUnit\Framework\TestRunner->run(Object(P\Tests\Feature\ApiTokenTest))
#57 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestSuite.php(374): PHPUnit\Framework\TestCase->run()
#58 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestSuite.php(374): PHPUnit\Framework\TestSuite->run()
#59 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\Framework\TestSuite->run()
#60 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/Application.php(229): PHPUnit\TextUI\TestRunner->run(Object(PHPUnit\TextUI\Configuration\Configuration), Object(PHPUnit\Runner\ResultCache\DefaultResultCache), Object(PHPUnit\Framework\TestSuite))
#61 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Kernel.php(103): PHPUnit\TextUI\Application->run(Array)
#62 /Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest(184): Pest\Kernel->handle(Array, Array)
#63 /Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest(192): {closure:/Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest:18}()
#64 {main}

----------------------------------------------------------------------------------

Undefined variable $user

  at tests/Feature/ApiTokenTest.php:60
     56▕     $token = $member->createToken('platform-attempt', ['manage-platform'])->plainTextToken;
     57▕
     58▕     $this->withToken($token)
     59▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Blocked Platform Org', 'billing_email' => 'blocked@example.test'])
  ➜  60▕         ->assertForbidden();
     61▕
     62▕     $platformToken = $this->admin->createToken('platform-manager', ['manage-platform'])->plainTextToken;
     63▕
     64▕     $this->withToken($platformToken)

  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\SearchAnalyticsWebhookTest > storefront search expo…
  Expected response status code [200] but received 500.
Failed asserting that 500 is identical to 200.

The following exception occurred during the last request:

ErrorException: Undefined array key "filters" in /Users/wesner/Herd/shop/app/Http/Controllers/Api/StorefrontSearchController.php:24
Stack trace:
#0 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php(258): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'Undefined array...', '/Users/wesner/H...', 24)
#1 /Users/wesner/Herd/shop/app/Http/Controllers/Api/StorefrontSearchController.php(24): Illuminate\Foundation\Bootstrap\HandleExceptions->{closure:Illuminate\Foundation\Bootstrap\HandleExceptions::forwardsTo():257}(2, 'Undefined array...', '/Users/wesner/H...', 24)
#2 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): App\Http\Controllers\Api\StorefrontSearchController->index(Object(Illuminate\Http\Request))
#3 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Route.php(265): Illuminate\Routing\ControllerDispatcher->dispatch(Object(Illuminate\Routing\Route), Object(App\Http\Controllers\Api\StorefrontSearchController), 'index')
#4 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Route.php(211): Illuminate\Routing\Route->runController()
#5 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(822): Illuminate\Routing\Route->run()
#6 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Routing\Router->{closure:Illuminate\Routing\Router::runRouteWithinStack():821}(Object(Illuminate\Http\Request))
#7 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\Pipeline\Pipeline->{closure:Illuminate\Pipeline\Pipeline::prepareDestination():178}(Object(Illuminate\Http\Request))
#8 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Routing\Middleware\SubstituteBindings->handle(Object(Illuminate\Http\Request), Object(Closure))
#9 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(166): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#10 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(129): Illuminate\Routing\Middleware\ThrottleRequests->handleRequest(Object(Illuminate\Http\Request), Object(Closure), Array)
#11 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(90): Illuminate\Routing\Middleware\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\Http\Request), Object(Closure), 'search', Object(Closure))
#12 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Routing\Middleware\ThrottleRequests->handle(Object(Illuminate\Http\Request), Object(Closure), 'search')
#13 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(166): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#14 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(129): Illuminate\Routing\Middleware\ThrottleRequests->handleRequest(Object(Illuminate\Http\Request), Object(Closure), Array)
#15 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(90): Illuminate\Routing\Middleware\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\Http\Request), Object(Closure), 'api.storefront', Object(Closure))
#16 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Routing\Middleware\ThrottleRequests->handle(Object(Illuminate\Http\Request), Object(Closure), 'api.storefront')
#17 /Users/wesner/Herd/shop/app/Http/Middleware/ResolveStore.php(65): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#18 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): App\Http\Middleware\ResolveStore->handle(Object(Illuminate\Http\Request), Object(Closure))
#19 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(120): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#20 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(63): Illuminate\Session\Middleware\StartSession->handleStatefulRequest(Object(Illuminate\Http\Request), Object(Illuminate\Session\EncryptedStore), Object(Closure))
#21 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#22 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#23 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(821): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#24 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(800): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#25 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(764): Illuminate\Routing\Router->runRoute(Object(Illuminate\Http\Request), Object(Illuminate\Routing\Route))
#26 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php(753): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#27 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#28 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(180): Illuminate\Foundation\Http\Kernel->{closure:Illuminate\Foundation\Http\Kernel::dispatchToRouter():197}(Object(Illuminate\Http\Request))
#29 /Users/wesner/Herd/shop/vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php(19): Illuminate\Pipeline\Pipeline->{closure:Illuminate\Pipeline\Pipeline::prepareDestination():178}(Object(Illuminate\Http\Request))
#30 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Livewire\Features\SupportDisablingBackButtonCache\DisableBackButtonCacheMiddleware->handle(Object(Illuminate\Http\Request), Object(Closure))
#31 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#32 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#33 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull->handle(Object(Illuminate\Http\Request), Object(Closure))
#34 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#35 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#36 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\TrimStrings->handle(Object(Illuminate\Http\Request), Object(Closure))
#37 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#38 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\ValidatePostSize->handle(Object(Illuminate\Http\Request), Object(Closure))
#39 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(109): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#40 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance->handle(Object(Illuminate\Http\Request), Object(Closure))
#41 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(74): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#42 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\HandleCors->handle(Object(Illuminate\Http\Request), Object(Closure))
#43 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(58): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#44 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\TrustProxies->handle(Object(Illuminate\Http\Request), Object(Closure))
#45 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php(22): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#46 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks->handle(Object(Illuminate\Http\Request), Object(Closure))
#47 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php(26): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#48 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(219): Illuminate\Http\Middleware\ValidatePathEncoding->handle(Object(Illuminate\Http\Request), Object(Closure))
#49 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(137): Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}(Object(Illuminate\Http\Request))
#50 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#51 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#52 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(607): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#53 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(573): Illuminate\Foundation\Testing\TestCase->call('GET', 'http://shop.tes...', Array, Array, Array, Array, '[]')
#54 /Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php(381): Illuminate\Foundation\Testing\TestCase->json('GET', 'http://shop.tes...', Array, Array, 0)
#55 /Users/wesner/Herd/shop/tests/Feature/SearchAnalyticsWebhookTest.php(38): Illuminate\Foundation\Testing\TestCase->getJson('http://shop.tes...')
#56 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseMethodFactory.php(168): P\Tests\Feature\SearchAnalyticsWebhookTest->{closure:/Users/wesner/Herd/shop/tests/Feature/SearchAnalyticsWebhookTest.php:37}()
#57 [internal function]: P\Tests\Feature\SearchAnalyticsWebhookTest->{closure:Pest\Factories\TestCaseMethodFactory::getClosure():158}()
#58 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(429): call_user_func_array(Object(Closure), Array)
#59 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Support/ExceptionTrace.php(26): P\Tests\Feature\SearchAnalyticsWebhookTest->{closure:Pest\Concerns\Testable::__callClosure():429}()
#60 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(429): Pest\Support\ExceptionTrace::ensure(Object(Closure))
#61 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php(331): P\Tests\Feature\SearchAnalyticsWebhookTest->__callClosure(Object(Closure), Array)
#62 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()'d code(26): P\Tests\Feature\SearchAnalyticsWebhookTest->__runTest(Object(Closure))
#63 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(1332): P\Tests\Feature\SearchAnalyticsWebhookTest->__pest_evaluable_storefront_search_exposes_the_documented_result_and_pagination_contract()
#64 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(519): PHPUnit\Framework\TestCase->runTest()
#65 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(99): PHPUnit\Framework\TestCase->runBare()
#66 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php(359): PHPUnit\Framework\TestRunner->run(Object(P\Tests\Feature\SearchAnalyticsWebhookTest))
#67 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestSuite.php(374): PHPUnit\Framework\TestCase->run()
#68 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestSuite.php(374): PHPUnit\Framework\TestSuite->run()
#69 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\Framework\TestSuite->run()
#70 /Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/Application.php(229): PHPUnit\TextUI\TestRunner->run(Object(PHPUnit\TextUI\Configuration\Configuration), Object(PHPUnit\Runner\ResultCache\DefaultResultCache), Object(PHPUnit\Framework\TestSuite))
#71 /Users/wesner/Herd/shop/vendor/pestphp/pest/src/Kernel.php(103): PHPUnit\TextUI\Application->run(Array)
#72 /Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest(184): Pest\Kernel->handle(Array, Array)
#73 /Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest(192): {closure:/Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest:18}()
#74 {main}

----------------------------------------------------------------------------------

Undefined array key "filters"

  at tests/Feature/SearchAnalyticsWebhookTest.php:39
     35▕ });
     36▕
     37▕ test('storefront search exposes the documented result and pagination contract', function (): void {
     38▕     $this->getJson('http://shop.test/api/storefront/v1/search?q=classic')
  ➜  39▕         ->assertOk()
     40▕         ->assertJsonPath('query', 'classic')
     41▕         ->assertJsonPath('results.0.handle', 'classic-cotton-t-shirt')
     42▕         ->assertJsonStructure(['results', 'facets', 'pagination.current_page', 'pagination.total']);
     43▕

  Tests:    2 failed, 18 passed (87 assertions)
  Duration: 3.46s


exec

status: completed

input:
const patch = "*** Begin Patch\n*** Update File: /Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php\n@@\n namespace App\\Http\\Middleware;\n \n+use App\\Models\\User;\n@@\n-        $ability = $this->abilityFor($request);\n+        $ability = $this->abilityFor($request, $user);\n@@\n-    private function abilityFor(Request $request): ?string\n+    private function abilityFor(Request $request, User $user): ?string\n*** Update File: /Users/wesner/Herd/shop/app/Http/Controllers/Api/StorefrontSearchController.php\n@@\n-        $filters = $data['filters'] === null ? [] : json_decode($data['filters'], true, 512, JSON_THROW_ON_ERROR);\n+        $filters = ($data['filters'] ?? null) === null ? [] : json_decode($data['filters'], true, 512, JSON_THROW_ON_ERROR);\n*** End Patch";
text(await tools.apply_patch(patch));


type: FileChange

type: update

unified_diff:
@@ -4,2 +4,3 @@

+use App\Models\User;
 use Closure;
@@ -17,3 +18,3 @@

-        $ability = $this->abilityFor($request);
+        $ability = $this->abilityFor($request, $user);

@@ -24,3 +25,3 @@

-    private function abilityFor(Request $request): ?string
+    private function abilityFor(Request $request, User $user): ?string
     {

unified_diff:
@@ -23,3 +23,3 @@
         ]);
-        $filters = $data['filters'] === null ? [] : json_decode($data['filters'], true, 512, JSON_THROW_ON_ERROR);
+        $filters = ($data['filters'] ?? null) === null ? [] : json_decode($data['filters'], true, 512, JSON_THROW_ON_ERROR);
         abort_unless(is_array($filters), 422, 'The filters parameter must be a JSON object.');

status: completed

stdout:
Success. Updated the following files:
M /Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php
M /Users/wesner/Herd/shop/app/Http/Controllers/Api/StorefrontSearchController.php


text:
Script completed
Wall time 0.0 seconds
Output:


status: completed

input:
const r = await tools.exec_command({
  cmd: "vendor/bin/pint --dirty --format agent && php artisan test --compact tests/Feature/ApiTokenTest.php tests/Feature/SearchAnalyticsWebhookTest.php tests/Feature/CommerceFlowTest.php",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms: 1000,
  max_output_tokens: 18000
});
text(r.output);
if (r.session_id) text((await tools.write_stdin({session_id:r.session_id, chars:"", yield_time_ms:1000, max_output_tokens:18000})).output);


type: CommandExecution

command: vendor/bin/pint --dirty --format agent && php artisan test --compact tests/Feature/ApiTokenTest.php tests/Feature/SearchAnalyticsWebhookTest.php tests/Feature/CommerceFlowTest.php

type: unknown

status: failed

stdout:
{"result":"pass"}
  ....⨯.⨯...........
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [201] but received 403.
Failed asserting that 403 is identical to 201.

  at tests/Feature/ApiTokenTest.php:66
     62▕     $platformToken = $this->admin->createToken('platform-manager', ['manage-platform'])->plainTextToken;
     63▕
     64▕     $this->withToken($platformToken)
     65▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])
  ➜  66▕         ->assertCreated();
     67▕ });
     68▕

  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\SearchAnalyticsWebhookTest > storefront search expo…
  Failed asserting that an array has the key 'pagination.current_page'.

  at tests/Feature/SearchAnalyticsWebhookTest.php:42
     38▕     $this->getJson('http://shop.test/api/storefront/v1/search?q=classic')
     39▕         ->assertOk()
     40▕         ->assertJsonPath('query', 'classic')
     41▕         ->assertJsonPath('results.0.handle', 'classic-cotton-t-shirt')
  ➜  42▕         ->assertJsonStructure(['results', 'facets', 'pagination.current_page', 'pagination.total']);
     43▕
     44▕     $this->getJson('http://shop.test/api/storefront/v1/search/suggest?q=c')
     45▕         ->assertOk()
     46▕         ->assertJsonPath('query', 'c')

  Tests:    2 failed, 16 passed (64 assertions)
  Duration: 3.04s


text:
Script completed
Wall time 3.5 seconds
Output:

text: {"result":"pass"}

text:
  ....⨯.⨯...........
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [201] but received 403.
Failed asserting that 403 is identical to 201.

  at tests/Feature/ApiTokenTest.php:66
     62▕     $platformToken = $this->admin->createToken('platform-manager', ['manage-platform'])->plainTextToken;
     63▕
     64▕     $this->withToken($platformToken)
     65▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])
  ➜  66▕         ->assertCreated();
     67▕ });
     68▕

  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\SearchAnalyticsWebhookTest > storefront search expo…
  Failed asserting that an array has the key 'pagination.current_page'.

  at tests/Feature/SearchAnalyticsWebhookTest.php:42
     38▕     $this->getJson('http://shop.test/api/storefront/v1/search?q=classic')
     39▕         ->assertOk()
     40▕         ->assertJsonPath('query', 'classic')
     41▕         ->assertJsonPath('results.0.handle', 'classic-cotton-t-shirt')
  ➜  42▕         ->assertJsonStructure(['results', 'facets', 'pagination.current_page', 'pagination.total']);
     43▕
     44▕     $this->getJson('http://shop.test/api/storefront/v1/search/suggest?q=c')
     45▕         ->assertOk()
     46▕         ->assertJsonPath('query', 'c')

  Tests:    2 failed, 16 passed (64 assertions)
  Duration: 3.04s


status: completed

input:
const r = await tools.exec_command({
  cmd: "sed -n '1,100p' database/seeders/UserSeeder.php; sed -n '1,100p' app/Http/Middleware/EnsureApiAbility.php; php artisan tinker --execute 'dump(App\\Models\\User::query()->where(\"email\",\"admin@acme.test\")->value(\"is_platform_admin\"));'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms: 1000,
  max_output_tokens: 10000
});
text(r.output);


command: sed -n '1,100p' database/seeders/UserSeeder.php; sed -n '1,100p' app/Http/Middleware/EnsureApiAbility.php; php artisan tinker --execute 'dump(App\Models\User::query()->where("email","admin@acme.test")->value("is_platform_admin"));'


text:
Script completed
Wall time 0.2 seconds
Output:

text:
<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        foreach ([
            ['email' => 'admin@acme.test', 'name' => 'Admin User', 'last_login_at' => now()],
            ['email' => 'staff@acme.test', 'name' => 'Staff User', 'last_login_at' => now()->subDays(2)],
            ['email' => 'support@acme.test', 'name' => 'Support User', 'last_login_at' => now()->subDay()],
            ['email' => 'manager@acme.test', 'name' => 'Store Manager', 'last_login_at' => now()->subDay()],
            ['email' => 'admin2@acme.test', 'name' => 'Admin Two', 'last_login_at' => now()->subDay()],
        ] as $user) {
            $password = Hash::make('password');

            User::query()->updateOrCreate(
                ['email' => $user['email']],
                ['name' => $user['name'], 'password' => $password, 'password_hash' => $password, 'status' => 'active', 'is_platform_admin' => $user['email'] === 'admin@acme.test', 'email_verified_at' => now(), 'last_login_at' => $user['last_login_at']],
            );
        }
    }
}
<?php

namespace App\Http\Middleware;

use App\Models\User;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;

class EnsureApiAbility
{
    public function handle(Request $request, Closure $next): Response
    {
        $user = $request->user('sanctum');

        abort_unless($request->bearerToken() !== null && $user !== null, 401, 'A Sanctum bearer token is required.');

        $ability = $this->abilityFor($request, $user);

        abort_unless($ability !== null && $user->tokenCan($ability), 403, 'This token does not have the required ability.');

        return $next($request);
    }

    private function abilityFor(Request $request, User $user): ?string
    {
        $path = $request->path();
        $method = $request->method();

        if (Str::contains($path, '/platform/')) {
            abort_unless($user->isPlatformAdmin(), 403, 'Platform administration is restricted to platform administrators.');

            return 'manage-platform';
        }

        if (Str::endsWith($path, '/invites')) {
            return 'manage-platform';
        }

        if (Str::contains($path, '/exports/')) {
            return 'read-orders';
        }

        foreach (['products' => 'products', 'collections' => 'collections', 'orders' => 'orders', 'customers' => 'customers', 'discounts' => 'discounts'] as $segment => $resource) {
            if (Str::contains($path, '/'.$segment)) {
                return in_array($method, ['GET', 'HEAD'], true) ? 'read-'.$resource : 'write-'.$resource;
            }
        }

        if (Str::contains($path, '/themes')) {
            return in_array($method, ['GET', 'HEAD'], true) ? 'read-themes' : 'write-themes';
        }

        if (Str::contains($path, '/pages')) {
            return in_array($method, ['GET', 'HEAD'], true) ? 'read-content' : 'write-content';
        }

        if (Str::contains($path, '/shipping') || Str::contains($path, '/tax/')) {
            return in_array($method, ['GET', 'HEAD'], true) ? 'read-settings' : 'write-settings';
        }

        if (Str::contains($path, '/analytics')) {
            return 'read-analytics';
        }

        if (Str::contains($path, '/search')) {
            return in_array($method, ['GET', 'HEAD'], true) ? 'read-products' : 'write-products';
        }

        return null;
    }
}
null // vendor/psy/psysh/src/ExecutionClosure.php(41) : eval()'d code:1


status: completed

input:
const r = await tools.exec_command({
  cmd: "rg -n \"isPlatformAdmin|is_platform_admin\" app/Models/User.php database/migrations/2026_08_21_003147* tests; sed -n '1,140p' database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php; php artisan migrate:status | tail -20",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:10000
});
text(r.output);


command: rg -n "isPlatformAdmin|is_platform_admin" app/Models/User.php database/migrations/2026_08_21_003147* tests; sed -n '1,140p' database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php; php artisan migrate:status | tail -20

stdout:
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:15:            if (! Schema::hasColumn('users', 'is_platform_admin')) {
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:16:                $table->boolean('is_platform_admin')->default(false)->index();
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:33:            if (Schema::hasColumn('users', 'is_platform_admin')) {
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:34:                $table->dropColumn('is_platform_admin');
app/Models/User.php:32:        'is_platform_admin',
app/Models/User.php:59:            'is_platform_admin' => 'boolean',
app/Models/User.php:89:    public function isPlatformAdmin(): bool
app/Models/User.php:91:        return (bool) $this->is_platform_admin;
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table): void {
            if (! Schema::hasColumn('users', 'is_platform_admin')) {
                $table->boolean('is_platform_admin')->default(false)->index();
            }
        });

        Schema::table('payments', function (Blueprint $table): void {
            if (! Schema::hasColumn('payments', 'raw_json')) {
                $table->text('raw_json')->nullable();
            }
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table): void {
            if (Schema::hasColumn('users', 'is_platform_admin')) {
                $table->dropColumn('is_platform_admin');
            }
        });

        Schema::table('payments', function (Blueprint $table): void {
            if (Schema::hasColumn('payments', 'raw_json')) {
                $table->dropColumn('raw_json');
            }
        });
    }
};
  0001_01_01_000002_create_jobs_table ................................ [1] Ran
  2025_08_14_170933_add_two_factor_columns_to_users_table ............ [1] Ran
  2026_08_20_000000_create_shop_schema ............................... [1] Ran
  2026_08_20_210057_add_enum_defaults_to_tenancy_tables .............. [1] Ran
  2026_08_20_210057_add_settings_json_to_store_settings_table ........ [1] Ran
  2026_08_20_210057_add_tls_mode_to_store_domains_table .............. [1] Ran
  2026_08_20_220000_add_search_and_auth_support ...................... [1] Ran
  2026_08_20_220001_add_webhook_delivery_status ...................... [1] Ran
  2026_08_20_220002_add_spec_compatibility_columns ................... [1] Ran
  2026_08_20_220003_add_discount_code_to_carts ....................... [1] Ran
  2026_08_20_223045_add_remaining_schema_contract_columns ............ [2] Ran
  2026_08_20_224526_add_line_allocations_to_refunds_table ............ [3] Ran
  2026_08_20_230144_add_remaining_spec_contract_fields ............... [4] Ran
  2026_08_20_231709_scope_customer_password_reset_tokens_by_store .... [5] Ran
  2026_08_20_234417_align_theme_settings_password_reset_and_webhook_contracts  [6] Ran
  2026_08_20_235245_remove_legacy_settings_from_themes_table ......... [7] Ran
  2026_08_21_000335_create_store_invitations_table ................... [8] Ran
  2026_08_21_002127_create_order_exports_table ....................... Pending
  2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns .. Pending


text:
Script completed
Wall time 0.2 seconds
Output:

text:
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:15:            if (! Schema::hasColumn('users', 'is_platform_admin')) {
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:16:                $table->boolean('is_platform_admin')->default(false)->index();
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:33:            if (Schema::hasColumn('users', 'is_platform_admin')) {
database/migrations/2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns.php:34:                $table->dropColumn('is_platform_admin');
app/Models/User.php:32:        'is_platform_admin',
app/Models/User.php:59:            'is_platform_admin' => 'boolean',
app/Models/User.php:89:    public function isPlatformAdmin(): bool
app/Models/User.php:91:        return (bool) $this->is_platform_admin;
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table): void {
            if (! Schema::hasColumn('users', 'is_platform_admin')) {
                $table->boolean('is_platform_admin')->default(false)->index();
            }
        });

        Schema::table('payments', function (Blueprint $table): void {
            if (! Schema::hasColumn('payments', 'raw_json')) {
                $table->text('raw_json')->nullable();
            }
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table): void {
            if (Schema::hasColumn('users', 'is_platform_admin')) {
                $table->dropColumn('is_platform_admin');
            }
        });

        Schema::table('payments', function (Blueprint $table): void {
            if (Schema::hasColumn('payments', 'raw_json')) {
                $table->dropColumn('raw_json');
            }
        });
    }
};
  0001_01_01_000002_create_jobs_table ................................ [1] Ran
  2025_08_14_170933_add_two_factor_columns_to_users_table ............ [1] Ran
  2026_08_20_000000_create_shop_schema ............................... [1] Ran
  2026_08_20_210057_add_enum_defaults_to_tenancy_tables .............. [1] Ran
  2026_08_20_210057_add_settings_json_to_store_settings_table ........ [1] Ran
  2026_08_20_210057_add_tls_mode_to_store_domains_table .............. [1] Ran
  2026_08_20_220000_add_search_and_auth_support ...................... [1] Ran
  2026_08_20_220001_add_webhook_delivery_status ...................... [1] Ran
  2026_08_20_220002_add_spec_compatibility_columns ................... [1] Ran
  2026_08_20_220003_add_discount_code_to_carts ....................... [1] Ran
  2026_08_20_223045_add_remaining_schema_contract_columns ............ [2] Ran
  2026_08_20_224526_add_line_allocations_to_refunds_table ............ [3] Ran
  2026_08_20_230144_add_remaining_spec_contract_fields ............... [4] Ran
  2026_08_20_231709_scope_customer_password_reset_tokens_by_store .... [5] Ran
  2026_08_20_234417_align_theme_settings_password_reset_and_webhook_contracts  [6] Ran
  2026_08_20_235245_remove_legacy_settings_from_themes_table ......... [7] Ran
  2026_08_21_000335_create_store_invitations_table ................... [8] Ran
  2026_08_21_002127_create_order_exports_table ....................... Pending
  2026_08_21_003147_add_platform_admin_and_payment_raw_json_columns .. Pending


status: completed

input:
const r = await tools.exec_command({
  cmd: "sed -n '1,160p' tests/Pest.php; sed -n '1,120p' app/Models/User.php; sed -n '1,120p' database/migrations/2026_08_20_000000_create_shop_schema.php | grep -n -A15 -B5 \"Schema::create('users'\"",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:14000
});
text(r.output);


command: sed -n '1,160p' tests/Pest.php; sed -n '1,120p' app/Models/User.php; sed -n '1,120p' database/migrations/2026_08_20_000000_create_shop_schema.php | grep -n -A15 -B5 "Schema::create('users'"

Pest.php

type: read

cmd: sed -n '1,160p' tests/Pest.php

User.php

cmd: sed -n '1,120p' app/Models/User.php

type: search

query: Schema::create('users'

stdout:
<?php

/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "pest()" function to bind a different classes or traits.
|
*/

pest()->extend(Tests\TestCase::class)
 // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
    ->in('Feature');

/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/

expect()->extend('toBeOne', function () {
    return $this->toBe(1);
});

/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/

function something()
{
    // ..
}
<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Database\Eloquent\Factories\HasFactory;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'password_hash',
        'status',
        'last_login_at',
        'is_platform_admin',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'password_hash',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'last_login_at' => 'datetime',
            'is_platform_admin' => 'boolean',
        ];
    }

    protected static function booted(): void
    {
        static::saving(function (User $user): void {
            if ($user->isDirty('password')) {
                $user->password_hash = $user->password;
            } elseif ($user->isDirty('password_hash')) {
                $user->password = $user->password_hash;
            }
        });
    }

    public function stores(): BelongsToMany
    {
        return $this->belongsToMany(Store::class, 'store_users')->using(StoreUser::class)->withPivot('role')->withTimestamps();
    }

    public function getAuthPasswordName(): string
    {
        return 'password_hash';
    }

    public function getAuthPassword(): ?string
    {
        return $this->password_hash;
    }

    public function isPlatformAdmin(): bool
    {
        return (bool) $this->is_platform_admin;
    }

    public function roleForStore(Store $store): ?StoreUserRole
    {
        $pivot = $this->stores()->where('stores.id', $store->getKey())->first()?->pivot;

        return $pivot?->role instanceof StoreUserRole ? $pivot->role : ($pivot?->role ? StoreUserRole::tryFrom($pivot->role) : null);
    }

    public function canManageStore(Store $store): bool
    {
        return in_array($this->roleForStore($store), [StoreUserRole::Owner, StoreUserRole::Admin], true);
    }

    /**
     * Get the user's initials
     */
    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->take(2)
            ->map(fn ($word) => Str::substr($word, 0, 1))
            ->implode('');
    }
}


text:
Script completed
Wall time 0.1 seconds
Output:

text:
<?php

/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "pest()" function to bind a different classes or traits.
|
*/

pest()->extend(Tests\TestCase::class)
 // ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
    ->in('Feature');

/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/

expect()->extend('toBeOne', function () {
    return $this->toBe(1);
});

/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/

function something()
{
    // ..
}
<?php

namespace App\Models;

use App\Enums\StoreUserRole;
use Illuminate\Database\Eloquent\Factories\HasFactory;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'password_hash',
        'status',
        'last_login_at',
        'is_platform_admin',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'password_hash',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'last_login_at' => 'datetime',
            'is_platform_admin' => 'boolean',
        ];
    }

    protected static function booted(): void
    {
        static::saving(function (User $user): void {
            if ($user->isDirty('password')) {
                $user->password_hash = $user->password;
            } elseif ($user->isDirty('password_hash')) {
                $user->password = $user->password_hash;
            }
        });
    }

    public function stores(): BelongsToMany
    {
        return $this->belongsToMany(Store::class, 'store_users')->using(StoreUser::class)->withPivot('role')->withTimestamps();
    }

    public function getAuthPasswordName(): string
    {
        return 'password_hash';
    }

    public function getAuthPassword(): ?string
    {
        return $this->password_hash;
    }

    public function isPlatformAdmin(): bool
    {
        return (bool) $this->is_platform_admin;
    }

    public function roleForStore(Store $store): ?StoreUserRole
    {
        $pivot = $this->stores()->where('stores.id', $store->getKey())->first()?->pivot;

        return $pivot?->role instanceof StoreUserRole ? $pivot->role : ($pivot?->role ? StoreUserRole::tryFrom($pivot->role) : null);
    }

    public function canManageStore(Store $store): bool
    {
        return in_array($this->roleForStore($store), [StoreUserRole::Owner, StoreUserRole::Admin], true);
    }

    /**
     * Get the user's initials
     */
    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->take(2)
            ->map(fn ($word) => Str::substr($word, 0, 1))
            ->implode('');
    }
}


exec

status: completed

input:
const r = await tools.exec_command({
  cmd: "php artisan migrate:fresh --seed --env=testing --no-interaction >/tmp/shop-test-seed.log && php artisan tinker --env=testing --execute 'dump(App\\\\Models\\\\User::query()->where(\"email\",\"admin@acme.test\")->first()?->only([\"email\",\"is_platform_admin\"]));'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:5000
});
text(r.output);
if (r.session_id) text((await tools.write_stdin({session_id:r.session_id, chars:"", yield_time_ms:1000, max_output_tokens:5000})).output);


command: php artisan migrate:fresh --seed --env=testing --no-interaction >/tmp/shop-test-seed.log && php artisan tinker --env=testing --execute 'dump(App\\Models\\User::query()->where("email","admin@acme.test")->first()?->only(["email","is_platform_admin"]));'

stdout:
In ParseErrorException.php line 44:

  PHP Parse error: Syntax error, unexpected T_NS_SEPARATOR, expecting ')' on
  line 1


text:
Script completed
Wall time 3.7 seconds
Output:

text:
In ParseErrorException.php line 44:

  PHP Parse error: Syntax error, unexpected T_NS_SEPARATOR, expecting ')' on
  line 1


status: completed

input:
const r = await tools.exec_command({
  cmd: "php artisan tinker --env=testing --execute 'dump(App\\Models\\User::query()->where(\"email\", \"admin@acme.test\")->first()?->only([\"email\", \"is_platform_admin\"]));'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:5000
});
text(r.output);


command: php artisan tinker --env=testing --execute 'dump(App\Models\User::query()->where("email", "admin@acme.test")->first()?->only(["email", "is_platform_admin"]));'

stdout:
array:2 [
  "email" => "admin@acme.test"
  "is_platform_admin" => true
] // vendor/psy/psysh/src/ExecutionClosure.php(41) : eval()'d code:1


text:
Script completed
Wall time 0.2 seconds
Output:

text:
array:2 [
  "email" => "admin@acme.test"
  "is_platform_admin" => true
] // vendor/psy/psysh/src/ExecutionClosure.php(41) : eval()'d code:1


status: completed

input:
const r = await tools.exec_command({
  cmd: "php artisan test --compact tests/Feature/ApiTokenTest.php --filter='platform management'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:12000
});
text(r.output);
if (r.session_id) text((await tools.write_stdin({session_id:r.session_id, chars:"", yield_time_ms:1000, max_output_tokens:12000})).output);


command: php artisan test --compact tests/Feature/ApiTokenTest.php --filter='platform management'

stdout:
  ⨯
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [201] but received 403.
Failed asserting that 403 is identical to 201.

  at tests/Feature/ApiTokenTest.php:66
     62▕     $platformToken = $this->admin->createToken('platform-manager', ['manage-platform'])->plainTextToken;
     63▕
     64▕     $this->withToken($platformToken)
     65▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])
  ➜  66▕         ->assertCreated();
     67▕ });
     68▕

  Tests:    1 failed (2 assertions)
  Duration: 0.32s


text:
Script completed
Wall time 0.5 seconds
Output:

text:
  ⨯
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [201] but received 403.
Failed asserting that 403 is identical to 201.

  at tests/Feature/ApiTokenTest.php:66
     62▕     $platformToken = $this->admin->createToken('platform-manager', ['manage-platform'])->plainTextToken;
     63▕
     64▕     $this->withToken($platformToken)
     65▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])
  ➜  66▕         ->assertCreated();
     67▕ });
     68▕

  Tests:    1 failed (2 assertions)
  Duration: 0.32s


status: completed

input:
const patch = "*** Begin Patch\n*** Update File: /Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php\n@@\n-    $this->withToken($platformToken)\n-        ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])\n-        ->assertCreated();\n+    $response = $this->withToken($platformToken)\n+        ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test']);\n+    dump($response->getContent(), $this->admin->fresh()->is_platform_admin, $this->admin->fresh()->tokens()->latest()->first()?->abilities);\n+    $response->assertCreated();\n*** End Patch";
text(await tools.apply_patch(patch));


unified_diff:
@@ -63,5 +63,6 @@

-    $this->withToken($platformToken)
-        ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test'])
-        ->assertCreated();
+    $response = $this->withToken($platformToken)
+        ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test']);
+    dump($response->getContent(), $this->admin->fresh()->is_platform_admin, $this->admin->fresh()->tokens()->latest()->first()?->abilities);
+    $response->assertCreated();
 });

stdout:
Success. Updated the following files:
M /Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php


text:
Script completed
Wall time 0.0 seconds
Output:


status: completed

input:
const r = await tools.exec_command({
  cmd: "php artisan test --compact tests/Feature/ApiTokenTest.php --filter='platform management'",
  workdir: "/Users/wesner/Herd/shop",
  yield_time_ms:1000,
  max_output_tokens:12000
});
text(r.output);
if (r.session_id) text((await tools.write_stdin({session_id:r.session_id, chars:"", yield_time_ms:1000, max_output_tokens:12000})).output);


cmd: php artisan test --compact tests/Feature/ApiTokenTest.php --filter='platform management'

stdout:
"""
{\n
    "message": "Platform administration is restricted to platform administrators.",\n
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException",\n
    "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",\n
    "line": 1440,\n
    "trace": [\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",\n
            "line": 67,\n
            "function": "abort",\n
            "class": "Illuminate\\Foundation\\Application",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",\n
            "line": 104,\n
            "function": "abort"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php",\n
            "line": 32,\n
            "function": "abort_unless"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/app/Http/Middleware/EnsureApiAbility.php",\n
            "line": 19,\n
            "function": "abilityFor",\n
            "class": "App\\Http\\Middleware\\EnsureApiAbility",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "App\\Http\\Middleware\\EnsureApiAbility",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",\n
            "line": 50,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",\n
            "line": 166,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",\n
            "line": 129,\n
            "function": "handleRequest",\n
            "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",\n
            "line": 90,\n
            "function": "handleRequestUsingNamedLimiter",\n
            "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",\n
            "line": 63,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Auth\\Middleware\\Authenticate",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 137,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php",\n
            "line": 821,\n
            "function": "then",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php",\n
            "line": 800,\n
            "function": "runRouteWithinStack",\n
            "class": "Illuminate\\Routing\\Router",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php",\n
            "line": 764,\n
            "function": "runRoute",\n
            "class": "Illuminate\\Routing\\Router",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Routing/Router.php",\n
            "line": 753,\n
            "function": "dispatchToRoute",\n
            "class": "Illuminate\\Routing\\Router",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",\n
            "line": 200,\n
            "function": "dispatch",\n
            "class": "Illuminate\\Routing\\Router",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 180,\n
            "function": "{closure:Illuminate\\Foundation\\Http\\Kernel::dispatchToRouter():197}",\n
            "class": "Illuminate\\Foundation\\Http\\Kernel",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php",\n
            "line": 19,\n
            "function": "{closure:Illuminate\\Pipeline\\Pipeline::prepareDestination():178}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Livewire\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",\n
            "line": 21,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",\n
            "line": 31,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",\n
            "line": 21,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",\n
            "line": 51,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php",\n
            "line": 27,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Http\\Middleware\\ValidatePostSize",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",\n
            "line": 109,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",\n
            "line": 74,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Http\\Middleware\\HandleCors",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",\n
            "line": 58,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Http\\Middleware\\TrustProxies",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php",\n
            "line": 22,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Middleware\\InvokeDeferredCallbacks",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php",\n
            "line": 26,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 219,\n
            "function": "handle",\n
            "class": "Illuminate\\Http\\Middleware\\ValidatePathEncoding",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",\n
            "line": 137,\n
            "function": "{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",\n
            "line": 175,\n
            "function": "then",\n
            "class": "Illuminate\\Pipeline\\Pipeline",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",\n
            "line": 144,\n
            "function": "sendRequestThroughRouter",\n
            "class": "Illuminate\\Foundation\\Http\\Kernel",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php",\n
            "line": 607,\n
            "function": "handle",\n
            "class": "Illuminate\\Foundation\\Http\\Kernel",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php",\n
            "line": 573,\n
            "function": "call",\n
            "class": "Illuminate\\Foundation\\Testing\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php",\n
            "line": 411,\n
            "function": "json",\n
            "class": "Illuminate\\Foundation\\Testing\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php",\n
            "line": 65,\n
            "function": "postJson",\n
            "class": "Illuminate\\Foundation\\Testing\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseMethodFactory.php",\n
            "line": 168,\n
            "function": "{closure:/Users/wesner/Herd/shop/tests/Feature/ApiTokenTest.php:53}",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "function": "{closure:Pest\\Factories\\TestCaseMethodFactory::getClosure():158}",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php",\n
            "line": 429,\n
            "function": "call_user_func_array"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Support/ExceptionTrace.php",\n
            "line": 26,\n
            "function": "{closure:Pest\\Concerns\\Testable::__callClosure():429}",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php",\n
            "line": 429,\n
            "function": "ensure",\n
            "class": "Pest\\Support\\ExceptionTrace",\n
            "type": "::"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Concerns/Testable.php",\n
            "line": 331,\n
            "function": "__callClosure",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Factories/TestCaseFactory.php(169) : eval()'d code",\n
            "line": 53,\n
            "function": "__runTest",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php",\n
            "line": 1332,\n
            "function": "__pest_evaluable_platform_management_is_restricted_to_platform_administrators",\n
            "class": "P\\Tests\\Feature\\ApiTokenTest",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php",\n
            "line": 519,\n
            "function": "runTest",\n
            "class": "PHPUnit\\Framework\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php",\n
            "line": 99,\n
            "function": "runBare",\n
            "class": "PHPUnit\\Framework\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestCase.php",\n
            "line": 359,\n
            "function": "run",\n
            "class": "PHPUnit\\Framework\\TestRunner",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/Framework/TestSuite.php",\n
            "line": 374,\n
            "function": "run",\n
            "class": "PHPUnit\\Framework\\TestCase",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/TestRunner.php",\n
            "line": 64,\n
            "function": "run",\n
            "class": "PHPUnit\\Framework\\TestSuite",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/phpunit/phpunit/src/TextUI/Application.php",\n
            "line": 229,\n
            "function": "run",\n
            "class": "PHPUnit\\TextUI\\TestRunner",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/src/Kernel.php",\n
            "line": 103,\n
            "function": "run",\n
            "class": "PHPUnit\\TextUI\\Application",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest",\n
            "line": 184,\n
            "function": "handle",\n
            "class": "Pest\\Kernel",\n
            "type": "->"\n
        },\n
        {\n
            "file": "/Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest",\n
            "line": 192,\n
            "function": "{closure:/Users/wesner/Herd/shop/vendor/pestphp/pest/bin/pest:18}"\n
        }\n
    ]\n
}
""" // tests/Feature/ApiTokenTest.php:66
true // tests/Feature/ApiTokenTest.php:66
array:1 [
  0 => "manage-platform"
] // tests/Feature/ApiTokenTest.php:66

  ⨯
  ────────────────────────────────────────────────────────────────────────────
   FAILED  Tests\Feature\ApiTokenTest > platform management is restricted to…
  Expected response status code [201] but received 403.
Failed asserting that 403 is identical to 201.

  at tests/Feature/ApiTokenTest.php:67
     63▕
     64▕     $response = $this->withToken($platformToken)
     65▕         ->postJson('http://shop.test/api/admin/v1/platform/organizations', ['name' => 'Allowed Platform Org', 'billing_email' => 'allowed@example.test']);
     66▕     dump($response->getContent(), $this->admin->fresh()->is_platform_admin, $this->admin->fresh()->tokens()->latest()->first()?->abilities);
  ➜  67▕     $response->assertCreated();
     68▕ });
     69▕

  Tests:    1 failed (2 assertions)
  Duration: 0.32s