# spec/services/mood_calendar_service_spec.rb require 'rails_helper' RSpec.describe MoodCalendarService do describe '.generate_calendar' do context 'avec un recorded_at pour chaque jour' do it 'génère un calendrier complet sans jours nil' do # Données du 1er au 7 février 2025 (une semaine complète) (1..7).each do |day| create(:mood, mode: "mood_#{day}", recorded_at: Time.zone.parse("2025-02-0#{day} 10:00:00")) end result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-02-28") ) expect(result.length).to eq(1) # Un seul mois expect(result[0][:month]).to eq(Date.parse("2025-02-01")) # Première semaine commence le lundi 3 février first_week = result[0][:weeks][0] expect(first_week.length).to eq(7) # Du lundi 3 au dimanche 9 # Vérifier que tous les jours du 3 au 7 ont un mode (3..7).each do |day| mood = first_week.find { |m| m[:recorded_at].day == day } expect(mood[:mode]).to eq("mood_#{day}") expect(mood).not_to have_key(:guess) end # Le 8 et 9 février devraient avoir mode: nil avec guess: "mood_7" mood_8 = first_week.find { |m| m[:recorded_at].day == 8 } expect(mood_8[:mode]).to be_nil expect(mood_8[:guess]).to eq("mood_7") mood_9 = first_week.find { |m| m[:recorded_at].day == 9 } expect(mood_9[:mode]).to be_nil expect(mood_9[:guess]).to eq("mood_7") end end context 'avec des jours manquants' do it 'remplit les jours manquants avec mode: nil et guess égal au dernier mode' do create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-12 08:30:00")) create(:mood, mode: "content", recorded_at: Time.zone.parse("2025-02-12 14:20:00")) create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-02-18 16:45:00")) moods = Mood.all result = MoodCalendarService.generate_calendar( moods, end_date: Date.parse("2025-02-28") ) expect(result.length).to eq(1) expect(result[0][:month]).to eq(Date.parse("2025-02-01")) # Trouver tous les jours all_days = result[0][:weeks].flatten # Le 12 février devrait avoir le dernier mood (content à 14:20) mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") } expect(mood_12[:mode]).to eq("content") expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i) # Le 13 février devrait être nil avec guess: "content" mood_13 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-13") } expect(mood_13[:mode]).to be_nil expect(mood_13[:guess]).to eq("content") # Le 14 février devrait être nil avec guess: "content" mood_14 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-14") } expect(mood_14[:mode]).to be_nil expect(mood_14[:guess]).to eq("content") # Le 15 février devrait avoir le mood "heureux" mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") } expect(mood_15[:mode]).to eq("heureux") # Le 16 février devrait être nil avec guess: "heureux" mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") } expect(mood_16[:mode]).to be_nil expect(mood_16[:guess]).to eq("heureux") # Les jours avant le 12 (à partir du premier lundi 3 février) devraient avoir guess: nil mood_3 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-03") } expect(mood_3[:mode]).to be_nil expect(mood_3[:guess]).to be_nil end it 'gère correctement plusieurs mois avec des jours manquants' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, mode: "joyeux", recorded_at: Time.zone.parse("2025-04-20 11:30:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-04-30") ) # Devrait avoir exactement février, mars et avril expect(result.length).to eq(3) # Vérifier février february = result.find { |m| m[:month] == Date.parse("2025-02-01") } expect(february).not_to be_nil # Vérifier mars (tous les jours devraient avoir mode: nil et guess: "heureux") march = result.find { |m| m[:month] == Date.parse("2025-03-01") } expect(march).not_to be_nil all_march_days = march[:weeks].flatten # Tous les jours de mars (y compris ceux qui débordent début avril) devraient avoir mode: nil expect(all_march_days.all? { |d| d[:mode].nil? }).to be true # Tous les jours de mars devraient avoir guess: "heureux" # (le mois de mars va du 3 mars au 6 avril, avant le mood du 20 avril) all_march_days.each do |day| expect(day[:guess]).to eq("heureux"), "Le jour #{day[:recorded_at].to_date} devrait avoir guess: 'heureux' mais a: #{day[:guess].inspect}" end # Vérifier avril april = result.find { |m| m[:month] == Date.parse("2025-04-01") } expect(april).not_to be_nil all_april_days = april[:weeks].flatten mood_20_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-20") } expect(mood_20_april[:mode]).to eq("joyeux") # Les jours du 7 au 19 avril devraient avoir guess: "heureux" mood_10_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-10") } expect(mood_10_april[:guess]).to eq("heureux") # Les jours après le 20 avril devraient avoir guess: "joyeux" mood_21_april = all_april_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-04-21") } expect(mood_21_april[:guess]).to eq("joyeux") end end context 'sans aucun recorded_at' do it 'retourne un tableau vide' do result = MoodCalendarService.generate_calendar(Mood.none) expect(result).to eq([]) end end context 'avec plusieurs moods le même jour' do it 'garde uniquement le mood le plus récent' do create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-12 08:30:00")) create(:mood, mode: "neutre", recorded_at: Time.zone.parse("2025-02-12 12:00:00")) create(:mood, mode: "content", recorded_at: Time.zone.parse("2025-02-12 14:20:00")) create(:mood, mode: "fatigué", recorded_at: Time.zone.parse("2025-02-12 09:15:00")) moods = Mood.all result = MoodCalendarService.generate_calendar( moods, end_date: Date.parse("2025-02-28") ) all_days = result[0][:weeks].flatten mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") } expect(mood_12[:mode]).to eq("content") expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i) end end context 'structure du calendrier' do it 'commence chaque mois par le premier lundi' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-02-28") ) february = result[0] first_day = february[:weeks][0][0] # Le premier jour de février 2025 devrait être le lundi 3 février expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03")) expect(first_day[:recorded_at].to_date.monday?).to be true end it 'va du premier lundi du mois au dimanche avant le premier lundi du mois suivant' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-03-31") ) february = result[0] all_days = february[:weeks].flatten # Premier jour : lundi 3 février 2025 first_day = all_days.first expect(first_day[:recorded_at].to_date).to eq(Date.parse("2025-02-03")) expect(first_day[:recorded_at].to_date.monday?).to be true # Dernier jour : dimanche 2 mars 2025 (veille du premier lundi de mars qui est le 3) last_day = all_days.last expect(last_day[:recorded_at].to_date).to eq(Date.parse("2025-03-02")) expect(last_day[:recorded_at].to_date.sunday?).to be true # Le 3 mars (premier lundi de mars) ne devrait PAS être dans février expect(all_days.any? { |d| d[:recorded_at].to_date == Date.parse("2025-03-03") }).to be false end it 'toutes les semaines ont exactement 7 jours' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-02-28") ) february = result[0] # Toutes les semaines devraient avoir exactement 7 jours february[:weeks].each do |week| expect(week.length).to eq(7) end end it 'retourne month comme Date (le premier du mois)' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-02-28") ) february = result[0] # month devrait être une Date expect(february[:month]).to be_a(Date) expect(february[:month]).to eq(Date.parse("2025-02-01")) expect(february[:month].day).to eq(1) # Premier du mois end end context 'avec paramètres par défaut' do it 'utilise le premier mood comme start_date et aujourd\'hui comme end_date' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar(Mood.all) expect(result).not_to be_empty # Devrait commencer en février expect(result.first[:month]).to eq(Date.parse("2025-02-01")) end end context 'avec start_date spécifié' do it 'commence à la date spécifiée' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, start_date: Date.parse("2025-03-01"), end_date: Date.parse("2025-03-31") ) # Devrait commencer en mars même si le mood est en février expect(result.first[:month]).to eq(Date.parse("2025-03-01")) # Ne devrait pas contenir février expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be false # Tous les jours de mars devraient avoir guess: "heureux" (du 15 février) all_march_days = result.first[:weeks].flatten puts all_march_days.inspect expect(all_march_days.all? { |d| d[:guess] == "heureux" }).to be true end end context 'avec end_date spécifié' do it 'se termine à la date spécifiée' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-02-28") ) # Devrait s'arrêter en février expect(result.last[:month]).to eq(Date.parse("2025-02-01")) # Ne devrait pas contenir mars ou au-delà expect(result.any? { |m| m[:month] == Date.parse("2025-03-01") }).to be false end end context 'avec start_date et end_date spécifiés' do it 'génère le calendrier pour la plage spécifiée' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-04-20 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, start_date: Date.parse("2025-03-01"), end_date: Date.parse("2025-03-31") ) # Devrait contenir uniquement mars expect(result.length).to eq(1) expect(result.first[:month]).to eq(Date.parse("2025-03-01")) # Tous les jours de mars devraient avoir guess: "heureux" (du 15 février) all_days = result.first[:weeks].flatten expect(all_days.all? { |d| d[:mode].nil? }).to be true expect(all_days.all? { |d| d[:guess] == "heureux" }).to be true end it 'gère une plage de plusieurs mois' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, start_date: Date.parse("2025-02-01"), end_date: Date.parse("2025-04-30") ) # Devrait contenir février, mars et avril months = result.map { |m| m[:month] } expect(months).to include( Date.parse("2025-02-01"), Date.parse("2025-03-01"), Date.parse("2025-04-01") ) end it 'fonctionne avec DateTime en paramètres' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) # Passer des DateTime au lieu de Date result = MoodCalendarService.generate_calendar( Mood.all, start_date: Time.zone.parse("2025-02-01 00:00:00"), end_date: Time.zone.parse("2025-02-28 23:59:59") ) expect(result).not_to be_empty expect(result.first[:month]).to eq(Date.parse("2025-02-01")) end end context 'avec une plage ne contenant aucun mood' do it 'génère un calendrier avec tous les jours à nil et guess du dernier mood avant la plage' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, start_date: Date.parse("2025-04-01"), end_date: Date.parse("2025-04-30") ) # Devrait contenir avril expect(result.first[:month]).to eq(Date.parse("2025-04-01")) # Tous les jours devraient avoir mode: nil et guess: "heureux" all_days = result.first[:weeks].flatten expect(all_days.all? { |d| d[:mode].nil? }).to be true expect(all_days.all? { |d| d[:guess] == "heureux" }).to be true end end context 'avec plusieurs mois' do it 'chaque mois commence et finit correctement' do create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, mode: "calme", recorded_at: Time.zone.parse("2025-03-20 10:00:00")) result = MoodCalendarService.generate_calendar( Mood.all, end_date: Date.parse("2025-03-31") ) february = result.find { |m| m[:month] == Date.parse("2025-02-01") } march = result.find { |m| m[:month] == Date.parse("2025-03-01") } # Février se termine le dimanche 2 mars last_day_february = february[:weeks].flatten.last expect(last_day_february[:recorded_at].to_date).to eq(Date.parse("2025-03-02")) # Mars commence le lundi 3 mars first_day_march = march[:weeks].flatten.first expect(first_day_march[:recorded_at].to_date).to eq(Date.parse("2025-03-03")) # Pas de chevauchement expect(last_day_february[:recorded_at].to_date + 1.day).to eq(first_day_march[:recorded_at].to_date) end end context 'avec un scope spécifique' do it 'fonctionne avec des moods filtrés par user' do user1 = create(:user) user2 = create(:user) create(:mood, mode: "heureux", recorded_at: Time.zone.parse("2025-02-15 10:00:00"), user: user1) create(:mood, mode: "triste", recorded_at: Time.zone.parse("2025-02-16 10:00:00"), user: user2) moods = Mood.where(user: user1) result = MoodCalendarService.generate_calendar( moods, end_date: Date.parse("2025-02-28") ) all_days = result[0][:weeks].flatten mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") } expect(mood_15[:mode]).to eq("heureux") # Le mood du user2 ne devrait pas apparaître mood_16 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-16") } expect(mood_16[:mode]).to be_nil expect(mood_16[:guess]).to eq("heureux") end end end end