Fix tests

pull/7202/head
Your Name 2 weeks ago
parent 289251985a
commit 888fa1bc30

@ -1,74 +1,65 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Database\Factories\ProfileFactory;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\DB;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class ComposeControllerTest extends TestCase
{
use LazilyRefreshDatabase;
uses(LazilyRefreshDatabase::class);
#[Test]
public function search_location_can_filter_by_country()
{
$user = User::factory()->create();
it('can filter location search results by country', function () {
$user = User::factory()->create();
$profile = ProfileFactory::new()->create([
'user_id' => $user->id,
]);
$profile = ProfileFactory::new()->create([
'user_id' => $user->id,
]);
$user->update([
'profile_id' => $profile->id,
]);
$user->update([
'profile_id' => $profile->id,
]);
$user->refresh();
$user->refresh();
DB::table('places')->insert([
[
'id' => 1,
'slug' => 'san-francisco-usa',
'name' => 'San Francisco',
'state' => 'California',
'country' => 'USA',
'aliases' => null,
'lat' => 37.7749,
'long' => -122.4194,
'score' => 100,
'created_at' => now(),
'updated_at' => now(),
],
[
'id' => 2,
'slug' => 'san-francisco-philippines',
'name' => 'San Francisco',
'state' => 'Cebu',
'country' => 'Philippines',
'aliases' => null,
'lat' => 10.3,
'long' => 123.9,
'score' => 50,
'created_at' => now(),
'updated_at' => now(),
],
]);
DB::table('places')->insert([
[
'id' => 1,
'slug' => 'san-francisco-usa',
'name' => 'San Francisco',
'state' => 'California',
'country' => 'USA',
'aliases' => null,
'lat' => 37.7749,
'long' => -122.4194,
'score' => 100,
'created_at' => now(),
'updated_at' => now(),
],
[
'id' => 2,
'slug' => 'san-francisco-philippines',
'name' => 'San Francisco',
'state' => 'Cebu',
'country' => 'Philippines',
'aliases' => null,
'lat' => 10.3,
'long' => 123.9,
'score' => 50,
'created_at' => now(),
'updated_at' => now(),
],
]);
$response = $this->actingAs($user, 'api')
->get('/api/v1.1/compose/search/location?q=San%20Francisco%2C%20USA');
$response = $this->actingAs($user, 'api')
->get('/api/v1.1/compose/search/location?q=San%20Francisco%2C%20USA');
$response->assertJsonCount(1);
$response->assertJsonCount(1);
$response->assertJsonFragment([
'name' => 'San Francisco',
'country' => 'USA',
]);
$response->assertJsonFragment([
'name' => 'San Francisco',
'country' => 'USA',
]);
$response->assertJsonMissing([
'country' => 'Philippines',
]);
}
}
$response->assertJsonMissing([
'country' => 'Philippines',
]);
});

@ -1,17 +1,7 @@
<?php
namespace Tests\Feature;
it('shows the login page', function () {
$response = $this->get('login');
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class LoginTest extends TestCase
{
#[Test]
public function view_login_page()
{
$response = $this->get('login');
$response->assertSee('Forgot Password');
}
}
$response->assertSee('Forgot Password');
});

