api/tests/Feature/PostReviewsControllerTest.php

222 lines
6.5 KiB
PHP

<?php
use App\Models\MealPosts;
use App\Models\PostReviews;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Laravel\Sanctum\Sanctum;
uses(RefreshDatabase::class);
it('creates one review per user for a meal post', function () {
$user = User::factory()->create();
$mealPost = MealPosts::factory()->create();
Sanctum::actingAs($user);
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 5,
'comment' => 'Très bon repas.',
])
->assertCreated()
->assertJsonPath('data.rating', 5)
->assertJsonPath('data.comment', 'Très bon repas.')
->assertJsonPath('data.meal_post_id', $mealPost->id)
->assertJsonPath('data.user_id', $user->id);
$this->assertDatabaseHas('post_reviews', [
'meal_post_id' => $mealPost->id,
'user_id' => $user->id,
'rating' => 5,
'comment' => 'Très bon repas.',
]);
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 4,
])
->assertConflict()
->assertJsonPath('message', __('api.post_reviews.already_exists'));
expect(PostReviews::query()->count())->toBe(1);
});
it('sends a push notification to the meal owner when another user comments on their meal', function () {
Http::fake([
'https://exp.host/*' => Http::response([
'data' => [
['status' => 'ok', 'id' => 'ticket-one'],
],
]),
]);
$owner = User::factory()->create();
$owner->deviceTokens()->create([
'expo_push_token' => 'ExponentPushToken[owner]',
'platform' => 'ios',
]);
$reviewer = User::factory()->create(['name' => 'Alex']);
$mealPost = MealPosts::factory()
->for($owner, 'user')
->create(['title' => 'Recovery bowl']);
Sanctum::actingAs($reviewer);
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 5,
'comment' => 'Très bon repas.',
])->assertCreated();
Http::assertSent(fn (Request $request) => $request->url() === 'https://exp.host/--/api/v2/push/send'
&& $request->data() === [
[
'to' => 'ExponentPushToken[owner]',
'title' => 'New comment',
'body' => 'Alex commented on your meal "Recovery bowl".',
'sound' => 'default',
'channelId' => 'default',
'data' => [
'url' => "bowly://meals/{$mealPost->id}",
],
],
]);
});
it('does not send a push notification for rating-only reviews', function () {
Http::fake();
$owner = User::factory()->create();
$owner->deviceTokens()->create([
'expo_push_token' => 'ExponentPushToken[owner]',
'platform' => 'ios',
]);
$reviewer = User::factory()->create();
$mealPost = MealPosts::factory()->for($owner, 'user')->create();
Sanctum::actingAs($reviewer);
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 5,
])->assertCreated();
Http::assertNothingSent();
});
it('does not notify the owner when they comment on their own meal', function () {
Http::fake();
$owner = User::factory()->create();
$owner->deviceTokens()->create([
'expo_push_token' => 'ExponentPushToken[owner]',
'platform' => 'ios',
]);
$mealPost = MealPosts::factory()->for($owner, 'user')->create();
Sanctum::actingAs($owner);
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 5,
'comment' => 'Note personnelle.',
])->assertCreated();
Http::assertNothingSent();
});
it('lists reviews for a single meal post', function () {
$user = User::factory()->create();
$mealPost = MealPosts::factory()->create();
$otherMealPost = MealPosts::factory()->create();
$firstReview = PostReviews::query()->create([
'meal_post_id' => $mealPost->id,
'user_id' => User::factory()->create()->id,
'rating' => 4,
'comment' => 'Solide.',
]);
$secondReview = PostReviews::query()->create([
'meal_post_id' => $mealPost->id,
'user_id' => User::factory()->create()->id,
'rating' => 5,
'comment' => null,
]);
$otherReview = PostReviews::query()->create([
'meal_post_id' => $otherMealPost->id,
'user_id' => User::factory()->create()->id,
'rating' => 1,
'comment' => 'Pas celui-ci.',
]);
Sanctum::actingAs($user);
$this->getJson("/api/meal-posts/{$mealPost->id}/reviews")
->assertOk()
->assertJsonCount(2, 'data')
->assertJsonFragment(['id' => $firstReview->id])
->assertJsonFragment(['id' => $secondReview->id])
->assertJsonMissing(['id' => $otherReview->id]);
});
it('lets the review author update and delete their review', function () {
$user = User::factory()->create();
$review = PostReviews::query()->create([
'meal_post_id' => MealPosts::factory()->create()->id,
'user_id' => $user->id,
'rating' => 3,
'comment' => 'Initial.',
]);
Sanctum::actingAs($user);
$this->patchJson("/api/post-reviews/{$review->id}", [
'rating' => 4,
'comment' => null,
])
->assertOk()
->assertJsonPath('data.rating', 4)
->assertJsonPath('data.comment', null);
$this->assertDatabaseHas('post_reviews', [
'id' => $review->id,
'rating' => 4,
'comment' => null,
]);
$this->deleteJson("/api/post-reviews/{$review->id}")
->assertNoContent();
$this->assertDatabaseMissing('post_reviews', [
'id' => $review->id,
]);
});
it('does not let another user mutate a review', function () {
$review = PostReviews::query()->create([
'meal_post_id' => MealPosts::factory()->create()->id,
'user_id' => User::factory()->create()->id,
'rating' => 3,
'comment' => 'Initial.',
]);
Sanctum::actingAs(User::factory()->create());
$this->patchJson("/api/post-reviews/{$review->id}", [
'rating' => 4,
])->assertNotFound();
$this->deleteJson("/api/post-reviews/{$review->id}")
->assertNotFound();
$this->assertDatabaseHas('post_reviews', [
'id' => $review->id,
'rating' => 3,
]);
});
it('requires authentication to create a review', function () {
$mealPost = MealPosts::factory()->create();
$this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
'rating' => 5,
])->assertUnauthorized();
});