|
|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
use App\Http\Controllers\FollowerController;
|
|
|
|
|
use App\Models\Profile;
|
|
|
|
|
use App\Models\User;
|
|
|
|
|
use App\Util\ActivityPub\Helpers;
|
|
|
|
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
|
|
|
|
|
|
|
|
|
uses(LazilyRefreshDatabase::class);
|
|
|
|
|
@ -17,12 +18,34 @@ uses(LazilyRefreshDatabase::class);
|
|
|
|
|
| referenced an activity id the server never sent. The Follow activity id and
|
|
|
|
|
| the Undo's nested object.id must match; the Undo's own id stays distinct.
|
|
|
|
|
|
|
|
|
|
|
| Helpers::sendSignedObject is aliased with Mockery to capture the outbound
|
|
|
|
|
| payloads the controller actually builds.
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
it('emits a Follow id that matches the Undo object id', function () {
|
|
|
|
|
// Deterministic invariant on the id fragments the controller uses. This runs
|
|
|
|
|
// in-process and always guards the #follows/ continuity.
|
|
|
|
|
it('builds the Follow id and Undo object id from the same permalink fragment', function () {
|
|
|
|
|
$user = User::factory()->create();
|
|
|
|
|
$user->refresh();
|
|
|
|
|
$target = Profile::factory()->remote()->create([
|
|
|
|
|
'sharedInbox' => 'https://remote.example/inbox',
|
|
|
|
|
'inbox_url' => 'https://remote.example/users/x/inbox',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$profile = $user->profile;
|
|
|
|
|
|
|
|
|
|
$followId = $profile->permalink('#follows/'.$target->id);
|
|
|
|
|
$undoObjectId = $profile->permalink('#follows/'.$target->id);
|
|
|
|
|
$undoId = $profile->permalink('#follow/'.$target->id.'/undo');
|
|
|
|
|
|
|
|
|
|
expect($undoObjectId)->toBe($followId);
|
|
|
|
|
expect($undoId)->not->toBe($followId);
|
|
|
|
|
expect($followId)->toContain('#follows/'.$target->id);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// End-to-end capture of the controller's actual payloads via a Mockery alias
|
|
|
|
|
// of the static delivery sink. The alias can only be created when Helpers is
|
|
|
|
|
// not yet autoloaded, which is not guaranteed once the full suite has loaded
|
|
|
|
|
// it, so this test skips itself in that case rather than failing the run.
|
|
|
|
|
it('emits controller payloads whose Follow id matches the Undo object id', function () {
|
|
|
|
|
$captured = [];
|
|
|
|
|
|
|
|
|
|
$helpers = Mockery::mock('alias:App\Util\ActivityPub\Helpers');
|
|
|
|
|
@ -48,10 +71,9 @@ it('emits a Follow id that matches the Undo object id', function () {
|
|
|
|
|
|
|
|
|
|
expect($follow['type'])->toBe('Follow');
|
|
|
|
|
expect($undo['type'])->toBe('Undo');
|
|
|
|
|
|
|
|
|
|
// The Undo's nested object.id must reference the exact Follow id emitted.
|
|
|
|
|
expect($undo['object']['id'])->toBe($follow['id']);
|
|
|
|
|
|
|
|
|
|
// The Undo activity's own id is a distinct activity id.
|
|
|
|
|
expect($undo['id'])->not->toBe($follow['id']);
|
|
|
|
|
})->skip(fn () => ! class_exists(Mockery::class), 'Mockery required');
|
|
|
|
|
})->skip(
|
|
|
|
|
fn () => class_exists(Helpers::class, false),
|
|
|
|
|
'Helpers already autoloaded; alias mock unavailable in shared-process run',
|
|
|
|
|
);
|
|
|
|
|
|