"""연장근무 자동 적립 가드 테스트. auto_overtime(자동 적립)가 OFF면, 자동 퇴근 경로(근무일 경계 롤오버 등)에서도 은행 적립을 하지 않아야 한다 — clock_out() 대화상자에서 '아니오'를 고른 것과 동일한 의미. handle_workday_rollover는 위젯 의존이 tail(load_today_data/update_overtime_balance)뿐이라, __new__로 만든 인스턴스에 필요한 속성만 채워 단위 테스트한다 (QApplication 불필요). """ import os import sys from datetime import datetime, timedelta, time as dtime sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from core.database import Database from core.time_calculator import TimeCalculator from ui.main_window import MainWindow def _rollover_balance(db, monkeypatch): """어제 미퇴근 상태에서 근무일 경계 롤오버를 실행하고 적립 잔액을 반환.""" from PyQt5.QtWidgets import QMessageBox monkeypatch.setattr(QMessageBox, 'information', staticmethod(lambda *a, **k: QMessageBox.Ok)) today = datetime.now().date() y = today - timedelta(days=1) db.add_work_record(y.isoformat(), '09:00:00', is_manual=True) # 어제: 미퇴근 w = MainWindow.__new__(MainWindow) # __init__ 우회 (위젯/타이머 없음) w.db = db w.time_calc = TimeCalculator(work_minutes=480) w.clock_in_time = datetime.combine(y, dtime(9, 0, 0)) w.is_clocked_in = True w.midnight_rollover_handled = False w.is_on_break = False w.lunch_break_enabled = False w.dinner_break_enabled = False w.load_today_data = lambda: None # tail UI refresh stub w.update_overtime_balance = lambda: None # tail UI refresh stub w.handle_workday_rollover(datetime.combine(today, dtime(7, 0, 0))) return db.get_total_overtime_balance() def test_rollover_does_not_accrue_when_auto_overtime_off(tmp_path, monkeypatch): db = Database(str(tmp_path / 'off.db')) db.set_setting('auto_overtime', 'false') assert _rollover_balance(db, monkeypatch) == 0 def test_rollover_accrues_when_auto_overtime_on(tmp_path, monkeypatch): db = Database(str(tmp_path / 'on.db')) db.set_setting('auto_overtime', 'true') assert _rollover_balance(db, monkeypatch) > 0 def test_delete_overtime_earned_reduces_balance(tmp_path): """적립(은행) 기록 삭제 시 잔액이 그만큼 감소한다.""" from datetime import date db = Database(str(tmp_path / 'del.db')) today = date.today().isoformat() db.add_overtime_earned(None, 90, today) assert db.get_total_overtime_balance() == 90 bank_id = db.get_connection().execute( 'SELECT id FROM overtime_bank').fetchone()[0] assert db.delete_overtime_earned(bank_id) is True assert db.get_total_overtime_balance() == 0 # 없는 id 삭제는 False assert db.delete_overtime_earned(999999) is False