mirror of https://github.com/pixelfed/pixelfed
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
3.4 KiB
PHTML
94 lines
3.4 KiB
PHTML
|
1 week ago
|
<?php
|
||
|
|
|
||
|
|
use App\Models\User;
|
||
|
|
use App\Services\ActivityPubDeliveryService;
|
||
|
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||
|
|
use Illuminate\Http\Client\ConnectionException;
|
||
|
|
use Illuminate\Support\Facades\Cache;
|
||
|
|
use Illuminate\Support\Facades\Http;
|
||
|
|
|
||
|
|
uses(LazilyRefreshDatabase::class);
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| ActivityPubDeliveryService single-delivery transport failures
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
|
|
||
|
|
| queueDelivery() runs synchronously from follow/unfollow (via
|
||
|
|
| Helpers::sendSignedObject), which already commit local state before delivery
|
||
|
|
| and have no try/catch. A momentarily-unreachable remote raises a
|
||
|
|
| ConnectionException from Http::send(); that must be treated as best-effort
|
||
|
|
| (logged, host-health recorded) and NOT propagated to the caller, or the
|
||
|
|
| follow/unfollow request 500s after the DB mutation is committed. Other
|
||
|
|
| exception types (bad sender/destination) must still throw.
|
||
|
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Run a callback with the app environment temporarily set to production, since
|
||
|
|
* delivery is skipped outside production. Restores env afterwards.
|
||
|
|
*/
|
||
|
|
function deliverAsProduction(callable $fn): mixed
|
||
|
|
{
|
||
|
|
$app = app();
|
||
|
|
$previous = $app['env'];
|
||
|
|
$app['env'] = 'production';
|
||
|
|
|
||
|
|
try {
|
||
|
|
return $fn();
|
||
|
|
} finally {
|
||
|
|
$app['env'] = $previous;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
it('does not propagate a ConnectionException from a momentarily-unreachable remote', function () {
|
||
|
|
Http::fake(function () {
|
||
|
|
throw new ConnectionException('cURL error 7: Failed to connect to remote.example (Connection refused)');
|
||
|
|
});
|
||
|
|
|
||
|
|
$user = User::factory()->create();
|
||
|
|
$user->refresh();
|
||
|
|
$sender = $user->profile;
|
||
|
|
|
||
|
|
// Seed AFTER creating the user: model/factory setup and the lazy DB refresh
|
||
|
|
// can flush the cache store, which would wipe an earlier seed. Seed the DNS
|
||
|
|
// cache so validateDestination treats the host as publicly resolvable, and
|
||
|
|
// the banned-domains cache so the production ban check hits cache
|
||
|
|
// (validateUrl runs its ban check only in production).
|
||
|
|
Cache::put('helpers:url:public-ips:'.hash('xxh128', 'remote.example'), ['203.0.113.40'], 3600);
|
||
|
|
Cache::put('instances:banned:domains', [], 1209600);
|
||
|
|
|
||
|
|
$payload = [
|
||
|
|
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||
|
|
'id' => $sender->permalink('#follow/1'),
|
||
|
|
'type' => 'Follow',
|
||
|
|
'actor' => $sender->permalink(),
|
||
|
|
'object' => 'https://remote.example/users/target',
|
||
|
|
];
|
||
|
|
|
||
|
|
// Must complete without throwing (best-effort delivery).
|
||
|
|
deliverAsProduction(function () use ($sender, $payload) {
|
||
|
|
(new ActivityPubDeliveryService)
|
||
|
|
->from($sender)
|
||
|
|
->to('https://remote.example/users/target/inbox')
|
||
|
|
->payload($payload)
|
||
|
|
->send();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Reaching here without an exception is the assertion.
|
||
|
|
expect(true)->toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('still throws for a missing sender (non-transport error)', function () {
|
||
|
|
$user = User::factory()->create();
|
||
|
|
$user->refresh();
|
||
|
|
|
||
|
|
// No ->from(): validateSender/precondition path must still throw, unchanged.
|
||
|
|
expect(fn () => deliverAsProduction(function () {
|
||
|
|
(new ActivityPubDeliveryService)
|
||
|
|
->to('https://remote.example/users/target/inbox')
|
||
|
|
->payload(['type' => 'Follow'])
|
||
|
|
->send();
|
||
|
|
}))->toThrow(InvalidArgumentException::class);
|
||
|
|
});
|