Добавлен REST API для мобильного приложения (этап 3, без Telegram)
Токен-аутентификация Sanctum (login/logout по устройствам), эндпоинты /api/v1 для зон/устройств/правил автоматизации с тем же RBAC (owner/viewer), что и в веб-версии — контроллеры переиспользуют существующие Policy и FormRequest. Устройства отдают живой статус из Device Shadow (Redis) и историю телеметрии из ClickHouse, плюс управление (turn-on/turn-off/set-level) через device-control-service.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Api\V1;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AuthTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_login_issues_a_token(): void
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'role' => UserRole::Owner,
|
||||
'password' => bcrypt('secret1234'),
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/api/v1/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'secret1234',
|
||||
'device_name' => 'iphone-15',
|
||||
]);
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJsonStructure(['token', 'user' => ['id', 'name', 'email', 'role']]);
|
||||
$this->assertDatabaseHas('personal_access_tokens', [
|
||||
'tokenable_id' => $user->id,
|
||||
'name' => 'iphone-15',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_login_rejects_wrong_password(): void
|
||||
{
|
||||
$user = User::factory()->create(['password' => bcrypt('secret1234')]);
|
||||
|
||||
$response = $this->postJson('/api/v1/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'wrong-password',
|
||||
'device_name' => 'iphone-15',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors('email');
|
||||
}
|
||||
|
||||
public function test_authenticated_token_can_reach_protected_route(): void
|
||||
{
|
||||
$user = User::factory()->create(['password' => bcrypt('secret1234')]);
|
||||
|
||||
$token = $this->postJson('/api/v1/login', [
|
||||
'email' => $user->email,
|
||||
'password' => 'secret1234',
|
||||
'device_name' => 'iphone-15',
|
||||
])->json('token');
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/v1/me')
|
||||
->assertOk()
|
||||
->assertJsonPath('data.email', $user->email);
|
||||
}
|
||||
|
||||
public function test_logout_revokes_the_current_token(): void
|
||||
{
|
||||
$user = User::factory()->create(['password' => bcrypt('secret1234')]);
|
||||
|
||||
$tokenModel = $user->createToken('iphone-15');
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$tokenModel->plainTextToken}")
|
||||
->postJson('/api/v1/logout')
|
||||
->assertNoContent();
|
||||
|
||||
$this->assertDatabaseMissing('personal_access_tokens', ['id' => $tokenModel->accessToken->id]);
|
||||
}
|
||||
|
||||
public function test_guest_cannot_reach_protected_route(): void
|
||||
{
|
||||
$this->getJson('/api/v1/me')->assertUnauthorized();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user