create(); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $this->assertTrue($investigation->isAccessibleBy($owner)); $this->assertTrue($investigation->isEditableBy($owner)); $this->assertTrue($owner->can('manageAccess', $investigation)); } public function test_unrelated_user_has_no_access(): void { $owner = User::factory()->create(); $stranger = User::factory()->create(); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $this->assertFalse($investigation->isAccessibleBy($stranger)); $this->assertFalse($stranger->can('view', $investigation)); } public function test_owner_can_grant_direct_viewer_access(): void { $owner = User::factory()->create(); $viewer = User::factory()->create(); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->users()->attach($viewer, ['role' => 'viewer']); $this->assertTrue($investigation->isAccessibleBy($viewer)); $this->assertFalse($investigation->isEditableBy($viewer)); $this->assertFalse($viewer->can('manageAccess', $investigation)); } public function test_owner_can_grant_direct_editor_access(): void { $owner = User::factory()->create(); $editor = User::factory()->create(); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->users()->attach($editor, ['role' => 'editor']); $this->assertTrue($investigation->isAccessibleBy($editor)); $this->assertTrue($investigation->isEditableBy($editor)); $this->assertTrue($editor->can('update', $investigation)); } public function test_group_grant_gives_access_to_all_members(): void { $owner = User::factory()->create(); $groupOwner = User::factory()->create(); $memberOne = User::factory()->create(); $memberTwo = User::factory()->create(); $group = Group::factory()->for($groupOwner, 'owner')->create(); $group->members()->attach([$memberOne->id, $memberTwo->id]); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->groups()->attach($group, ['role' => 'viewer']); $this->assertTrue($investigation->isAccessibleBy($memberOne)); $this->assertTrue($investigation->isAccessibleBy($memberTwo)); $this->assertFalse($investigation->isEditableBy($memberOne)); } public function test_direct_grant_wins_over_conflicting_group_grant(): void { $owner = User::factory()->create(); $groupOwner = User::factory()->create(); $user = User::factory()->create(); $group = Group::factory()->for($groupOwner, 'owner')->create(); $group->members()->attach($user->id); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->groups()->attach($group, ['role' => 'editor']); $investigation->users()->attach($user, ['role' => 'viewer']); $this->assertSame('viewer', $investigation->roleFor($user)); $this->assertFalse($investigation->isEditableBy($user)); } public function test_editor_role_wins_when_user_belongs_to_multiple_groups(): void { $owner = User::factory()->create(); $user = User::factory()->create(); $viewerGroup = Group::factory()->create(); $editorGroup = Group::factory()->create(); $viewerGroup->members()->attach($user->id); $editorGroup->members()->attach($user->id); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->groups()->attach($viewerGroup, ['role' => 'viewer']); $investigation->groups()->attach($editorGroup, ['role' => 'editor']); $this->assertSame('editor', $investigation->roleFor($user)); } public function test_only_owner_can_delete_investigation(): void { $owner = User::factory()->create(); $editor = User::factory()->create(); $investigation = Investigation::factory()->for($owner, 'owner')->create(); $investigation->users()->attach($editor, ['role' => 'editor']); $this->assertTrue($owner->can('delete', $investigation)); $this->assertFalse($editor->can('delete', $investigation)); } }