setUpHasPlanner(); } public function test_generate_schedule_creates_schedule_records(): void { $planner = $this->planner; $user = User::factory()->planner($planner)->create(); $dish = Dish::factory()->planner($planner)->create(); $dish->users()->attach($user); $this->assertDatabaseEmpty(Schedule::class); $response = $this ->actingAs($planner) ->post(route('api.schedule.generate'), [ 'overwrite' => false, ]); $response->assertStatus(200); // Should create 14 schedule records (2 weeks) $this->assertDatabaseCount(Schedule::class, 14); } public function test_overwrite_false_preserves_existing_schedules(): void { $planner = $this->planner; $user = User::factory()->planner($planner)->create(); $dish1 = Dish::factory()->planner($planner)->create(['name' => 'Dish 1']); $dish2 = Dish::factory()->planner($planner)->create(['name' => 'Dish 2']); $dish1->users()->attach($user); $dish2->users()->attach($user); // Create a pre-existing schedule for today $schedule = Schedule::factory()->create([ 'planner_id' => $planner->id, 'date' => now()->format('Y-m-d'), ]); $userDish1 = $user->userDishes()->where('dish_id', $dish1->id)->first(); $existingScheduledDish = ScheduledUserDish::factory()->create([ 'schedule_id' => $schedule->id, 'user_id' => $user->id, 'user_dish_id' => $userDish1->id, ]); // Generate with overwrite = false $response = $this ->actingAs($planner) ->post(route('api.schedule.generate'), [ 'overwrite' => false, ]); $response->assertStatus(200); // The existing scheduled dish should remain unchanged $this->assertDatabaseHas(ScheduledUserDish::class, [ 'id' => $existingScheduledDish->id, 'user_dish_id' => $userDish1->id, ]); } public function test_schedule_isolation_between_planners(): void { $planner1 = $this->planner; $planner2 = Planner::factory()->create(); $user1 = User::factory()->planner($planner1)->create(); $user2 = User::factory()->planner($planner2)->create(); $dish1 = Dish::factory()->planner($planner1)->create(); $dish2 = Dish::factory()->planner($planner2)->create(); $dish1->users()->attach($user1); $dish2->users()->attach($user2); // Generate schedule for planner1 $this ->actingAs($planner1) ->post(route('api.schedule.generate'), ['overwrite' => false]) ->assertStatus(200); // Generate schedule for planner2 $this ->actingAs($planner2) ->post(route('api.schedule.generate'), ['overwrite' => false]) ->assertStatus(200); // Verify each planner only has their own schedules $planner1Schedules = Schedule::withoutGlobalScopes() ->where('planner_id', $planner1->id) ->count(); $planner2Schedules = Schedule::withoutGlobalScopes() ->where('planner_id', $planner2->id) ->count(); $this->assertEquals(14, $planner1Schedules); $this->assertEquals(14, $planner2Schedules); } public function test_skip_schedule_day_nullifies_user_dish(): void { $planner = $this->planner; $user = User::factory()->planner($planner)->create(); $dish = Dish::factory()->planner($planner)->create(); $dish->users()->attach($user); $userDish = $user->userDishes()->first(); $date = now()->format('Y-m-d'); // Create a schedule with a dish $schedule = Schedule::factory()->create([ 'planner_id' => $planner->id, 'date' => $date, ]); $scheduledUserDish = ScheduledUserDish::factory()->create([ 'schedule_id' => $schedule->id, 'user_id' => $user->id, 'user_dish_id' => $userDish->id, 'is_skipped' => false, ]); // Mark the day as skipped $response = $this ->actingAs($planner) ->put(route('api.schedule.update', ['date' => $date]), [ 'is_skipped' => true, ]); $response->assertStatus(200); // Verify schedule is marked as skipped $this->assertDatabaseHas(Schedule::class, [ 'id' => $schedule->id, 'is_skipped' => true, ]); // Verify scheduled user dish has null user_dish_id $scheduledUserDish->refresh(); $this->assertNull($scheduledUserDish->user_dish_id); } public function test_delete_scheduled_user_dish_removes_record(): void { $planner = $this->planner; $user = User::factory()->planner($planner)->create(); $dish = Dish::factory()->planner($planner)->create(); $dish->users()->attach($user); $userDish = $user->userDishes()->first(); $schedule = Schedule::factory()->create([ 'planner_id' => $planner->id, 'date' => now()->format('Y-m-d'), ]); $scheduledUserDish = ScheduledUserDish::factory()->create([ 'schedule_id' => $schedule->id, 'user_id' => $user->id, 'user_dish_id' => $userDish->id, ]); $response = $this ->actingAs($planner) ->delete(route('api.scheduled-user-dishes.destroy', ['scheduledUserDish' => $scheduledUserDish->id])); $response->assertStatus(200); $this->assertDatabaseMissing(ScheduledUserDish::class, [ 'id' => $scheduledUserDish->id, ]); } public function test_planner_cannot_delete_other_planners_scheduled_dish(): void { $planner1 = $this->planner; $planner2 = Planner::factory()->create(); $user = User::factory()->planner($planner2)->create(); $dish = Dish::factory()->planner($planner2)->create(); $dish->users()->attach($user); $userDish = $user->userDishes()->first(); $schedule = Schedule::factory()->create([ 'planner_id' => $planner2->id, 'date' => now()->format('Y-m-d'), ]); $scheduledUserDish = ScheduledUserDish::factory()->create([ 'schedule_id' => $schedule->id, 'user_id' => $user->id, 'user_dish_id' => $userDish->id, ]); // Try to delete as planner1 (should fail) $response = $this ->actingAs($planner1) ->delete(route('api.scheduled-user-dishes.destroy', ['scheduledUserDish' => $scheduledUserDish->id])); $response->assertStatus(403); // Record should still exist $this->assertDatabaseHas(ScheduledUserDish::class, [ 'id' => $scheduledUserDish->id, ]); } public function test_schedule_show_creates_schedule_if_not_exists(): void { $planner = $this->planner; $futureDate = now()->addDays(30)->format('Y-m-d'); $this->assertDatabaseMissing(Schedule::class, [ 'planner_id' => $planner->id, 'date' => $futureDate, ]); $response = $this ->actingAs($planner) ->get(route('api.schedule.show', ['date' => $futureDate])); $response->assertStatus(200); // Schedule should now exist $this->assertDatabaseHas(Schedule::class, [ 'planner_id' => $planner->id, 'date' => $futureDate, ]); } }