aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-drill-smart-reschedule.el
blob: 49eddb04fcf57e0bac5ab936e5a207f84e483f42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
;;; test-org-drill-smart-reschedule.el --- Tests for smart-reschedule  -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for `org-drill-smart-reschedule', the function that runs after a
;; user rates a card.  It updates the card's DRILL_* properties via
;; store-item-data and then sets a fresh SCHEDULED stamp.
;;
;; Three days-ahead branches:
;;
;; - 0   → unschedule (treat as new again)
;; - <0  → schedule for today (right now)
;; - >0  → schedule N days from today
;; - nil → use the algorithm-computed next-interval (this branch was
;;         broken before the fix — `(= 0 nil)' errored).
;;
;; The user-facing contract: rate a card, see the next-review date
;; advance to a sensible point.

;;; Code:

(require 'ert)
(require 'cl-lib)
(require 'org)
(require 'org-drill)

;;;; Helpers

(defmacro with-fresh-drill-entry (&rest body)
  (declare (indent 0))
  `(with-temp-buffer
     (let ((org-startup-folded nil))
       (insert "* Question :drill:\n")
       (org-mode)
       (goto-char (point-min))
       ,@body)))

(defmacro with-fixed-now (&rest body)
  `(cl-letf (((symbol-function 'current-time)
              (lambda () (encode-time 0 0 12 5 5 2026))))
     ,@body))

(defun current-scheduled-time-string ()
  "Return SCHEDULED stamp on entry at point or nil."
  (org-entry-get (point) "SCHEDULED"))

;;;; Branch: days-ahead = 0 (unschedule)

(ert-deftest test-org-drill-smart-reschedule-zero-days-ahead-unschedules ()
  "Passing 0 removes the SCHEDULED stamp — the entry is treated as new again."
  (with-fresh-drill-entry
    (org-schedule nil "2026-05-01")
    (with-fixed-now
      (org-drill-smart-reschedule 4 0))
    (should (null (current-scheduled-time-string)))))

;;;; Branch: days-ahead < 0 (today)

(ert-deftest test-org-drill-smart-reschedule-negative-schedules-today ()
  "Negative days-ahead schedules for current-time (today)."
  (with-fresh-drill-entry
    (with-fixed-now
      (org-drill-smart-reschedule 4 -1))
    (let ((scheduled (current-scheduled-time-string)))
      (should scheduled)
      ;; Scheduled stamp matches today's date.
      (should (string-match-p "2026-05-05" scheduled)))))

;;;; Branch: days-ahead > 0 (N days from today)

(ert-deftest test-org-drill-smart-reschedule-positive-schedules-n-days-ahead ()
  "Positive days-ahead schedules exactly N days into the future."
  (with-fresh-drill-entry
    (with-fixed-now
      (org-drill-smart-reschedule 4 7))
    (let ((scheduled (current-scheduled-time-string)))
      (should scheduled)
      ;; 2026-05-05 + 7 days = 2026-05-12.
      (should (string-match-p "2026-05-12" scheduled)))))

;;;; Branch: days-ahead = nil (use algorithm)

(ert-deftest test-org-drill-smart-reschedule-nil-days-ahead-uses-algorithm ()
  "Without days-ahead, fall back to the scheduler's computed next-interval.
Pre-fix this branch crashed with `Wrong type argument: number-or-marker-p, nil'
because the cond compared nil with `=' before the type-guard."
  (with-fresh-drill-entry
    (with-fixed-now
      ;; Should not error.  The scheduled date should be in the future
      ;; (some sensible interval).
      (org-drill-smart-reschedule 5)
      (let ((scheduled (current-scheduled-time-string)))
        (should scheduled)))))

;;;; Property side-effects

(ert-deftest test-org-drill-smart-reschedule-writes-drill-properties ()
  "Rescheduling also writes DRILL_* properties via store-item-data."
  (with-fresh-drill-entry
    (with-fixed-now
      (org-drill-smart-reschedule 5 7))
    (should (org-entry-get (point) "DRILL_LAST_INTERVAL"))
    (should (org-entry-get (point) "DRILL_TOTAL_REPEATS"))
    (should (org-entry-get (point) "DRILL_EASE"))))

(ert-deftest test-org-drill-smart-reschedule-increments-total-repeats ()
  "Each rating increments DRILL_TOTAL_REPEATS."
  (with-fresh-drill-entry
    (with-fixed-now
      (org-drill-smart-reschedule 4 7)
      (let ((after-first (string-to-number
                          (org-entry-get (point) "DRILL_TOTAL_REPEATS"))))
        (org-drill-smart-reschedule 4 7)
        (let ((after-second (string-to-number
                             (org-entry-get (point) "DRILL_TOTAL_REPEATS"))))
          (should (= (1+ after-first) after-second)))))))

(provide 'test-org-drill-smart-reschedule)

;;; test-org-drill-smart-reschedule.el ends here