@ -1,99 +1,82 @@
<?php
namespace Tests\Feature;
use App\Models\RemoteAuthInstance;
use App\Services\Account\RemoteAuthService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class RemoteAuthServiceTest extends TestCase
{
use LazilyRefreshDatabase;
uses(LazilyRefreshDatabase::class);
private function activeInstance(string $domain = 'mastodon.example'): RemoteAuthInstance
{
return RemoteAuthInstance::create([
'domain' => $domain,
'client_id' => 'cid',
'client_secret' => 'secret',
'redirect_uri' => url('/auth/mastodon/callback'),
'active' => true,
'banned' => false,
]);
}
function activeRemoteAuthInstance(string $domain = 'mastodon.example'): RemoteAuthInstance
{
return RemoteAuthInstance::create([
'domain' => $domain,
'client_id' => 'cid',
'client_secret' => 'secret',
'redirect_uri' => url('/auth/mastodon/callback'),
'active' => true,
'banned' => false,
]);
}
#[Test]
public function verify_credentials_returns_false_on_connection_failure()
{
$this->activeInstance();
it('returns false on connection failure when verifying credentials', function () {
activeRemoteAuthInstance();
Http::fake(function () {
throw new ConnectionException('timed out');
});
Http::fake(function () {
throw new ConnectionException('timed out');
});
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$this->assertFalse($res);
}
expect($res)->toBeFalse();
});
#[Test]
public function verify_credentials_returns_false_on_server_error()
{
$this->activeInstance();
it('returns false on server error when verifying credentials', function () {
activeRemoteAuthInstance();
Http::fake([
'*' => Http::response('nope', 500),
]);
Http::fake([
'*' => Http::response('nope', 500),
]);
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$this->assertFalse($res);
}
expect($res)->toBeFalse();
});
#[Test]
public function verify_credentials_returns_json_on_success()
{
$this->activeInstance();
it('returns json on success when verifying credentials', function () {
activeRemoteAuthInstance();
Http::fake([
'*' => Http::response(['acct' => 'alice', 'id' => '1'], 200),
]);
Http::fake([
'*' => Http::response(['acct' => 'alice', 'id' => '1'], 200),
]);
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token');
$this->assertIsArray($res);
$this->assertSame('alice', $res['acct']);
}
expect($res)->toBeArray();
expect($res['acct'])->toBe('alice');
});
#[Test]
public function get_following_returns_false_on_connection_failure()
{
$this->activeInstance();
it('returns false on connection failure when getting following', function () {
activeRemoteAuthInstance();
Http::fake(function () {
throw new ConnectionException('timed out');
});
Http::fake(function () {
throw new ConnectionException('timed out');
});
$res = RemoteAuthService::getFollowing('mastodon.example', 'token', 42);
$res = RemoteAuthService::getFollowing('mastodon.example', 'token', 42);
$this->assertFalse($res);
}
expect($res)->toBeFalse();
});
#[Test]
public function get_token_returns_false_on_connection_failure()
{
$this->activeInstance();
it('returns false on connection failure when getting a token', function () {
activeRemoteAuthInstance();
Http::fake(function () {
throw new ConnectionException('timed out');
});
Http::fake(function () {
throw new ConnectionException('timed out');
});
$res = RemoteAuthService::getToken('mastodon.example', 'code');
$res = RemoteAuthService::getToken('mastodon.example', 'code');
$this->assertFalse($res);
}
}
expect($res)->toBeFalse();
});

