trypost/tests/Feature/VerifyWorkspaceConnectionsTest.php
Paulo Castellano 56b8c92e72 refactor: settings redesign, Spanish translations, language system, strict_types
Settings pages:
- Redesign layout to match Sendkit (max-w-4xl, space-y-12, Separator sections)
- Merge Members page into Workspace settings with Table, invite Dialog, ConfirmDeleteModal
- Add workspace logo upload/delete routes and controller methods
- Translate all hardcoded strings in Workspace.vue modals

Language system:
- Drop languages table, replace language_id FK with locale string column on users
- Create config/languages.php for available languages and default locale
- Add Spanish (es) translations (13 files)
- Simplify HandleInertiaRequests, ProfileController, RegisteredUserController

Code quality:
- Add declare(strict_types=1) to all PHP files
- Fix MastodonPublisher using wrong attribute (filename -> original_filename)
- Fix HasMediaTest for new has_photo/photo_url accessors
- Fix PublishToSocialPlatformTest type error revealed by strict_types
- Remove orphaned Language model from AppServiceProvider morph map
- Update User TypeScript interface (has_photo, photo_url, locale)
- Eager load media relation on workspaces to prevent N+1
- Add 8 new tests for workspace logo upload/delete
- Update workspace settings test to assert members/invitations props

All 710 tests passing.
2026-03-30 00:20:43 -03:00

132 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Enums\SocialAccount\Status;
use App\Exceptions\TokenExpiredException;
use App\Jobs\VerifyWorkspaceConnections;
use App\Mail\WorkspaceConnectionsDisconnected;
use App\Models\SocialAccount;
use App\Models\Workspace;
use App\Services\Social\ConnectionVerifier;
use Illuminate\Support\Facades\Mail;
test('job does nothing when workspace has no connected accounts', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
VerifyWorkspaceConnections::dispatch($workspace);
Mail::assertNothingSent();
});
test('job does not send email when all connections are valid', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
SocialAccount::factory()->linkedin()->create(['workspace_id' => $workspace->id]);
SocialAccount::factory()->x()->create(['workspace_id' => $workspace->id]);
$verifier = mock(ConnectionVerifier::class);
$verifier->shouldReceive('verify')->andReturn(true);
app()->instance(ConnectionVerifier::class, $verifier);
VerifyWorkspaceConnections::dispatch($workspace);
Mail::assertNothingSent();
});
test('job marks account as disconnected and sends email when token is invalid', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
$account = SocialAccount::factory()->linkedin()->create(['workspace_id' => $workspace->id]);
$verifier = mock(ConnectionVerifier::class);
$verifier->shouldReceive('verify')
->andThrow(new TokenExpiredException('Token expired'));
app()->instance(ConnectionVerifier::class, $verifier);
VerifyWorkspaceConnections::dispatch($workspace);
expect($account->fresh()->status)->toBe(Status::Disconnected);
expect($account->fresh()->error_message)->toBe('Token expired');
Mail::assertQueued(WorkspaceConnectionsDisconnected::class, function ($mail) use ($workspace) {
return $mail->workspace->id === $workspace->id
&& $mail->disconnectedAccounts->count() === 1;
});
});
test('job sends single email with all disconnected accounts', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
$account1 = SocialAccount::factory()->linkedin()->create(['workspace_id' => $workspace->id]);
$account2 = SocialAccount::factory()->x()->create(['workspace_id' => $workspace->id]);
$verifier = mock(ConnectionVerifier::class);
$verifier->shouldReceive('verify')
->andThrow(new TokenExpiredException('Token expired'));
app()->instance(ConnectionVerifier::class, $verifier);
VerifyWorkspaceConnections::dispatch($workspace);
expect($account1->fresh()->status)->toBe(Status::Disconnected);
expect($account2->fresh()->status)->toBe(Status::Disconnected);
Mail::assertQueued(WorkspaceConnectionsDisconnected::class, function ($mail) use ($workspace) {
return $mail->workspace->id === $workspace->id
&& $mail->disconnectedAccounts->count() === 2;
});
Mail::assertQueuedCount(1);
});
test('job only includes failed accounts in email', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
$validAccount = SocialAccount::factory()->linkedin()->create(['workspace_id' => $workspace->id]);
$invalidAccount = SocialAccount::factory()->x()->create(['workspace_id' => $workspace->id]);
$verifier = mock(ConnectionVerifier::class);
$verifier->shouldReceive('verify')
->with(Mockery::on(fn ($acc) => $acc->id === $validAccount->id))
->andReturn(true);
$verifier->shouldReceive('verify')
->with(Mockery::on(fn ($acc) => $acc->id === $invalidAccount->id))
->andThrow(new TokenExpiredException('Token expired'));
app()->instance(ConnectionVerifier::class, $verifier);
VerifyWorkspaceConnections::dispatch($workspace);
expect($validAccount->fresh()->status)->toBe(Status::Connected);
expect($invalidAccount->fresh()->status)->toBe(Status::Disconnected);
Mail::assertQueued(WorkspaceConnectionsDisconnected::class, function ($mail) use ($invalidAccount) {
return $mail->disconnectedAccounts->count() === 1
&& $mail->disconnectedAccounts->first()->id === $invalidAccount->id;
});
});
test('job skips already disconnected accounts', function () {
Mail::fake();
$workspace = Workspace::factory()->create();
SocialAccount::factory()->linkedin()->disconnected()->create(['workspace_id' => $workspace->id]);
$verifier = mock(ConnectionVerifier::class);
$verifier->shouldNotReceive('verify');
app()->instance(ConnectionVerifier::class, $verifier);
VerifyWorkspaceConnections::dispatch($workspace);
Mail::assertNothingSent();
});