moved roles to a separate database and added code to make updates
This commit is contained in:
parent
985f339725
commit
836ef8f1f6
14 changed files with 317 additions and 21 deletions
|
|
@ -6,6 +6,7 @@ use App\Http\Requests\LoginRequest;
|
||||||
use App\Http\Requests\PasswordResetRequest;
|
use App\Http\Requests\PasswordResetRequest;
|
||||||
use App\Http\Requests\RegisterRequest;
|
use App\Http\Requests\RegisterRequest;
|
||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
|
use App\Models\Role;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
@ -31,8 +32,13 @@ class AuthController extends Controller
|
||||||
->where('email', $user->email)
|
->where('email', $user->email)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$viewerRole = Role::query()->where('name', 'VIEWER')->value('id');
|
||||||
|
|
||||||
foreach ($invitations as $invitation) {
|
foreach ($invitations as $invitation) {
|
||||||
$user->sharedLists()->attach($invitation->movie_list_id);
|
$user->sharedLists()->attach(
|
||||||
|
$invitation->movie_list_id,
|
||||||
|
['role_id' => $viewerRole]
|
||||||
|
);
|
||||||
$invitation->update(['status' => 'accepted']);
|
$invitation->update(['status' => 'accepted']);
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use Illuminate\Http\Request;
|
||||||
|
|
||||||
class MovieController extends Controller
|
class MovieController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct(private MovieDbInterface $movieDb) {}
|
public function __construct() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
|
|
@ -60,6 +60,6 @@ class MovieController extends Controller
|
||||||
{
|
{
|
||||||
$movies = $movieDb->search($query, $request->input('options', []));
|
$movies = $movieDb->search($query, $request->input('options', []));
|
||||||
|
|
||||||
return response()->json(['results' => $movies]);
|
return response()->json(['data' => $movies]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use App\Http\Resources\MovieListResource;
|
||||||
use App\Interfaces\MovieDbInterface;
|
use App\Interfaces\MovieDbInterface;
|
||||||
use App\Models\Movie;
|
use App\Models\Movie;
|
||||||
use App\Models\MovieList;
|
use App\Models\MovieList;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -64,7 +66,7 @@ class MovieListController extends Controller
|
||||||
$validated = $request->validated();
|
$validated = $request->validated();
|
||||||
$movieList->update($validated);
|
$movieList->update($validated);
|
||||||
|
|
||||||
return MovieListResource::make($movieList);
|
return MovieListResource::make($movieList->load('movies', 'collaborators'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,16 +89,34 @@ class MovieListController extends Controller
|
||||||
$movieList->movies()->attach($movie);
|
$movieList->movies()->attach($movie);
|
||||||
$movieList->load('movies');
|
$movieList->load('movies');
|
||||||
|
|
||||||
return MovieListResource::make($movieList);
|
return MovieListResource::make($movieList->load('movies', 'collaborators'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeMovie(Request $request, MovieList $movieList, Movie $movie): MovieListResource
|
public function removeMovie(MovieList $movieList, Movie $movie): MovieListResource
|
||||||
{
|
{
|
||||||
$this->authorize('update', $movieList);
|
$this->authorize('update', $movieList);
|
||||||
|
|
||||||
$movieList->movies()->detach($movie);
|
$movieList->movies()->detach($movie);
|
||||||
$movieList->load('movies');
|
$movieList->load('movies');
|
||||||
|
|
||||||
return MovieListResource::make($movieList);
|
return MovieListResource::make($movieList->load('movies', 'collaborators'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateCollaboratorRole(Request $request, MovieList $movieList, User $collaborator): MovieListResource|JsonResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'role_id' => 'required|exists:roles,id',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$adminRole = Role::query()->where('name', 'ADMIN')->first()?->id;
|
||||||
|
if (Auth::id() !== $movieList->owner && ! Auth::user()->hasRole($movieList, $adminRole)) {
|
||||||
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$movieList->collaborators()->updateExistingPivot($collaborator->getKey(), [
|
||||||
|
'role_id' => $request->input('role_id'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return MovieListResource::make($movieList->load('movies', 'collaborators'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
app/Http/Controllers/RoleController.php
Normal file
15
app/Http/Controllers/RoleController.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Role;
|
||||||
|
|
||||||
|
class RoleController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$roles = Role::all();
|
||||||
|
|
||||||
|
return response()->json(['data' => $roles]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,10 +2,23 @@
|
||||||
|
|
||||||
namespace App\Http\Resources;
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Auth;
|
use Auth;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property bool $is_public
|
||||||
|
* @property int $owner
|
||||||
|
* @property User $listOwner
|
||||||
|
* @property Collection $collaborators
|
||||||
|
* @property Collection $movies
|
||||||
|
*
|
||||||
|
* @method string|null getUserRole(int $user_id)
|
||||||
|
*/
|
||||||
class MovieListResource extends JsonResource
|
class MovieListResource extends JsonResource
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,9 +36,10 @@ class MovieListResource extends JsonResource
|
||||||
'is_public' => $this->is_public,
|
'is_public' => $this->is_public,
|
||||||
'owner' => $this->listOwner->username,
|
'owner' => $this->listOwner->username,
|
||||||
'role' => $this->getRole($this->owner, $user_id),
|
'role' => $this->getRole($this->owner, $user_id),
|
||||||
'collaborators' => $this->whenLoaded('collaborators', fn () => $this->collaborators->map(fn ($user) => [
|
'collaborators' => $this->whenLoaded('collaborators', fn () => $this->collaborators->map(fn (User $user) => [
|
||||||
|
'id' => $user->getKey(),
|
||||||
'username' => $user->username,
|
'username' => $user->username,
|
||||||
'role' => $user->pivot->role,
|
'role' => $user->pivot->getAttribute('role_id'),
|
||||||
])),
|
])),
|
||||||
'movies' => $this->whenLoaded('movies'),
|
'movies' => $this->whenLoaded('movies'),
|
||||||
];
|
];
|
||||||
|
|
@ -34,7 +48,7 @@ class MovieListResource extends JsonResource
|
||||||
private function getRole(int $owner_id, int $user_id): ?string
|
private function getRole(int $owner_id, int $user_id): ?string
|
||||||
{
|
{
|
||||||
if ($owner_id === $user_id) {
|
if ($owner_id === $user_id) {
|
||||||
return 'owner';
|
return 'OWNER';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getUserRole($user_id);
|
return $this->getUserRole($user_id);
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,19 @@ class MovieList extends Model
|
||||||
return $this->belongsToMany(Movie::class);
|
return $this->belongsToMany(Movie::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserRole($userId)
|
public function getUserRole($userId): string
|
||||||
{
|
{
|
||||||
return $this->collaborators()
|
$roleId = $this->collaborators()
|
||||||
->where('user_id', $userId)
|
->where('user_id', $userId)
|
||||||
->first()
|
->first()
|
||||||
?->pivot
|
?->pivot->role_id;
|
||||||
->role;
|
|
||||||
|
return Role::query()->find($roleId)?->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function collaborators(): BelongsToMany
|
public function collaborators(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(User::class, 'movie_list_user')
|
return $this->belongsToMany(User::class, 'movie_list_user')
|
||||||
->withPivot('role');
|
->withPivot('role_id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
app/Models/Role.php
Normal file
11
app/Models/Role.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Role extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
}
|
||||||
|
|
@ -45,9 +45,19 @@ class User extends Authenticatable
|
||||||
return $this->hasMany(MovieList::class, 'owner');
|
return $this->hasMany(MovieList::class, 'owner');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasRole(MovieList $movieList, int $role): bool
|
||||||
|
{
|
||||||
|
return $this->sharedLists()
|
||||||
|
->wherePivot('movie_list_id', $movieList->id)
|
||||||
|
->wherePivot('role_id', $role)
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
public function sharedLists(): BelongsToMany
|
public function sharedLists(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(MovieList::class)->withPivot('role')->withTimestamps();
|
return $this->belongsToMany(MovieList::class)
|
||||||
|
->withPivot('role_id')
|
||||||
|
->withTimestamps();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
24
database/factories/RoleFactory.php
Normal file
24
database/factories/RoleFactory.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||||
|
*/
|
||||||
|
class RoleFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'ADMIN',
|
||||||
|
'display_name' => 'Administrator',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
30
database/migrations/2026_03_01_035158_create_roles_table.php
Normal file
30
database/migrations/2026_03_01_035158_create_roles_table.php
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('roles', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('display_name');
|
||||||
|
$table->string('name')->unique();
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('roles');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -15,7 +15,7 @@ return new class extends Migration
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->foreignId('movie_list_id')->constrained()->cascadeOnDelete();
|
$table->foreignId('movie_list_id')->constrained()->cascadeOnDelete();
|
||||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||||
$table->enum('role', ['viewer', 'editor', 'admin'])->default('viewer');
|
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
|
||||||
$table->unique(['movie_list_id', 'user_id']);
|
$table->unique(['movie_list_id', 'user_id']);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Role;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
@ -15,11 +16,27 @@ class DatabaseSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
// User::factory(10)->create();
|
if (config('app.env') === 'local') {
|
||||||
|
User::factory()->create([
|
||||||
|
'username' => 'testy_mctestface',
|
||||||
|
'email' => 'test@example.com',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
User::factory()->create([
|
Role::factory()->createMany([
|
||||||
'username' => 'testy_mctestface',
|
[
|
||||||
'email' => 'test@example.com',
|
'name' => 'ADMIN',
|
||||||
|
'display_name' => 'Administrator',
|
||||||
|
'description' => 'Can make any changes to the list including deleting it. Can also invite other users to collaborate on this list.'],
|
||||||
|
[
|
||||||
|
'name' => 'EDITOR',
|
||||||
|
'display_name' => 'Editor',
|
||||||
|
'description' => 'Can edit list details and can add/remove movies from the list.'],
|
||||||
|
[
|
||||||
|
'name' => 'VIEWER',
|
||||||
|
'display_name' => 'Viewer',
|
||||||
|
'description' => 'Can view the list, but cannot make any changes.',
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use App\Http\Controllers\AuthController;
|
||||||
use App\Http\Controllers\InvitationController;
|
use App\Http\Controllers\InvitationController;
|
||||||
use App\Http\Controllers\MovieController;
|
use App\Http\Controllers\MovieController;
|
||||||
use App\Http\Controllers\MovieListController;
|
use App\Http\Controllers\MovieListController;
|
||||||
|
use App\Http\Controllers\RoleController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
// Public auth routes
|
// Public auth routes
|
||||||
|
|
@ -32,4 +33,8 @@ Route::middleware('auth:sanctum')->group(function () {
|
||||||
Route::post('/movielists/{movieList}/movies', [MovieListController::class, 'addMovie'])->name('movielists.addMovie');
|
Route::post('/movielists/{movieList}/movies', [MovieListController::class, 'addMovie'])->name('movielists.addMovie');
|
||||||
Route::delete('/movielists/{movieList}/movies/{movie}', [MovieListController::class, 'removeMovie'])->name('movielists.removeMovie');
|
Route::delete('/movielists/{movieList}/movies/{movie}', [MovieListController::class, 'removeMovie'])->name('movielists.removeMovie');
|
||||||
Route::delete('/movielists/{movieList}', [MovieListController::class, 'destroy'])->name('movielists.destroy');
|
Route::delete('/movielists/{movieList}', [MovieListController::class, 'destroy'])->name('movielists.destroy');
|
||||||
|
Route::patch('/movielists/{movieList}/collaborators/{collaborator}', [MovieListController::class, 'updateCollaboratorRole'])->name('movielists.updateCollaboratorRole');
|
||||||
|
|
||||||
|
// Roles
|
||||||
|
Route::get('/roles', [RoleController::class, 'index'])->name('roles.index');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
143
tests/Feature/UpdateCollaboratorRoleTest.php
Normal file
143
tests/Feature/UpdateCollaboratorRoleTest.php
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\MovieList;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use Database\Seeders\DatabaseSeeder;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class UpdateCollaboratorRoleTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
private Role $adminRole;
|
||||||
|
private Role $editorRole;
|
||||||
|
private Role $viewerRole;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->seed(DatabaseSeeder::class);
|
||||||
|
|
||||||
|
$this->adminRole = Role::where('name', 'ADMIN')->first();
|
||||||
|
$this->editorRole = Role::where('name', 'EDITOR')->first();
|
||||||
|
$this->viewerRole = Role::where('name', 'VIEWER')->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeList(User $owner): MovieList
|
||||||
|
{
|
||||||
|
return MovieList::create([
|
||||||
|
'name' => 'Test List',
|
||||||
|
'owner' => $owner->getKey(),
|
||||||
|
'slug' => 'test-list',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_role_id_is_required(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($owner)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", []);
|
||||||
|
|
||||||
|
$response->assertUnprocessable()
|
||||||
|
->assertJsonValidationErrors(['role_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_role_id_must_exist_in_roles_table(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($owner)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", [
|
||||||
|
'role_id' => 9999,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertUnprocessable()
|
||||||
|
->assertJsonValidationErrors(['role_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_owner_can_update_collaborator_role(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($owner)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", [
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertOk();
|
||||||
|
$this->assertDatabaseHas('movie_list_user', [
|
||||||
|
'movie_list_id' => $movieList->getKey(),
|
||||||
|
'user_id' => $collaborator->getKey(),
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_collaborator_can_update_collaborator_role(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$admin = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($admin, ['role_id' => $this->adminRole->getKey()]);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($admin)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", [
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertOk();
|
||||||
|
$this->assertDatabaseHas('movie_list_user', [
|
||||||
|
'movie_list_id' => $movieList->getKey(),
|
||||||
|
'user_id' => $collaborator->getKey(),
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_admin_collaborator_cannot_update_collaborator_role(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$editor = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($editor, ['role_id' => $this->editorRole->getKey()]);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($editor)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", [
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_unrelated_user_cannot_update_collaborator_role(): void
|
||||||
|
{
|
||||||
|
$owner = User::factory()->create();
|
||||||
|
$collaborator = User::factory()->create();
|
||||||
|
$stranger = User::factory()->create();
|
||||||
|
$movieList = $this->makeList($owner);
|
||||||
|
$movieList->collaborators()->attach($collaborator, ['role_id' => $this->viewerRole->getKey()]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($stranger)
|
||||||
|
->patchJson("/api/movielists/{$movieList->getKey()}/collaborators/{$collaborator->getKey()}", [
|
||||||
|
'role_id' => $this->editorRole->getKey(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue