tests:adds testing to portfolio access policy

This commit is contained in:
hackerESQ
2024-10-22 22:24:39 -05:00
parent 5a04c33f13
commit d53d1a3ed3
8 changed files with 287 additions and 5 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ class PortfolioPolicy
{ {
$pivot = $portfolio->users()->where('user_id', $user->id)->first(); $pivot = $portfolio->users()->where('user_id', $user->id)->first();
return $pivot; return !!$pivot;
} }
/** /**
+1 -1
View File
@@ -358,7 +358,7 @@
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.", "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.",
"Hey again!": "Hey again!", "Hey again!": "Hey again!",
"Before you can get started with Investbrain, you'll want to create a password:": "Before you can get started with Investbrain, you'll want to create a password:", "Before you can get started with Investbrain, let's complete your profile:": "Before you can get started with Investbrain, let's complete your profile:",
"Or login with SSO:": "Or login with SSO:", "Or login with SSO:": "Or login with SSO:",
"Create Password": "Create Password" "Create Password": "Create Password"
+1 -1
View File
@@ -358,7 +358,7 @@
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente.", "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente.",
"Hey again!": "¡Oye de nuevo!", "Hey again!": "¡Oye de nuevo!",
"Before you can get started with Investbrain, you'll want to create a password:": "Antes de poder comenzar a utilizar Investbrain, deberá crear una cuenta:", "Before you can get started with Investbrain, let's complete your profile:": "Antes de poder comenzar a utilizar Investbrain, deberá crear una cuenta:",
"Or login with SSO:": "O iniciar sesión mediante SSO:", "Or login with SSO:": "O iniciar sesión mediante SSO:",
"Create Password": "Crear Contraseña" "Create Password": "Crear Contraseña"
} }
@@ -7,7 +7,7 @@
</x-slot:logo> </x-slot:logo>
<h1 class="text-2xl font-bold mb-4">{{ __('Hey again!') }} 👋</h1> <h1 class="text-2xl font-bold mb-4">{{ __('Hey again!') }} 👋</h1>
<p class="mb-2">{{ __('Before you can get started with Investbrain, you\'ll want to create a password:') }}</p> <p class="mb-2">{{ __('Before you can get started with Investbrain, let\'s complete your profile:') }}</p>
@livewire('invited-onboarding-form', [ @livewire('invited-onboarding-form', [
'portfolio' => $portfolio, 'portfolio' => $portfolio,
@@ -126,6 +126,7 @@ new class extends Component {
</label> </label>
<div class="border-primary border rounded-sm px-2 py-5 mb-2"> <div class="border-primary border rounded-sm px-2 py-5 mb-2">
@if ($portfolio->owner)
<x-list-item <x-list-item
:item="$portfolio->owner" :item="$portfolio->owner"
avatar="profile_photo_url" avatar="profile_photo_url"
@@ -145,6 +146,7 @@ new class extends Component {
{{ __('Owner') }} {{ __('Owner') }}
</x-slot:sub-value> </x-slot:sub-value>
</x-list-item> </x-list-item>
@endif
@foreach (collect($this->portfolio?->users)->where('pivot.owner', '!=', 1) as $user) @foreach (collect($this->portfolio?->users)->where('pivot.owner', '!=', 1) as $user)
<x-list-item <x-list-item
+1 -1
View File
@@ -47,4 +47,4 @@ Route::get('/privacy', [PrivacyPolicyController::class, 'show'])->name('policy.s
Route::get('auth/verify/{connected_account}', [ConnectedAccountController::class, 'verify'])->name('oauth.verify_connected_account'); Route::get('auth/verify/{connected_account}', [ConnectedAccountController::class, 'verify'])->name('oauth.verify_connected_account');
Route::get('auth/{provider}', [ConnectedAccountController::class, 'redirectToProvider'])->name('oauth.redirect'); Route::get('auth/{provider}', [ConnectedAccountController::class, 'redirectToProvider'])->name('oauth.redirect');
Route::get('auth/{provider}/callback', [ConnectedAccountController::class, 'handleProviderCallback']); Route::get('auth/{provider}/callback', [ConnectedAccountController::class, 'handleProviderCallback'])->name('oauth.callback');
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace Tests;
use Tests\TestCase;
use App\Models\User;
use App\Models\ConnectedAccount;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Http\Controllers\ConnectedAccountController;
class ConnectedAccountTest extends TestCase
{
use RefreshDatabase;
protected $controller;
protected function setUp(): void
{
parent::setUp();
$this->controller = new ConnectedAccountController();
}
public function test_handle_provider_callback_with_already_connected_account()
{
$provider = 'github';
config(['services.enabled_login_providers' => 'github,google']);
// Create a user and a connected account for the provider
$user = User::create([
'name' => 'Alice Smith',
'email' => 'alice@example.com',
'email_verified_at' => now(),
]);
$providerUser = (object)[
'id' => '67890',
'name' => 'Alice Smith',
'email' => 'alice@example.com',
'token' => '15932t8',
'tokenSecret' => null,
'refreshToken' => null,
'expiresIn' => null,
];
ConnectedAccount::forceCreate([
'provider' => $provider,
'provider_id' => $providerUser->id,
'user_id' => $user->id,
'token' => $providerUser->token,
'verified_at' => now()
]);
Socialite::shouldReceive('driver')
->with($provider)
->andReturnSelf()
->shouldReceive('user')
->andReturn($providerUser);
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
$this->assertTrue(Auth::check());
$this->assertEquals($user->id, Auth::id());
$response->assertRedirect(route('dashboard'));
}
public function test_handle_provider_callback_with_new_user()
{
$provider = 'github';
config(['services.enabled_login_providers' => 'github,google']);
$providerUser = (object)[
'id' => '12345',
'name' => 'John Doe',
'email' => 'john@example.com',
'token' => 'token',
'tokenSecret' => null,
'refreshToken' => null,
'expiresIn' => null,
];
Socialite::shouldReceive('driver')
->with($provider)
->andReturnSelf()
->shouldReceive('user')
->andReturn($providerUser);
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
$user = User::where('email', 'john@example.com')->first();
$this->assertNotNull($user);
$this->assertEquals('John Doe', $user->name);
$connectedAccount = ConnectedAccount::first();
$this->assertNotNull($connectedAccount);
$this->assertEquals('github', $connectedAccount->provider);
$this->assertEquals('12345', $connectedAccount->provider_id);
$this->assertNotNull($connectedAccount->verified_at);
$this->assertTrue(Auth::check());
$response->assertRedirect(route('dashboard'));
}
public function test_handle_provider_callback_with_existing_account()
{
$provider = 'github';
config(['services.enabled_login_providers' => 'github,google']);
User::create([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'email_verified_at' => now(),
]);
$providerUser = (object)[
'id' => '54321',
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'token' => 'token',
'tokenSecret' => null,
'refreshToken' => null,
'expiresIn' => null,
];
Socialite::shouldReceive('driver')
->with($provider)
->andReturnSelf()
->shouldReceive('user')
->andReturn($providerUser);
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
$connectedAccount = ConnectedAccount::first();
$this->assertNotNull($connectedAccount);
$this->assertEquals('github', $connectedAccount->provider);
$this->assertEquals('54321', $connectedAccount->provider_id);
$this->assertNull($connectedAccount->verified_at);
$response->assertRedirect(route('login'));
$response->assertSessionHas('status', 'Account already exists. Check your email to connect your GitHub account.');
}
public function test_verify_connected_account()
{
$user = User::create([
'name' => 'Alice Smith',
'email' => 'alice@example.com',
'email_verified_at' => null,
]);
$connectedAccount = ConnectedAccount::forceCreate([
'provider' => 'github',
'provider_id' => '12345',
'token' => '0283523',
'user_id' => $user->id,
'verified_at' => null,
]);
$this->assertNull($connectedAccount->verified_at);
$response = $this->get(route('oauth.verify_connected_account', ['connected_account' => $connectedAccount->id]));
$connectedAccount->refresh();
$this->assertNotNull($connectedAccount->verified_at);
$this->assertNotNull($connectedAccount->user);
$response->assertRedirect(route('dashboard'));
$response->assertSessionHas('toast');
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
namespace Tests;
use Tests\TestCase;
use App\Models\User;
use App\Models\Portfolio;
use App\Policies\PortfolioPolicy;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
class PortfolioPolicyTest extends TestCase
{
use RefreshDatabase;
protected $policy;
protected $user;
protected $portfolio;
protected function setUp(): void
{
parent::setUp();
$this->policy = new PortfolioPolicy();
$this->user = User::factory()->create();
Auth::login($this->user);
$this->portfolio = Portfolio::factory()->create();
// Attach the users to the portfolio
$this->portfolio->users()->syncWithoutDetaching([
$this->user->id => [
'full_access' => false,
'owner' => false,
]
]);
}
public function test_stranger_access_viaweb()
{
$user = User::factory()->create();
$result = $this->actingAs($user)->get(route('portfolio.show', ['portfolio' => $this->portfolio]));
$result->assertStatus(403);
}
public function test_stranger_access_via_policy()
{
$user = User::factory()->create();
$result = $this->policy->readOnly($user, $this->portfolio);
$this->assertFalse($result, 'User should not have readonly access');
$result = $this->policy->fullAccess($user, $this->portfolio);
$this->assertFalse($result, 'User should not have full access');
$result = $this->policy->owner($user, $this->portfolio);
$this->assertFalse($result, 'User should not have owner access');
}
public function test_read_only_policy()
{
$result = $this->policy->readOnly($this->user, $this->portfolio);
$this->assertTrue($result, 'User should have read-only access');
}
public function test_read_only_via_web()
{
$result = $this->actingAs($this->user)->get(route('portfolio.show', ['portfolio' => $this->portfolio]));
$result->assertStatus(200);
}
public function test_full_access_policy_with_full_access()
{
// Update pivot table to give full access
$this->portfolio->users()->updateExistingPivot($this->user->id, [
'full_access' => true,
]);
$result = $this->policy->fullAccess($this->user, $this->portfolio);
$this->assertTrue($result, 'User should have full access');
}
public function test_full_access_policy_without_full_access()
{
// Check that the user doesn't have full access
$result = $this->policy->fullAccess($this->user, $this->portfolio);
$this->assertFalse($result, 'User should not have full access');
}
public function test_owner_policy_when_user_is_owner()
{
// Update pivot table to make the user the owner
$this->portfolio->users()->updateExistingPivot($this->user->id, [
'owner' => true,
]);
$result = $this->policy->owner($this->user, $this->portfolio);
$this->assertTrue($result, 'User should be the owner');
}
public function test_owner_policy_when_user_is_not_owner()
{
// Check that the user is not the owner
$result = $this->policy->owner($this->user, $this->portfolio);
$this->assertFalse($result, 'User should not be the owner');
}
}