Use scopes #18
@@ -30,7 +30,7 @@ class EventConductor extends Conductor
|
||||
public function scope(Builder $builder)
|
||||
{
|
||||
$user = auth()->user();
|
||||
if ($user === null || $user->has_permission('admin/events') === false) {
|
||||
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||
$builder
|
||||
->where('status', '!=', 'draft')
|
||||
->where('publish_at', '<=', now());
|
||||
@@ -47,7 +47,7 @@ class EventConductor extends Conductor
|
||||
{
|
||||
if (strtolower($model->status) === 'draft' || Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||
$user = auth()->user();
|
||||
if ($user === null || $user->has_permission('admin/events') === false) {
|
||||
if ($user === null || $user->hasPermission('admin/events') === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class EventConductor extends Conductor
|
||||
public static function creatable()
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/events') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,18 +75,18 @@ class EventConductor extends Conductor
|
||||
public static function updatable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/events') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the current model is deletable.
|
||||
* Return if the current model is destroyable.
|
||||
*
|
||||
* @param Model $model The model.
|
||||
* @return boolean Allow deleting model.
|
||||
*/
|
||||
public static function deletable(Model $model)
|
||||
public static function destroyable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/events') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/events') === true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class MediaConductor extends Conductor
|
||||
{
|
||||
if ($model->permission !== null) {
|
||||
$user = auth()->user();
|
||||
if ($user === null || $user->has_permission($model->permission) === false) {
|
||||
if ($user === null || $user->hasPermission($model->permission) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -92,18 +92,18 @@ class MediaConductor extends Conductor
|
||||
public static function updatable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && (strcasecmp($model->user_id, $user->id) === 0 || $user->has_permission('admin/media') === true));
|
||||
return ($user !== null && (strcasecmp($model->user_id, $user->id) === 0 || $user->hasPermission('admin/media') === true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the current model is deletable.
|
||||
* Return if the current model is destroyable.
|
||||
*
|
||||
* @param Model $model The model.
|
||||
* @return boolean Allow deleting model.
|
||||
*/
|
||||
public static function deletable(Model $model)
|
||||
public static function destroyable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && ($model->user_id === $user->id || $user->has_permission('admin/media') === true));
|
||||
return ($user !== null && ($model->user_id === $user->id || $user->hasPermission('admin/media') === true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class PostConductor extends Conductor
|
||||
public function scope(Builder $builder)
|
||||
{
|
||||
$user = auth()->user();
|
||||
if ($user === null || $user->has_permission('admin/posts') === false) {
|
||||
if ($user === null || $user->hasPermission('admin/posts') === false) {
|
||||
$builder
|
||||
->where('publish_at', '<=', now());
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class PostConductor extends Conductor
|
||||
{
|
||||
if (Carbon::parse($model->publish_at)->isFuture() === true) {
|
||||
$user = auth()->user();
|
||||
if ($user === null || $user->has_permission('admin/posts') === false) {
|
||||
if ($user === null || $user->hasPermission('admin/posts') === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class PostConductor extends Conductor
|
||||
public static function creatable()
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/posts') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,18 +74,18 @@ class PostConductor extends Conductor
|
||||
public static function updatable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/posts') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the current model is deletable.
|
||||
* Return if the current model is destroyable.
|
||||
*
|
||||
* @param Model $model The model.
|
||||
* @return boolean Allow deleting model.
|
||||
*/
|
||||
public static function deletable(Model $model)
|
||||
public static function destroyable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && $user->has_permission('admin/posts') === true);
|
||||
return ($user !== null && $user->hasPermission('admin/posts') === true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,18 @@ class SubscriptionConductor extends Conductor
|
||||
public static function updatable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->has_permission('admin/subscriptions') === true));
|
||||
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->hasPermission('admin/subscriptions') === true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the current model is deletable.
|
||||
* Return if the current model is destroyable.
|
||||
*
|
||||
* @param Model $model The model.
|
||||
* @return boolean Allow deleting model.
|
||||
*/
|
||||
public static function deletable(Model $model)
|
||||
public static function destroyable(Model $model)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->has_permission('admin/subscriptions') === true));
|
||||
return ($user !== null && ((strcasecmp($model->email, $user->email) === 0 && $user->email_verified_at !== null) || $user->hasPermission('admin/subscriptions') === true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class UserConductor extends Conductor
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the current model is deletable.
|
||||
* Return if the current model is destroyable.
|
||||
*
|
||||
* @param Model $model The model.
|
||||
* @return boolean Allow deleting model.
|
||||
|
||||
@@ -121,13 +121,15 @@ class ApiController extends Controller
|
||||
/**
|
||||
* Return resource data
|
||||
*
|
||||
* @param array|Model|Collection $data Resource data.
|
||||
* @param array|null $appendData Data to append to response.
|
||||
* @param integer $respondCode Resource code.
|
||||
* @param array|Model|Collection $data Resource data.
|
||||
* @param boolean $isCollection If the data is a group of items.
|
||||
* @param array|null $appendData Data to append to response.
|
||||
* @param integer $respondCode Resource code.
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function respondAsResource(
|
||||
mixed $data,
|
||||
bool $isCollection = false,
|
||||
mixed $appendData = null,
|
||||
int $respondCode = HttpResponseCodes::HTTP_OK
|
||||
) {
|
||||
@@ -144,8 +146,6 @@ class ApiController extends Controller
|
||||
$resourceName = strtolower($resourceName);
|
||||
}
|
||||
|
||||
$is_multiple = true;
|
||||
|
||||
$dataArray = [];
|
||||
if ($data instanceof Collection) {
|
||||
$dataArray = $data->toArray();
|
||||
@@ -157,7 +157,7 @@ class ApiController extends Controller
|
||||
}
|
||||
|
||||
$resource = [];
|
||||
if ($is_multiple === true) {
|
||||
if ($isCollection === true) {
|
||||
$resource = [Str::plural($resourceName) => $dataArray];
|
||||
} else {
|
||||
$resource = [Str::singular($resourceName) => $dataArray];
|
||||
|
||||
@@ -73,6 +73,7 @@ class AuthController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$user->makeVisible(['permissions']),
|
||||
false,
|
||||
['token' => $token]
|
||||
);
|
||||
}//end if
|
||||
|
||||
@@ -31,6 +31,7 @@ class EventController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
@@ -63,6 +64,7 @@ class EventController extends ApiController
|
||||
$event = Event::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
EventConductor::model($request, $event),
|
||||
false,
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
|
||||
@@ -33,6 +33,7 @@ class MediaController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
@@ -103,6 +104,7 @@ class MediaController extends ApiController
|
||||
$media = $request->user()->media()->create($request->all());
|
||||
return $this->respondAsResource(
|
||||
MediaConductor::model($request, $media),
|
||||
false,
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
|
||||
@@ -35,6 +35,7 @@ class PostController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
@@ -67,6 +68,7 @@ class PostController extends ApiController
|
||||
$post = Post::create($request->all());
|
||||
return $this->respondAsResource(
|
||||
PostConductor::model($request, $post),
|
||||
false,
|
||||
null,
|
||||
HttpResponseCodes::HTTP_CREATED
|
||||
);
|
||||
|
||||
@@ -34,6 +34,7 @@ class SubscriptionController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ class UserController extends ApiController
|
||||
|
||||
return $this->respondAsResource(
|
||||
$collection,
|
||||
true,
|
||||
['total' => $total]
|
||||
);
|
||||
}
|
||||
@@ -70,7 +71,7 @@ class UserController extends ApiController
|
||||
{
|
||||
if (UserConductor::creatable() === true) {
|
||||
$user = User::create($request->all());
|
||||
return $this->respondAsResource(UserConductor::model($request, $user), [], HttpResponseCodes::HTTP_CREATED);
|
||||
return $this->respondAsResource(UserConductor::model($request, $user), false, [], HttpResponseCodes::HTTP_CREATED);
|
||||
} else {
|
||||
return $this->respondForbidden();
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@ class PostRequest extends BaseRequest
|
||||
public function postRules()
|
||||
{
|
||||
return [
|
||||
'slug' => 'string|min:6|unique:posts',
|
||||
'title' => 'string|min:6|max:255',
|
||||
'publish_at' => 'date',
|
||||
'user_id' => 'uuid|exists:users,id',
|
||||
'slug' => 'required|string|min:6|unique:posts',
|
||||
'title' => 'required|string|min:6|max:255',
|
||||
'publish_at' => 'required|date',
|
||||
'user_id' => 'required|uuid|exists:users,id',
|
||||
'content' => 'required|string|min:6',
|
||||
'hero' => 'required|uuid|exists:media,id',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -37,6 +39,8 @@ class PostRequest extends BaseRequest
|
||||
'title' => 'string|min:6|max:255',
|
||||
'publish_at' => 'date',
|
||||
'user_id' => 'uuid|exists:users,id',
|
||||
'content' => 'string|min:6',
|
||||
'hero' => 'uuid|exists:media,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,28 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id !== null ?: $request->ip());
|
||||
});
|
||||
// RateLimiter::for('api', function (Request $request) {
|
||||
// return Limit::perMinute(60)->by($request->user()?->id !== null ?: $request->ip());
|
||||
// });
|
||||
|
||||
$rateLimitEnabled = true;
|
||||
$user = auth()->user();
|
||||
|
||||
if (app()->environment('testing')) {
|
||||
$rateLimitEnabled = false;
|
||||
} elseif ($user !== null && $user->hasPermission('admin/ratelimit') === true) {
|
||||
// Admin users with the "admin/ratelimit" permission are not rate limited
|
||||
$rateLimitEnabled = false;
|
||||
}
|
||||
|
||||
if ($rateLimitEnabled === true) {
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(180)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
} else {
|
||||
RateLimiter::for('api', function () {
|
||||
return Limit::none();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
database/factories/EventFactory.php
Normal file
40
database/factories/EventFactory.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||
*/
|
||||
class EventFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$startDate = Carbon::parse($this->faker->dateTimeBetween('now', '+1 year'));
|
||||
$endDate = Carbon::parse($this->faker->dateTimeBetween($startDate, '+1 year'));
|
||||
$publishDate = Carbon::parse($this->faker->dateTimeBetween('-1 month', '+1 month'));
|
||||
|
||||
return [
|
||||
'title' => $this->faker->sentence(),
|
||||
'location' => $this->faker->randomElement(['online', 'physical']),
|
||||
'address' => $this->faker->address,
|
||||
'start_at' => $startDate,
|
||||
'end_at' => $endDate,
|
||||
'publish_at' => $publishDate,
|
||||
'status' => $this->faker->randomElement(['draft', 'soon', 'open', 'closed', 'cancelled']),
|
||||
'registration_type' => $this->faker->randomElement(['none', 'email', 'link', 'message']),
|
||||
'registration_data' => $this->faker->sentence(),
|
||||
'hero' => $this->faker->uuid,
|
||||
'content' => $this->faker->paragraphs(3, true),
|
||||
'price' => $this->faker->numberBetween(0, 150),
|
||||
'ages' => $this->faker->regexify('\d+(\+|\-\d+)?'),
|
||||
];
|
||||
}
|
||||
}
|
||||
29
database/factories/MediaFactory.php
Normal file
29
database/factories/MediaFactory.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||
*/
|
||||
class MediaFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'title' => $this->faker->sentence(),
|
||||
'name' => storage_path('app/public/') . $this->faker->slug() . '.' . $this->faker->fileExtension,
|
||||
'mime' => $this->faker->mimeType,
|
||||
'user_id' => $this->faker->uuid,
|
||||
'size' => $this->faker->numberBetween(1000, 1000000),
|
||||
'permission' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
31
database/factories/PostFactory.php
Normal file
31
database/factories/PostFactory.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Event>
|
||||
*/
|
||||
class PostFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$publishDate = Carbon::parse($this->faker->dateTimeBetween('-1 month', '+1 month'));
|
||||
|
||||
return [
|
||||
'title' => $this->faker->sentence(),
|
||||
'slug' => $this->faker->slug(),
|
||||
'publish_at' => $publishDate,
|
||||
'content' => $this->faker->paragraphs(3, true),
|
||||
'user_id' => $this->faker->uuid,
|
||||
'hero' => $this->faker->uuid,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
|
||||
class AuthEndpointTest extends TestCase
|
||||
class AuthApiTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
136
tests/Feature/EventsApiTest.php
Normal file
136
tests/Feature/EventsApiTest.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
use App\Models\Event;
|
||||
use App\Models\Media;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory as FakerFactory;
|
||||
|
||||
class EventsApiTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected $faker;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->faker = FakerFactory::create();
|
||||
}
|
||||
|
||||
public function testAnyUserCanViewEvent()
|
||||
{
|
||||
// Create an event
|
||||
$event = Event::factory()->create([
|
||||
'publish_at' => Carbon::parse($this->faker->dateTimeBetween('-2 months', '-1 month')),
|
||||
]);
|
||||
|
||||
// Create a future event
|
||||
$futureEvent = Event::factory()->create([
|
||||
'publish_at' => Carbon::parse($this->faker->dateTimeBetween('+1 month', '+2 months')),
|
||||
]);
|
||||
|
||||
// Send GET request to the /api/events endpoint
|
||||
$response = $this->getJson('/api/events');
|
||||
$response->assertStatus(200);
|
||||
|
||||
// Assert that the event is in the response data
|
||||
$response->assertJsonCount(1, 'events');
|
||||
$response->assertJsonFragment([
|
||||
'id' => $event->id,
|
||||
'title' => $event->title,
|
||||
]);
|
||||
|
||||
$response->assertJsonMissing([
|
||||
'id' => $futureEvent->id,
|
||||
'title' => $futureEvent->title,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAdminCanCreateUpdateDeleteEvent()
|
||||
{
|
||||
// Create a user with the admin/events permission
|
||||
$adminUser = User::factory()->create();
|
||||
$adminUser->givePermission('admin/events');
|
||||
|
||||
// Create media data
|
||||
$media = Media::factory()->create(['user_id' => $adminUser->id]);
|
||||
|
||||
// Create event data
|
||||
$eventData = Event::factory()->make([
|
||||
'start_at' => now()->addDays(7),
|
||||
'end_at' => now()->addDays(7)->addHours(2),
|
||||
'hero' => $media->id,
|
||||
])->toArray();
|
||||
|
||||
// Test creating event
|
||||
$response = $this->actingAs($adminUser)->postJson('/api/events', $eventData);
|
||||
$response->assertStatus(201);
|
||||
$this->assertDatabaseHas('events', [
|
||||
'title' => $eventData['title'],
|
||||
'content' => $eventData['content'],
|
||||
]);
|
||||
|
||||
// Test viewing event
|
||||
$event = Event::where('title', $eventData['title'])->first();
|
||||
$response = $this->get("/api/events/$event->id");
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure([
|
||||
'event' => [
|
||||
'id',
|
||||
'title',
|
||||
'content',
|
||||
'start_at',
|
||||
'end_at',
|
||||
]
|
||||
]);
|
||||
|
||||
// Test updating event
|
||||
$eventData['title'] = 'Updated Event';
|
||||
$response = $this->actingAs($adminUser)->putJson("/api/events/$event->id", $eventData);
|
||||
$response->assertStatus(200);
|
||||
$this->assertDatabaseHas('events', [
|
||||
'title' => 'Updated Event',
|
||||
]);
|
||||
|
||||
// Test deleting event
|
||||
$response = $this->actingAs($adminUser)->delete("/api/events/$event->id");
|
||||
$response->assertStatus(204);
|
||||
$this->assertDatabaseMissing('events', [
|
||||
'title' => 'Updated Event',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testNonAdminCannotCreateUpdateDeleteEvent()
|
||||
{
|
||||
// Create a user without admin/events permission
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Authenticate as the user
|
||||
$this->actingAs($user);
|
||||
|
||||
// Try to create a new event
|
||||
$media = Media::factory()->create(['user_id' => $user->id]);
|
||||
|
||||
$newEventData = Event::factory()->make(['hero' => $media->id])->toArray();
|
||||
|
||||
$response = $this->postJson('/api/events', $newEventData);
|
||||
$response->assertStatus(403);
|
||||
|
||||
// Try to update an event
|
||||
$event = Event::factory()->create();
|
||||
$updatedEventData = [
|
||||
'title' => 'Updated Event',
|
||||
'content' => 'This is an updated event.',
|
||||
// Add more fields as needed
|
||||
];
|
||||
$response = $this->putJson('/api/events/' . $event->id, $updatedEventData);
|
||||
$response->assertStatus(403);
|
||||
|
||||
// Try to delete an event
|
||||
$event = Event::factory()->create();
|
||||
$response = $this->deleteJson('/api/events/' . $event->id);
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
}
|
||||
134
tests/Feature/PostsApiTest.php
Normal file
134
tests/Feature/PostsApiTest.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
use App\Models\Media;
|
||||
use App\Models\Post;
|
||||
use Faker\Factory as FakerFactory;
|
||||
|
||||
class PostsApiTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected $faker;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->faker = FakerFactory::create();
|
||||
}
|
||||
|
||||
public function testAnyUserCanViewPost()
|
||||
{
|
||||
// Create an event
|
||||
$post = Post::factory()->create([
|
||||
'publish_at' => $this->faker->dateTimeBetween('-2 months', '-1 month'),
|
||||
]);
|
||||
|
||||
// Create a future event
|
||||
$futurePost = Post::factory()->create([
|
||||
'publish_at' => $this->faker->dateTimeBetween('+1 month', '+2 months'),
|
||||
]);
|
||||
|
||||
// Send GET request to the /api/posts endpoint
|
||||
$response = $this->getJson('/api/posts');
|
||||
$response->assertStatus(200);
|
||||
|
||||
// Assert that the event is in the response data
|
||||
$response->assertJsonCount(1, 'posts');
|
||||
$response->assertJsonFragment([
|
||||
'id' => $post->id,
|
||||
'title' => $post->title,
|
||||
'content' => $post->content,
|
||||
]);
|
||||
|
||||
$response->assertJsonMissing([
|
||||
'id' => $futurePost->id,
|
||||
'title' => $futurePost->title,
|
||||
'content' => $futurePost->content,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAdminCanCreateUpdateDeletePost()
|
||||
{
|
||||
// Create a user with the admin/events permission
|
||||
$adminUser = User::factory()->create();
|
||||
$adminUser->givePermission('admin/posts');
|
||||
|
||||
// Create media data
|
||||
$media = Media::factory()->create(['user_id' => $adminUser->id]);
|
||||
|
||||
// Create event data
|
||||
$postData = Post::factory()->make([
|
||||
'user_id' => $adminUser->id,
|
||||
'hero' => $media->id,
|
||||
])->toArray();
|
||||
|
||||
// Test creating event
|
||||
$response = $this->actingAs($adminUser)->postJson('/api/posts', $postData);
|
||||
$response->assertStatus(201);
|
||||
$this->assertDatabaseHas('posts', [
|
||||
'title' => $postData['title'],
|
||||
'content' => $postData['content'],
|
||||
]);
|
||||
|
||||
// Test viewing event
|
||||
$post = Post::where('title', $postData['title'])->first();
|
||||
$response = $this->get("/api/posts/$post->id");
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure([
|
||||
'post' => [
|
||||
'id',
|
||||
'title',
|
||||
'content',
|
||||
]
|
||||
]);
|
||||
|
||||
// Test updating event
|
||||
$postData['title'] = 'Updated Post';
|
||||
$response = $this->actingAs($adminUser)->putJson("/api/posts/$post->id", $postData);
|
||||
$response->assertStatus(200);
|
||||
$this->assertDatabaseHas('posts', [
|
||||
'title' => 'Updated Post',
|
||||
]);
|
||||
|
||||
// Test deleting event
|
||||
$response = $this->actingAs($adminUser)->delete("/api/posts/$post->id");
|
||||
$response->assertStatus(204);
|
||||
$this->assertDatabaseMissing('posts', [
|
||||
'title' => 'Updated Post',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testNonAdminCannotCreateUpdateDeletePost()
|
||||
{
|
||||
// Create a user without admin/events permission
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Authenticate as the user
|
||||
$this->actingAs($user);
|
||||
|
||||
// Try to create a new post
|
||||
$media = Media::factory()->create(['user_id' => $user->id]);
|
||||
|
||||
$newPostData = Post::factory()->make(['user_id' => $user->id, 'hero' => $media->id])->toArray();
|
||||
|
||||
$response = $this->postJson('/api/posts', $newPostData);
|
||||
$response->assertStatus(403);
|
||||
|
||||
// Try to update an event
|
||||
$post = Post::factory()->create();
|
||||
$updatedPostData = [
|
||||
'title' => 'Updated Event',
|
||||
'content' => 'This is an updated event.',
|
||||
// Add more fields as needed
|
||||
];
|
||||
$response = $this->putJson('/api/posts/' . $post->id, $updatedPostData);
|
||||
$response->assertStatus(403);
|
||||
|
||||
// Try to delete an event
|
||||
$post = Post::factory()->create();
|
||||
$response = $this->deleteJson('/api/posts/' . $post->id);
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
|
||||
class UsersEndpointTest extends TestCase
|
||||
class UsersApiTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
Reference in New Issue
Block a user