trypost/tests/Feature/Social/XControllerTest.php
Paulo Castellano 2fcf0a7e7a fix: update Socialite mocks in social controller tests
The mock shorthand Mockery::mock([...]) was not compatible with the
Socialite facade mock. Replaced with explicit shouldReceive chains
matching each controller's actual Socialite method calls.

Also keeps Google OAuth routes always registered so Wayfinder can
generate TypeScript helpers regardless of GOOGLE_AUTH_ENABLED setting.
2026-04-14 14:44:15 -03:00

172 lines
6 KiB
PHP

<?php
declare(strict_types=1);
use App\Enums\SocialAccount\Platform;
use App\Enums\SocialAccount\Status;
use App\Enums\UserWorkspace\Role;
use App\Models\SocialAccount;
use App\Models\User;
use App\Models\Workspace;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\Two\User as SocialiteUser;
beforeEach(function () {
$this->user = User::factory()->create();
$this->workspace = Workspace::factory()->create(['user_id' => $this->user->id]);
$this->user->update(['current_workspace_id' => $this->workspace->id]);
$this->workspace->members()->attach($this->user->id, ['role' => Role::Owner->value]);
});
test('x connect redirects to oauth provider', function () {
$driverMock = Mockery::mock();
$driverMock->shouldReceive('scopes')->andReturnSelf();
$driverMock->shouldReceive('redirect')->andReturn(Mockery::mock([
'getTargetUrl' => 'https://twitter.com/i/oauth2/authorize?test=1',
]));
Socialite::shouldReceive('driver')
->with('x')
->andReturn($driverMock);
$response = $this->actingAs($this->user)
->withHeader('X-Inertia', 'true')
->get(route('app.social.x.connect'));
$response->assertStatus(409); // Inertia::location returns 409 with X-Inertia header
expect(session('social_connect_workspace'))->toBe($this->workspace->id);
});
test('x oauth callback creates account', function () {
session([
'social_connect_workspace' => $this->workspace->id,
]);
$socialiteUser = Mockery::mock(SocialiteUser::class);
$socialiteUser->shouldReceive('getId')->andReturn('123456789');
$socialiteUser->shouldReceive('getNickname')->andReturn('testuser');
$socialiteUser->shouldReceive('getName')->andReturn('Test User');
$socialiteUser->shouldReceive('getAvatar')->andReturn(null);
$socialiteUser->token = 'test-access-token';
$socialiteUser->refreshToken = 'test-refresh-token';
$socialiteUser->expiresIn = 7200;
$socialiteUser->approvedScopes = ['tweet.read', 'tweet.write', 'users.read'];
Socialite::shouldReceive('driver')
->with('x')
->andReturn(Mockery::mock([
'user' => $socialiteUser,
]));
$response = $this->actingAs($this->user)->get(route('app.social.x.callback'));
$response->assertOk();
$response->assertViewIs('auth.social-callback');
$response->assertViewHas('success', true);
$this->assertDatabaseHas('social_accounts', [
'workspace_id' => $this->workspace->id,
'platform' => Platform::X->value,
'platform_user_id' => '123456789',
'username' => 'testuser',
'status' => Status::Connected->value,
]);
});
test('x callback fails with expired session', function () {
// No session data - simulating expired session
$response = $this->actingAs($this->user)->get(route('app.social.x.callback'));
$response->assertOk();
$response->assertViewHas('success', false);
$response->assertViewHas('message', 'Session expired. Please try again.');
});
test('user cannot connect x if already connected', function () {
SocialAccount::factory()->x()->create([
'workspace_id' => $this->workspace->id,
'platform_user_id' => '123456789',
]);
session([
'social_connect_workspace' => $this->workspace->id,
]);
$socialiteUser = Mockery::mock(SocialiteUser::class);
$socialiteUser->shouldReceive('getId')->andReturn('987654321');
$socialiteUser->shouldReceive('getNickname')->andReturn('anotheruser');
$socialiteUser->shouldReceive('getName')->andReturn('Another User');
$socialiteUser->shouldReceive('getAvatar')->andReturn(null);
$socialiteUser->token = 'new-access-token';
$socialiteUser->refreshToken = 'new-refresh-token';
$socialiteUser->expiresIn = 7200;
Socialite::shouldReceive('driver')
->with('x')
->andReturn(Mockery::mock([
'user' => $socialiteUser,
]));
$response = $this->actingAs($this->user)->get(route('app.social.x.callback'));
$response->assertOk();
$response->assertViewHas('success', false);
$response->assertViewHas('message', 'This platform is already connected.');
});
test('user can reconnect disconnected x account', function () {
$existingAccount = SocialAccount::factory()->x()->disconnected()->create([
'workspace_id' => $this->workspace->id,
'platform_user_id' => '123456789',
]);
session([
'social_connect_workspace' => $this->workspace->id,
]);
$socialiteUser = Mockery::mock(SocialiteUser::class);
$socialiteUser->shouldReceive('getId')->andReturn('123456789');
$socialiteUser->shouldReceive('getNickname')->andReturn('testuser');
$socialiteUser->shouldReceive('getName')->andReturn('Test User');
$socialiteUser->shouldReceive('getAvatar')->andReturn(null);
$socialiteUser->token = 'new-access-token';
$socialiteUser->refreshToken = 'new-refresh-token';
$socialiteUser->expiresIn = 7200;
$socialiteUser->approvedScopes = ['tweet.read', 'tweet.write'];
Socialite::shouldReceive('driver')
->with('x')
->andReturn(Mockery::mock([
'user' => $socialiteUser,
]));
$response = $this->actingAs($this->user)->get(route('app.social.x.callback'));
$response->assertOk();
$response->assertViewHas('success', true);
$existingAccount->refresh();
expect($existingAccount->status)->toBe(Status::Connected);
expect($existingAccount->access_token)->toBe('new-access-token');
});
test('x callback handles oauth errors gracefully', function () {
session([
'social_connect_workspace' => $this->workspace->id,
]);
$mock = Mockery::mock();
$mock->shouldReceive('user')->andThrow(new Exception('OAuth error'));
Socialite::shouldReceive('driver')
->with('x')
->andReturn($mock);
$response = $this->actingAs($this->user)->get(route('app.social.x.callback'));
$response->assertOk();
$response->assertViewHas('success', false);
$response->assertViewHas('message', 'Error connecting account. Please try again.');
});