@ -1,7 +1,5 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\UserOidcMapping;
use App\Services\UserOidcService;
@ -10,101 +8,139 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use League\OAuth2\Client\Provider\GenericResourceOwner;
use League\OAuth2\Client\Token\AccessToken;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use Mockery\MockInterface;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class RemoteOidcTest extends TestCase
{
use LazilyRefreshDatabase;
use MockeryPHPUnitIntegration;
#[Test]
public function view_oidc_start()
{
config([
'remote-auth.oidc.enabled' => true,
'remote-auth.oidc.clientId' => 'fake',
'remote-auth.oidc.clientSecret' => 'fakeSecret',
'remote-auth.oidc.authorizeURL' => 'http://fakeserver.oidc/authorizeURL',
'remote-auth.oidc.tokenURL' => 'http://fakeserver.oidc/tokenURL',
'remote-auth.oidc.profileURL' => 'http://fakeserver.oidc/profile',
]);
$response = $this->withoutExceptionHandling()->get('auth/oidc/start');
$state = session()->get('oauth2state');
$callbackUrl = urlencode(url('auth/oidc/callback'));
$response->assertRedirect("http://fakeserver.oidc/authorizeURL?scope=openid%20profile%20email&state={$state}&response_type=code&approval_prompt=auto&redirect_uri={$callbackUrl}&client_id=fake");
}
// #[Test]
public function view_oidc_callback_new_user()
{
$originalUserCount = User::count();
$this->assertDatabaseCount('users', $originalUserCount);
uses(LazilyRefreshDatabase::class);
it('shows the oidc start redirect', function () {
config([
'remote-auth.oidc.enabled' => true,
'remote-auth.oidc.clientId' => 'fake',
'remote-auth.oidc.clientSecret' => 'fakeSecret',
'remote-auth.oidc.authorizeURL' => 'http://fakeserver.oidc/authorizeURL',
'remote-auth.oidc.tokenURL' => 'http://fakeserver.oidc/tokenURL',
'remote-auth.oidc.profileURL' => 'http://fakeserver.oidc/profile',
]);
$response = $this->withoutExceptionHandling()->get('auth/oidc/start');
$state = session()->get('oauth2state');
$callbackUrl = urlencode(url('auth/oidc/callback'));
$response->assertRedirect("http://fakeserver.oidc/authorizeURL?scope=openid%20profile%20email&state={$state}&response_type=code&approval_prompt=auto&redirect_uri={$callbackUrl}&client_id=fake");
});
// it('creates a new user from the oidc callback', function () {
// $originalUserCount = User::count();
// $this->assertDatabaseCount('users', $originalUserCount);
//
// config(['remote-auth.oidc.enabled' => true]);
//
// $oauthData = [
// 'sub' => Str::random(10),
// 'preferred_username' => fake()->unique()->userName,
// 'email' => fake()->unique()->freeEmail,
// ];
//
// $this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
// $mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(['access_token' => 'token']));
// $mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
//
// return $mock;
// });
//
// $response = $this->withoutExceptionHandling()->withSession([
// 'oauth2state' => 'abc123',
// ])->get('auth/oidc/callback?state=abc123&code=1');
//
// $response->assertRedirect('/');
//
// $mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
// $this->assertNotNull($mappedUser, 'mapping is found');
// $user = $mappedUser->user;
// $this->assertEquals($user->username, $oauthData['preferred_username']);
// $this->assertEquals($user->email, $oauthData['email']);
// $this->assertEquals(Auth::guard()->user()->id, $user->id);
//
// $this->assertDatabaseCount('users', $originalUserCount + 1);
// });
// it('maps the oidc callback to an existing user', function () {
// $user = User::create([
// 'name' => fake()->name,
// 'username' => fake()->unique()->username,
// 'email' => fake()->unique()->freeEmail,
// ]);
// $originalUserCount = User::count();
// $this->assertDatabaseCount('users', $originalUserCount);
//
// config(['remote-auth.oidc.enabled' => true]);
//
// $oauthData = [
// 'sub' => Str::random(10),
// 'preferred_username' => $user->username,
// 'email' => $user->email,
// ];
//
// UserOidcMapping::create([
// 'oidc_id' => $oauthData['sub'],
// 'user_id' => $user->id,
// ]);
//
// $this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
// $mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(['access_token' => 'token']));
// $mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
//
// return $mock;
// });
//
// $response = $this->withoutExceptionHandling()->withSession([
// 'oauth2state' => 'abc123',
// ])->get('auth/oidc/callback?state=abc123&code=1');
//
// $response->assertRedirect('/');
//
// $mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
// $this->assertNotNull($mappedUser, 'mapping is found');
// $user = $mappedUser->user;
// $this->assertEquals($user->username, $oauthData['preferred_username']);
// $this->assertEquals($user->email, $oauthData['email']);
// $this->assertEquals(Auth::guard()->user()->id, $user->id);
//
// $this->assertDatabaseCount('users', $originalUserCount);
// });
it('ensures a valid username from the oidc callback', function () {
config(['remote-auth.oidc.enabled' => true]);
config(['remote-auth.oidc.field_username' => 'preferred_username']);
$dataset = [
'john.doe@domain.com' => 'johndoe',
'test+user@part1@domain.com' => 'testuser',
'user!#$%^&*()_test' => 'user_test',
'jean-luc.picard' => 'jeanlucpicard',
'supercalifragilisticexpialidøcious@test.com' => 'supercalifragilisticexpialidci',
'hélène_renåud' => 'hlne_renud',
'123456789' => '123456789',
' user _ name ' => 'user_name',
'foo+bar@sub.domain.co.uk' => 'foobar',
];
foreach ($dataset as $input => $expected) {
Auth::logout();
session()->flush();
config(['remote-auth.oidc.enabled' => true]);
$originalUserCount = User::count();
$oauthData = [
'sub' => Str::random(10),
'preferred_username' => fake()->unique()->userName,
'email' => fake()->unique()->freeEmail,
];
$this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
$mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(['access_token' => 'token']));
$mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
return $mock;
});
$response = $this->withoutExceptionHandling()->withSession([
'oauth2state' => 'abc123',
])->get('auth/oidc/callback?state=abc123&code=1');
$response->assertRedirect('/');
$mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
$this->assertNotNull($mappedUser, 'mapping is found');
$user = $mappedUser->user;
$this->assertEquals($user->username, $oauthData['preferred_username']);
$this->assertEquals($user->email, $oauthData['email']);
$this->assertEquals(Auth::guard()->user()->id, $user->id);
$this->assertDatabaseCount('users', $originalUserCount + 1);
}
// #[Test]
public function view_oidc_callback_existing_user()
{
$user = User::create([
'name' => fake()->name,
'username' => fake()->unique()->username,
'preferred_username' => $input,
'email' => fake()->unique()->freeEmail,
]);
$originalUserCount = User::count();
$this->assertDatabaseCount('users', $originalUserCount);
config(['remote-auth.oidc.enabled' => true]);
$oauthData = [
'sub' => Str::random(10),
'preferred_username' => $user->username,
'email' => $user->email,
];
UserOidcMapping::create([
'oidc_id' => $oauthData['sub'],
'user_id' => $user->id,
]);
$this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
$mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(['access_token' => 'token']));
$mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
return $mock;
});
$response = $this->withoutExceptionHandling()->withSession([
@ -114,62 +150,9 @@ class RemoteOidcTest extends TestCase
$response->assertRedirect('/');
$mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
$this->assertNotNull($mappedUser, 'mapping is found');
$user = $mappedUser->user;
$this->assertEquals($user->username, $oauthData['preferred_username']);
$this->assertEquals($user->email, $oauthData['email']);
$this->assertEquals(Auth::guard()->user()->id, $user->id);
$this->assertDatabaseCount('users', $originalUserCount);
}
#[Test]
public function view_oidc_callback_ensure_valid_username()
{
config(['remote-auth.oidc.enabled' => true]);
config(['remote-auth.oidc.field_username' => 'preferred_username']);
$dataset = [
'john.doe@domain.com' => 'johndoe',
'test+user@part1@domain.com' => 'testuser',
'user!#$%^&*()_test' => 'user_test',
'jean-luc.picard' => 'jeanlucpicard',
'supercalifragilisticexpialidøcious@test.com' => 'supercalifragilisticexpialidci',
'hélène_renåud' => 'hlne_renud',
'123456789' => '123456789',
' user _ name ' => 'user_name',
'foo+bar@sub.domain.co.uk' => 'foobar',
];
foreach ($dataset as $input => $expected) {
Auth::logout();
session()->flush();
$originalUserCount = User::count();
$oauthData = [
'sub' => Str::random(10),
'name' => fake()->name,
'preferred_username' => $input,
'email' => fake()->unique()->freeEmail,
];
$this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
$mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(['access_token' => 'token']));
$mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
});
$response = $this->withoutExceptionHandling()->withSession([
'oauth2state' => 'abc123',
])->get('auth/oidc/callback?state=abc123&code=1');
$response->assertRedirect('/');
$mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
$this->assertNotNull($mappedUser, "Mapping not found for : {$input}");
$this->assertEquals($expected, $mappedUser->user->username, "Username not valid : {$input}");
$this->assertDatabaseCount('users', $originalUserCount + 1);
}
$this->assertNotNull($mappedUser, "Mapping not found for : {$input}");
$this->assertEquals($expected, $mappedUser->user->username, "Username not valid : {$input}");
$this->assertDatabaseCount('users', $originalUserCount + 1);
}
}
});

