bdcb82f8f7
Task 0.3: guard 'sales' (driver sanctum, provider sales_users). Тест SalesGuardTest 3/3 (valid Bearer→200, без токена→401, мусор→401). Миграция personal_access_tokens (Sanctum Bearer; раньше не было — основной кабинет SPA cookie), DDL через pgsql_supplier, гранты crm_admin_user, CHANGELOG v8.60. Larastan baseline: +SalesGuardTest (Pest TestCall false-pos), SetTenantContext int→mixed (второй provider расширил тип $request->user()). План: admin-db ДО auth:sales. Один эскейп на сессию. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.7 KiB
PHP
63 lines
2.7 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Models\SalesUser;
|
||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Support\Facades\Route;
|
||
|
||
/**
|
||
* TDD: guard «sales» (Sanctum driver, provider sales_users).
|
||
*
|
||
* Проверяет, что:
|
||
* 1. Bearer-токен sales_user открывает доступ к маршруту auth:sales.
|
||
* 2. Запрос без токена получает 401.
|
||
*
|
||
* Примечание по кросс-модельной проверке:
|
||
* Стандартный tenant User (App\Models\User) в этом проекте НЕ использует
|
||
* HasApiTokens (SPA cookie-auth), поэтому createToken() на User недоступен.
|
||
* Кросс-модельная изоляция guard'а sales гарантируется архитектурно:
|
||
* Sanctum привязывает tokenable_type к модели в personal_access_tokens;
|
||
* guard с provider sales_users резолвит только записи, где tokenable_type =
|
||
* App\Models\SalesUser. Тест на «чужой токен» возможен между двумя
|
||
* разными SalesUser — разные пользователи корректно разрешаются (auth OK
|
||
* для обоих), но привязка к конкретному пользователю верна.
|
||
*
|
||
* Spec: docs/superpowers/plans/2026-06-30-sales-portal.md (Task 0.3)
|
||
*/
|
||
uses(DatabaseTransactions::class);
|
||
|
||
beforeEach(function () {
|
||
// Стаб-маршрут, защищённый guard'ом sales.
|
||
Route::middleware('auth:sales')->get('/__sales_ping', fn () => response()->json(['ok' => true]));
|
||
});
|
||
|
||
test('sales user с valid Bearer-токеном получает 200 на auth:sales маршруте', function () {
|
||
$salesUser = SalesUser::create([
|
||
'name' => 'Тест Менеджер',
|
||
'email' => 'sales-guard-test-'.uniqid().'@test.local',
|
||
'password' => Hash::make('test-secret-123'),
|
||
'role' => 'manager',
|
||
]);
|
||
|
||
$token = $salesUser->createToken('test')->plainTextToken;
|
||
|
||
$this->withHeader('Authorization', 'Bearer '.$token)
|
||
->getJson('/__sales_ping')
|
||
->assertOk()
|
||
->assertJson(['ok' => true]);
|
||
});
|
||
|
||
test('запрос без токена на auth:sales маршрут получает 401', function () {
|
||
$this->getJson('/__sales_ping')
|
||
->assertUnauthorized();
|
||
});
|
||
|
||
test('невалидный Bearer-токен на auth:sales маршруте получает 401', function () {
|
||
// Произвольная строка, которой нет в personal_access_tokens.
|
||
$this->withHeader('Authorization', 'Bearer '.str_repeat('x', 64))
|
||
->getJson('/__sales_ping')
|
||
->assertUnauthorized();
|
||
});
|