aboutsummaryrefslogtreecommitdiff
path: root/tests/test-dwim-shell-config-password-file.el
blob: b33deb5505165e6ed6fadfb6b2b97091b6f69bed (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
;;; test-dwim-shell-config-password-file.el --- Tests for password-file lifetime -*- lexical-binding: t; -*-

;;; Commentary:
;; Covers the password-file helpers in dwim-shell-config: the on-completion
;; cleanup callback factory and the run-with-password-file wrapper.  The point
;; of these helpers is that the password temp file is deleted only after the
;; spawned async process exits (success or failure), not when it is launched.
;; The async `dwim-shell-command-on-marked-files' call is stubbed so no external
;; process runs.

;;; Code:

(when noninteractive
  (package-initialize))

(require 'ert)
(require 'cl-lib)
(require 'dwim-shell-config)

;; ---------------------------------------------------------------------------
;;; cj/dwim-shell--password-cleanup-callback
;; ---------------------------------------------------------------------------

(ert-deftest test-dwim-password-cleanup-deletes-on-success ()
  "Normal: callback deletes the temp file when the process exits cleanly."
  (let ((temp (make-temp-file "dwim-pass-test-"))
        (pbuf (generate-new-buffer " *test-proc*")))
    (unwind-protect
        (cl-letf (((symbol-function 'processp) (lambda (_) t))
                  ((symbol-function 'process-exit-status) (lambda (_) 0)))
          (funcall (cj/dwim-shell--password-cleanup-callback temp) pbuf 'fake-proc)
          (should-not (file-exists-p temp)))
      (when (file-exists-p temp) (delete-file temp))
      (when (buffer-live-p pbuf) (kill-buffer pbuf)))))

(ert-deftest test-dwim-password-cleanup-deletes-on-error ()
  "Error: callback still deletes the temp file when the process exits non-zero."
  (let ((temp (make-temp-file "dwim-pass-test-"))
        (pbuf (generate-new-buffer " *test-proc*")))
    (unwind-protect
        (cl-letf (((symbol-function 'processp) (lambda (_) t))
                  ((symbol-function 'process-exit-status) (lambda (_) 1))
                  ((symbol-function 'display-buffer) (lambda (&rest _) nil)))
          (funcall (cj/dwim-shell--password-cleanup-callback temp) pbuf 'fake-proc)
          (should-not (file-exists-p temp)))
      (when (file-exists-p temp) (delete-file temp))
      (when (buffer-live-p pbuf) (kill-buffer pbuf)))))

(ert-deftest test-dwim-password-cleanup-missing-file-no-error ()
  "Boundary: callback does not error when the temp file is already gone."
  (let ((temp (make-temp-file "dwim-pass-test-"))
        (pbuf (generate-new-buffer " *test-proc*")))
    (delete-file temp)
    (unwind-protect
        (cl-letf (((symbol-function 'processp) (lambda (_) t))
                  ((symbol-function 'process-exit-status) (lambda (_) 0)))
          (funcall (cj/dwim-shell--password-cleanup-callback temp) pbuf 'fake-proc)
          (should-not (file-exists-p temp)))
      (when (buffer-live-p pbuf) (kill-buffer pbuf)))))

;; ---------------------------------------------------------------------------
;;; cj/dwim-shell--run-with-password-file
;; ---------------------------------------------------------------------------

(ert-deftest test-dwim-run-with-password-file-writes-and-defers-cleanup ()
  "Normal: writes a mode-600 temp file, passes :on-completion, keeps the file
alive after a successful launch (the callback owns deletion)."
  (let (captured-script captured-keys)
    (cl-letf (((symbol-function 'dwim-shell-command-on-marked-files)
               (lambda (_buffer-name script &rest keys)
                 (setq captured-script script
                       captured-keys keys))))
      (cj/dwim-shell--run-with-password-file
       "secret-pw" "Test op"
       (lambda (temp-file) (format "tool --password-file='%s' '<<f>>'" temp-file))
       :utils "tool")
      ;; the script embeds a real temp path, and that file still exists
      (should (string-match "--password-file='\\([^']*\\)'" captured-script))
      (let ((temp (match-string 1 captured-script)))
        (unwind-protect
            (progn
              (should (file-exists-p temp))
              (should (string= "secret-pw"
                               (with-temp-buffer (insert-file-contents temp)
                                                 (buffer-string))))
              (should (eq #o600 (file-modes temp)))
              (should (functionp (plist-get captured-keys :on-completion)))
              (should (equal "tool" (plist-get captured-keys :utils))))
          (when (file-exists-p temp) (delete-file temp)))))))

(ert-deftest test-dwim-run-with-password-file-cleans-up-on-launch-failure ()
  "Error: if the async launch throws before the process starts, the temp file
is cleaned up synchronously (no orphaned password file)."
  (let (leaked-temp)
    (cl-letf (((symbol-function 'dwim-shell-command-on-marked-files)
               (lambda (_buffer-name script &rest _keys)
                 ;; capture the temp path the script was built with, then throw
                 (when (string-match "FILE='\\(.*\\)'" script)
                   (setq leaked-temp (match-string 1 script)))
                 (error "simulated launch failure"))))
      (should-error
       (cj/dwim-shell--run-with-password-file
        "secret-pw" "Test op"
        (lambda (temp-file) (format "tool FILE='%s'" temp-file))
        :utils "tool"))
      (should leaked-temp)
      (should-not (file-exists-p leaked-temp)))))

(provide 'test-dwim-shell-config-password-file)
;;; test-dwim-shell-config-password-file.el ends here