@ -1,12 +1,10 @@
<?php
namespace Tests\Feature;
use App\Models\Status;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\DB;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
@ -21,72 +19,63 @@ use Tests\TestCase;
|
*/
class SeasonalAggregationTest extends TestCase
/**
* Mirrors the aggregation used in SeasonalController::getData for
* average posts per profile.
*/
function averagePostsPerProfile(string $epochStart, string $epochEnd): float
{
use LazilyRefreshDatabase;
/**
* Mirrors the aggregation used in SeasonalController::getData for
* average posts per profile.
*/
private function averagePostsPerProfile(string $epochStart, string $epochEnd): float
{
return (float) DB::query()->fromSub(
Status::query()
->whereNull('uri')
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->where('created_at', '>', $epochStart)
->where('created_at', '<', $epochEnd)
->groupBy('profile_id')
->selectRaw('count(*) as count'),
'per_profile'
)->avg('count');
}
return (float) DB::query()->fromSub(
Status::query()
->whereNull('uri')
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->where('created_at', '>', $epochStart)
->where('created_at', '<', $epochEnd)
->groupBy('profile_id')
->selectRaw('count(*) as count'),
'per_profile'
)->avg('count');
}
#[Test]
public function it_averages_matching_posts_per_profile_in_sql()
{
$epochStart = '2020-01-01 00:00:00';
$epochEnd = '2020-12-31 23:59:59';
$inRange = '2020-06-01 12:00:00';
it('averages matching posts per profile in sql', function () {
$epochStart = '2020-01-01 00:00:00';
$epochEnd = '2020-12-31 23:59:59';
$inRange = '2020-06-01 12:00:00';
// Profile 1: 2 matching photos. Profile 2: 4 matching photos.
// Average per profile = 3.
Status::factory()->count(2)->photo()->create([
'profile_id' => 1001,
'created_at' => $inRange,
]);
Status::factory()->count(4)->photo()->create([
'profile_id' => 1002,
'created_at' => $inRange,
]);
// Profile 1: 2 matching photos. Profile 2: 4 matching photos.
// Average per profile = 3.
Status::factory()->count(2)->photo()->create([
'profile_id' => 1001,
'created_at' => $inRange,
]);
Status::factory()->count(4)->photo()->create([
'profile_id' => 1002,
'created_at' => $inRange,
]);
// Noise that must be excluded from the average:
// remote (uri set), wrong type, and out-of-range date.
Status::factory()->photo()->create([
'profile_id' => 1003,
'uri' => 'https://remote.example/statuses/1',
'created_at' => $inRange,
]);
Status::factory()->create([
'profile_id' => 1003,
'type' => 'text',
'created_at' => $inRange,
]);
Status::factory()->photo()->create([
'profile_id' => 1004,
'created_at' => '2019-01-01 00:00:00',
]);
// Noise that must be excluded from the average:
// remote (uri set), wrong type, and out-of-range date.
Status::factory()->photo()->create([
'profile_id' => 1003,
'uri' => 'https://remote.example/statuses/1',
'created_at' => $inRange,
]);
Status::factory()->create([
'profile_id' => 1003,
'type' => 'text',
'created_at' => $inRange,
]);
Status::factory()->photo()->create([
'profile_id' => 1004,
'created_at' => '2019-01-01 00:00:00',
]);
$this->assertSame(3.0, $this->averagePostsPerProfile($epochStart, $epochEnd));
}
expect(averagePostsPerProfile($epochStart, $epochEnd))->toBe(3.0);
});
#[Test]
public function it_returns_zero_when_no_posts_match()
{
$average = $this->averagePostsPerProfile('2020-01-01 00:00:00', '2020-12-31 23:59:59');
it('returns zero when no posts match', function () {
$average = averagePostsPerProfile('2020-01-01 00:00:00', '2020-12-31 23:59:59');
// No rows -> AVG returns null -> cast to 0.0
$this->assertSame(0.0, $average);
}
}
// No rows -> AVG returns null -> cast to 0.0
expect($average)->toBe(0.0);
});

