168 lines
6.9 KiB
PHP
168 lines
6.9 KiB
PHP
<?php
|
|
|
|
use App\Models\Group;
|
|
use App\Models\GroupMember;
|
|
use App\Models\User;
|
|
use App\Policies\GroupPolicy;
|
|
use App\Services\GroupMembershipService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function attachGroupMember(Group $group, User $user, string $role): GroupMember
|
|
{
|
|
return GroupMember::query()->create([
|
|
'group_id' => $group->id,
|
|
'user_id' => $user->id,
|
|
'invited_by_user_id' => $group->owner_user_id,
|
|
'role' => $role,
|
|
'status' => Group::STATUS_ACTIVE,
|
|
'invited_at' => now(),
|
|
'accepted_at' => now(),
|
|
]);
|
|
}
|
|
|
|
it('allows contributors into studio but not management or publishing policy actions', function () {
|
|
$owner = User::factory()->create();
|
|
$contributor = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $contributor, Group::ROLE_MEMBER);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->viewStudio($contributor, $group))->toBeTrue()
|
|
->and($policy->update($contributor, $group))->toBeFalse()
|
|
->and($policy->manageMembers($contributor, $group))->toBeFalse()
|
|
->and($policy->publishArtworks($contributor, $group))->toBeFalse()
|
|
->and($policy->manageCollections($contributor, $group))->toBeFalse();
|
|
});
|
|
|
|
it('allows editors to publish artworks and manage collections without member administration', function () {
|
|
$owner = User::factory()->create();
|
|
$editor = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $editor, Group::ROLE_EDITOR);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->viewStudio($editor, $group))->toBeTrue()
|
|
->and($policy->publishArtworks($editor, $group))->toBeTrue()
|
|
->and($policy->manageCollections($editor, $group))->toBeTrue()
|
|
->and($policy->manageMembers($editor, $group))->toBeFalse()
|
|
->and($policy->archive($editor, $group))->toBeFalse();
|
|
});
|
|
|
|
it('allows admins to manage group settings and members but not archive ownership actions', function () {
|
|
$owner = User::factory()->create();
|
|
$admin = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $admin, Group::ROLE_ADMIN);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->viewStudio($admin, $group))->toBeTrue()
|
|
->and($policy->update($admin, $group))->toBeTrue()
|
|
->and($policy->manageMembers($admin, $group))->toBeTrue()
|
|
->and($policy->publishArtworks($admin, $group))->toBeTrue()
|
|
->and($policy->archive($admin, $group))->toBeFalse();
|
|
});
|
|
|
|
it('blocks studio access for suspended groups even for active non-owner members', function () {
|
|
$owner = User::factory()->create();
|
|
$editor = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create([
|
|
'status' => Group::LIFECYCLE_SUSPENDED,
|
|
]);
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $editor, Group::ROLE_EDITOR);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->viewStudio($editor, $group))->toBeFalse()
|
|
->and($policy->publishArtworks($editor, $group))->toBeFalse()
|
|
->and($policy->manageCollections($editor, $group))->toBeFalse();
|
|
});
|
|
|
|
it('keeps archive authority with the owner', function () {
|
|
$owner = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->update($owner, $group))->toBeTrue()
|
|
->and($policy->manageMembers($owner, $group))->toBeTrue()
|
|
->and($policy->publishArtworks($owner, $group))->toBeTrue()
|
|
->and($policy->archive($owner, $group))->toBeTrue();
|
|
});
|
|
|
|
it('does not allow admins or editors to transfer ownership through policy update access alone', function () {
|
|
$owner = User::factory()->create();
|
|
$admin = User::factory()->create();
|
|
$editor = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $admin, Group::ROLE_ADMIN);
|
|
attachGroupMember($group, $editor, Group::ROLE_EDITOR);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->update($admin, $group))->toBeTrue()
|
|
->and($policy->archive($admin, $group))->toBeFalse()
|
|
->and($policy->manageMembers($editor, $group))->toBeFalse();
|
|
});
|
|
|
|
it('exposes explicit v3 event and private access policy hooks', function () {
|
|
$owner = User::factory()->create();
|
|
$editor = User::factory()->create();
|
|
$member = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $editor, Group::ROLE_EDITOR);
|
|
attachGroupMember($group, $member, Group::ROLE_MEMBER);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->publishEventUpdates($editor, $group))->toBeTrue()
|
|
->and($policy->viewInternalEvents($editor, $group))->toBeTrue()
|
|
->and($policy->viewPrivateProject($editor, $group))->toBeTrue()
|
|
->and($policy->participateInChallenge($member, $group))->toBeTrue()
|
|
->and($policy->publishEventUpdates($member, $group))->toBeFalse();
|
|
});
|
|
|
|
it('exposes explicit v4 release, milestone, badge, and trust policy hooks', function () {
|
|
$owner = User::factory()->create();
|
|
$admin = User::factory()->create();
|
|
$editor = User::factory()->create();
|
|
$member = User::factory()->create();
|
|
$group = Group::factory()->for($owner, 'owner')->create();
|
|
|
|
app(GroupMembershipService::class)->ensureOwnerMembership($group);
|
|
attachGroupMember($group, $admin, Group::ROLE_ADMIN);
|
|
attachGroupMember($group, $editor, Group::ROLE_EDITOR);
|
|
attachGroupMember($group, $member, Group::ROLE_MEMBER);
|
|
|
|
$policy = app(GroupPolicy::class);
|
|
|
|
expect($policy->manageReleases($editor, $group))->toBeTrue()
|
|
->and($policy->publishReleases($editor, $group))->toBeTrue()
|
|
->and($policy->moveReleaseStage($editor, $group))->toBeTrue()
|
|
->and($policy->manageMilestones($editor, $group))->toBeTrue()
|
|
->and($policy->assignReleaseLead($editor, $group))->toBeTrue()
|
|
->and($policy->viewReputationDashboard($editor, $group))->toBeFalse()
|
|
->and($policy->manageBadges($admin, $group))->toBeTrue()
|
|
->and($policy->viewInternalTrustMetrics($admin, $group))->toBeTrue()
|
|
->and($policy->featureRelease($admin, $group))->toBeTrue()
|
|
->and($policy->manageReleases($member, $group))->toBeFalse()
|
|
->and($policy->viewReputationDashboard($member, $group))->toBeFalse();
|
|
}); |