require 'rails_helper' RSpec.describe MoodCalendarService do let(:user) { create(:user) } let(:mode) { create(:mode, user: user) } describe '.generate_calendar' do context 'avec des jours manquants' do it 'remplit les jours manquants avec mode: nil et guess égal au dernier mode' do mode2 = create(:mode, user: user) mode3 = create(:mode, user: user) create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-02-12 14:20:00")) create(:mood, user: user, mode: mode3, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-18 16:45:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-02-28") ) expect(result.length).to be >= 1 expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be true all_days = result.find { |m| m[:month] == Date.parse("2025-02-01") }[:weeks].flatten mood_12 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-12") } expect(mood_12[:mode]).to eq(mode2) expect(mood_12[:recorded_at].to_i).to eq(Time.zone.parse("2025-02-12 14:20:00").to_i) 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(mode2) 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(mode2) mood_15 = all_days.find { |m| m[:recorded_at].to_date == Date.parse("2025-02-15") } expect(mood_15[:mode]).to eq(mode3) 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(mode3) 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 mode2 = create(:mode, user: user) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-04-20 11:30:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-04-30") ) expect(result.length).to be >= 3 expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be true expect(result.any? { |m| m[:month] == Date.parse("2025-03-01") }).to be true expect(result.any? { |m| m[:month] == Date.parse("2025-04-01") }).to be true march = result.find { |m| m[:month] == Date.parse("2025-03-01") } all_march_days = march[:weeks].flatten expect(all_march_days.all? { |d| d[:mode].nil? }).to be true all_march_days.each do |day| expect(day[:guess]).to be_present, "Le jour #{day[:recorded_at].to_date} devrait avoir un guess" end april = result.find { |m| m[:month] == Date.parse("2025-04-01") } 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(mode2) 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(mode) 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(mode2) end end context 'sans aucun recorded_at' do it 'retourne un calendrier à partir de la date courante' do result = MoodCalendarService.generate_calendar(user) expect(result).not_to be_empty expect(result.first[:month]).to eq(Date.current.beginning_of_month) end end context 'structure du calendrier' do it 'commence chaque mois par le premier lundi' do create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-02-28") ) february = result[0] first_day = february[:weeks][0][0] 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-03-31") ) february = result[0] all_days = february[:weeks].flatten 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 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 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-02-28") ) february = result[0] 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-02-28") ) february = result[0] expect(february[:month]).to be_a(Date) expect(february[:month]).to eq(Date.parse("2025-02-01")) expect(february[:month].day).to eq(1) 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar(user) expect(result).not_to be_empty 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, start_date: Date.parse("2025-03-01"), end_date: Date.parse("2025-03-31") ) expect(result.first[:month]).to eq(Date.parse("2025-03-01")) expect(result.any? { |m| m[:month] == Date.parse("2025-02-01") }).to be false end end context 'avec end_date spécifié' do it 'se termine à end_date' do create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, end_date: Date.parse("2025-09-30") ) expect(result.last[:month]).to eq(Date.parse("2025-09-01")) 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 mode2 = create(:mode, user: user) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-04-20 10:00:00")) result = MoodCalendarService.generate_calendar( user, start_date: Date.parse("2025-03-01"), end_date: Date.parse("2025-03-31") ) expect(result.length).to eq(1) expect(result.first[:month]).to eq(Date.parse("2025-03-01")) all_days = result.first[:weeks].flatten expect(all_days.all? { |d| d[:mode].nil? }).to be true end it 'gère une plage de plusieurs mois' do create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, start_date: Date.parse("2025-02-01"), end_date: Date.parse("2025-04-30") ) 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, 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, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) result = MoodCalendarService.generate_calendar( user, start_date: Date.parse("2025-04-01"), end_date: Date.parse("2025-04-30") ) expect(result.first[:month]).to eq(Date.parse("2025-04-01")) all_days = result.first[:weeks].flatten expect(all_days.all? { |d| d[:mode].nil? }).to be true end end context 'avec plusieurs mois' do it 'chaque mois commence et finit correctement' do mode2 = create(:mode, user: user) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user, mode: mode2, recorded_at: Time.zone.parse("2025-03-20 10:00:00")) result = MoodCalendarService.generate_calendar( user, 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") } last_day_february = february[:weeks].flatten.last expect(last_day_february[:recorded_at].to_date).to eq(Date.parse("2025-03-02")) first_day_march = march[:weeks].flatten.first expect(first_day_march[:recorded_at].to_date).to eq(Date.parse("2025-03-03")) 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 user2 = create(:user) mode2 = create(:mode, user: user2) create(:mood, user: user, mode: mode, recorded_at: Time.zone.parse("2025-02-15 10:00:00")) create(:mood, user: user2, mode: mode2, recorded_at: Time.zone.parse("2025-02-16 10:00:00")) result = MoodCalendarService.generate_calendar( user, 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(mode) 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(mode) end end end end