@ -1,7 +1,5 @@
<?php
namespace Tests\Feature;
use App\Jobs\StatusPipeline\StatusDelete;
use App\Models\DirectMessage;
use App\Models\MediaTag;
@ -9,8 +7,8 @@ use App\Models\Notification;
use App\Models\Status;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
@ -25,110 +23,97 @@ use Tests\TestCase;
|
*/
class StatusDeleteCleanupTest extends TestCase
{
use LazilyRefreshDatabase;
#[Test]
public function deleting_a_status_removes_associated_dms_and_their_notifications()
{
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
$dm = new DirectMessage;
$dm->to_id = $user->profile_id;
$dm->from_id = $user->profile_id;
$dm->status_id = $status->id;
$dm->save();
$notification = Notification::create([
'profile_id' => $user->profile_id,
'actor_id' => $user->profile_id,
'action' => 'dm',
'item_type' => DirectMessage::class,
'item_id' => $dm->id,
]);
(new StatusDelete($status))->handle();
$this->assertNull(DirectMessage::find($dm->id));
$this->assertNull(Notification::find($notification->id));
$this->assertNull(Status::find($status->id));
}
#[Test]
public function deleting_a_status_removes_associated_media_tags_and_their_notifications()
{
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
$tag = MediaTag::create([
'status_id' => $status->id,
'media_id' => 1,
'profile_id' => $user->profile_id,
'tagged_username' => 'someone',
]);
$notification = Notification::create([
'profile_id' => $user->profile_id,
'actor_id' => $user->profile_id,
'action' => 'tagged',
'item_type' => MediaTag::class,
'item_id' => $tag->id,
]);
(new StatusDelete($status))->handle();
$this->assertNull(MediaTag::find($tag->id));
$this->assertNull(Notification::find($notification->id));
}
#[Test]
public function deleting_a_status_without_dms_or_tags_still_deletes_the_status()
{
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
(new StatusDelete($status))->handle();
$this->assertNull(Status::find($status->id));
}
#[Test]
public function deleting_a_status_cleans_up_even_when_the_owning_profile_is_soft_deleted()
{
// Mirrors the account-deletion flow on AP-enabled instances: the
// owning profile is soft-deleted before the queued StatusDelete runs.
config(['federation.activitypub.enabled' => true]);
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
// Soft-delete the owning profile, as DeleteAccountPipeline does.
$user->profile->delete();
(new StatusDelete($status))->handle();
$this->assertNull(Status::find($status->id));
}
}
it('removes associated dms and their notifications when a status is deleted', function () {
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
$dm = new DirectMessage;
$dm->to_id = $user->profile_id;
$dm->from_id = $user->profile_id;
$dm->status_id = $status->id;
$dm->save();
$notification = Notification::create([
'profile_id' => $user->profile_id,
'actor_id' => $user->profile_id,
'action' => 'dm',
'item_type' => DirectMessage::class,
'item_id' => $dm->id,
]);
(new StatusDelete($status))->handle();
expect(DirectMessage::find($dm->id))->toBeNull();
expect(Notification::find($notification->id))->toBeNull();
expect(Status::find($status->id))->toBeNull();
});
it('removes associated media tags and their notifications when a status is deleted', function () {
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
$tag = MediaTag::create([
'status_id' => $status->id,
'media_id' => 1,
'profile_id' => $user->profile_id,
'tagged_username' => 'someone',
]);
$notification = Notification::create([
'profile_id' => $user->profile_id,
'actor_id' => $user->profile_id,
'action' => 'tagged',
'item_type' => MediaTag::class,
'item_id' => $tag->id,
]);
(new StatusDelete($status))->handle();
expect(MediaTag::find($tag->id))->toBeNull();
expect(Notification::find($notification->id))->toBeNull();
});
it('still deletes a status without dms or tags', function () {
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
(new StatusDelete($status))->handle();
expect(Status::find($status->id))->toBeNull();
});
it('cleans up even when the owning profile is soft deleted', function () {
// Mirrors the account-deletion flow on AP-enabled instances: the
// owning profile is soft-deleted before the queued StatusDelete runs.
config(['federation.activitypub.enabled' => true]);
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
// Soft-delete the owning profile, as DeleteAccountPipeline does.
$user->profile->delete();
(new StatusDelete($status))->handle();
expect(Status::find($status->id))->toBeNull();
});

@ -1,5 +1,15 @@
<?php
test('that true is true', function () {
expect(true)->toBeTrue();
});
namespace Tests\Unit;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class ExampleTest extends TestCase
{
#[Test]
public function that_true_is_true()
{
$this->assertTrue(true);
}
}

Loading…
Cancel
Save