diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-integration-transcription.el | 145 | ||||
| -rw-r--r-- | tests/test-transcription-audio-file.el | 83 | ||||
| -rw-r--r-- | tests/test-transcription-counter.el | 98 | ||||
| -rw-r--r-- | tests/test-transcription-duration.el | 58 | ||||
| -rw-r--r-- | tests/test-transcription-log-cleanup.el | 44 | ||||
| -rw-r--r-- | tests/test-transcription-paths.el | 80 |
6 files changed, 508 insertions, 0 deletions
diff --git a/tests/test-integration-transcription.el b/tests/test-integration-transcription.el new file mode 100644 index 00000000..96b617bc --- /dev/null +++ b/tests/test-integration-transcription.el @@ -0,0 +1,145 @@ +;;; test-integration-transcription.el --- Integration tests for transcription -*- lexical-binding: t; -*- + +;;; Commentary: +;; End-to-end integration tests for transcription workflow +;; Tests complete workflow with temporary files and mocked processes +;; Categories: Normal workflow, Error handling, Cleanup + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Test Helpers ---------------------------------- + +(defun test-transcription--make-mock-audio-file () + "Create a temporary mock audio file for testing. +Returns the absolute path to the file." + (let ((file (make-temp-file "test-audio-" nil ".m4a"))) + (with-temp-file file + (insert "Mock audio data")) + file)) + +(defun test-transcription--cleanup-output-files (audio-file) + "Delete transcript and log files associated with AUDIO-FILE." + (let* ((outputs (cj/--transcription-output-files audio-file)) + (txt-file (car outputs)) + (log-file (cdr outputs))) + (when (file-exists-p txt-file) + (delete-file txt-file)) + (when (file-exists-p log-file) + (delete-file log-file)))) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-integration-transcription-output-files-created () + "Test that .txt and .log files are created for audio file." + (let* ((audio-file (test-transcription--make-mock-audio-file)) + (outputs (cj/--transcription-output-files audio-file)) + (txt-file (car outputs)) + (log-file (cdr outputs))) + (unwind-protect + (progn + ;; Verify output file paths are correct + (should (string-suffix-p ".txt" txt-file)) + (should (string-suffix-p ".log" log-file)) + (should (string= (file-name-sans-extension txt-file) + (file-name-sans-extension audio-file))) + (should (string= (file-name-sans-extension log-file) + (file-name-sans-extension audio-file)))) + ;; Cleanup + (delete-file audio-file) + (test-transcription--cleanup-output-files audio-file)))) + +(ert-deftest test-integration-transcription-validates-file-exists () + "Test that transcription fails for non-existent file." + (should-error + (cj/--start-transcription-process "/nonexistent/audio.m4a") + :type 'user-error)) + +(ert-deftest test-integration-transcription-validates-audio-extension () + "Test that transcription fails for non-audio file." + (let ((non-audio (make-temp-file "test-" nil ".txt"))) + (unwind-protect + (should-error + (cj/--start-transcription-process non-audio) + :type 'user-error) + (delete-file non-audio)))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-integration-transcription-audio-file-detection () + "Test various audio file extensions are accepted." + (dolist (ext '("m4a" "mp3" "wav" "flac" "ogg" "opus")) + (let ((audio-file (make-temp-file "test-audio-" nil (concat "." ext)))) + (unwind-protect + (progn + (should (cj/--audio-file-p audio-file)) + ;; Would start transcription if script existed + ) + (delete-file audio-file))))) + +(ert-deftest test-integration-transcription-filename-with-spaces () + "Test transcription with audio file containing spaces." + (let ((audio-file (make-temp-file "test audio file" nil ".m4a"))) + (unwind-protect + (let* ((outputs (cj/--transcription-output-files audio-file)) + (txt-file (car outputs)) + (log-file (cdr outputs))) + (should (file-name-absolute-p txt-file)) + (should (file-name-absolute-p log-file))) + (delete-file audio-file)))) + +(ert-deftest test-integration-transcription-filename-with-special-chars () + "Test transcription with special characters in filename." + (let ((audio-file (make-temp-file "test_(final)" nil ".m4a"))) + (unwind-protect + (let* ((outputs (cj/--transcription-output-files audio-file)) + (txt-file (car outputs))) + ;; make-temp-file adds random suffix, so just check it ends with .txt + ;; and contains the special chars + (should (string-suffix-p ".txt" txt-file)) + (should (string-match-p "test_(final)" txt-file))) + (delete-file audio-file)))) + +;; ----------------------------- Cleanup Tests --------------------------------- + +(ert-deftest test-integration-transcription-cleanup-completed () + "Test that completed transcriptions are removed from tracking." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running) + (proc2 "file2.m4a" nil complete) + (proc3 "file3.m4a" nil error)))) + (cj/--cleanup-completed-transcriptions) + (should (= 1 (length cj/transcriptions-list))) + (should (eq 'running (nth 3 (car cj/transcriptions-list)))))) + +(ert-deftest test-integration-transcription-cleanup-all-complete () + "Test cleanup when all transcriptions are complete." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil complete) + (proc2 "file2.m4a" nil error)))) + (cj/--cleanup-completed-transcriptions) + (should (null cj/transcriptions-list)))) + +(ert-deftest test-integration-transcription-cleanup-preserves-running () + "Test that running transcriptions are not cleaned up." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running) + (proc2 "file2.m4a" nil running)))) + (cj/--cleanup-completed-transcriptions) + (should (= 2 (length cj/transcriptions-list))))) + +;; ----------------------------- Backend Tests --------------------------------- + +(ert-deftest test-integration-transcription-script-path-exists () + "Test that transcription scripts exist in expected location." + (dolist (backend '(local-whisper openai-api)) + (let ((cj/transcribe-backend backend)) + (let ((script (cj/--transcription-script-path))) + (should (file-name-absolute-p script)) + ;; Note: Script may not exist in test environment, just check path format + (should (string-match-p "scripts/" script)))))) + +(provide 'test-integration-transcription) +;;; test-integration-transcription.el ends here diff --git a/tests/test-transcription-audio-file.el b/tests/test-transcription-audio-file.el new file mode 100644 index 00000000..f40d9ca6 --- /dev/null +++ b/tests/test-transcription-audio-file.el @@ -0,0 +1,83 @@ +;;; test-transcription-audio-file.el --- Tests for audio file detection -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--audio-file-p function +;; Categories: Normal cases, Boundary cases, Error cases + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-cj/--audio-file-p-m4a () + "Test that .m4a files are recognized as audio." + (should (cj/--audio-file-p "meeting.m4a"))) + +(ert-deftest test-cj/--audio-file-p-mp3 () + "Test that .mp3 files are recognized as audio." + (should (cj/--audio-file-p "podcast.mp3"))) + +(ert-deftest test-cj/--audio-file-p-wav () + "Test that .wav files are recognized as audio." + (should (cj/--audio-file-p "recording.wav"))) + +(ert-deftest test-cj/--audio-file-p-flac () + "Test that .flac files are recognized as audio." + (should (cj/--audio-file-p "music.flac"))) + +(ert-deftest test-cj/--audio-file-p-with-path () + "Test audio file recognition with full path." + (should (cj/--audio-file-p "/home/user/recordings/meeting.m4a"))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-cj/--audio-file-p-uppercase-extension () + "Test that uppercase extensions are recognized." + (should (cj/--audio-file-p "MEETING.M4A"))) + +(ert-deftest test-cj/--audio-file-p-mixed-case () + "Test that mixed case extensions are recognized." + (should (cj/--audio-file-p "podcast.Mp3"))) + +(ert-deftest test-cj/--audio-file-p-no-extension () + "Test that files without extension are not recognized." + (should-not (cj/--audio-file-p "meeting"))) + +(ert-deftest test-cj/--audio-file-p-empty-string () + "Test that empty string is not recognized as audio." + (should-not (cj/--audio-file-p ""))) + +(ert-deftest test-cj/--audio-file-p-dotfile () + "Test that dotfiles without proper extension are not recognized." + (should-not (cj/--audio-file-p ".hidden"))) + +(ert-deftest test-cj/--audio-file-p-multiple-dots () + "Test file with multiple dots but audio extension." + (should (cj/--audio-file-p "meeting.2025-11-04.final.m4a"))) + +;; ------------------------------ Error Cases ---------------------------------- + +(ert-deftest test-cj/--audio-file-p-not-audio () + "Test that non-audio files are not recognized." + (should-not (cj/--audio-file-p "document.pdf"))) + +(ert-deftest test-cj/--audio-file-p-text-file () + "Test that text files are not recognized as audio." + (should-not (cj/--audio-file-p "notes.txt"))) + +(ert-deftest test-cj/--audio-file-p-org-file () + "Test that org files are not recognized as audio." + (should-not (cj/--audio-file-p "tasks.org"))) + +(ert-deftest test-cj/--audio-file-p-video-file () + "Test that video files are not recognized as audio." + (should-not (cj/--audio-file-p "video.mp4"))) + +(ert-deftest test-cj/--audio-file-p-nil () + "Test that nil input returns nil." + (should-not (cj/--audio-file-p nil))) + +(provide 'test-transcription-audio-file) +;;; test-transcription-audio-file.el ends here diff --git a/tests/test-transcription-counter.el b/tests/test-transcription-counter.el new file mode 100644 index 00000000..fae353ba --- /dev/null +++ b/tests/test-transcription-counter.el @@ -0,0 +1,98 @@ +;;; test-transcription-counter.el --- Tests for active transcription counting -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--count-active-transcriptions and modeline integration +;; Categories: Normal cases, Boundary cases + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-cj/--count-active-transcriptions-empty () + "Test count when no transcriptions are active." + (let ((cj/transcriptions-list '())) + (should (= 0 (cj/--count-active-transcriptions))))) + +(ert-deftest test-cj/--count-active-transcriptions-one-running () + "Test count with one running transcription." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running)))) + (should (= 1 (cj/--count-active-transcriptions))))) + +(ert-deftest test-cj/--count-active-transcriptions-multiple-running () + "Test count with multiple running transcriptions." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running) + (proc2 "file2.m4a" nil running) + (proc3 "file3.m4a" nil running)))) + (should (= 3 (cj/--count-active-transcriptions))))) + +(ert-deftest test-cj/--count-active-transcriptions-mixed-status () + "Test count excludes completed/errored transcriptions." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running) + (proc2 "file2.m4a" nil complete) + (proc3 "file3.m4a" nil running) + (proc4 "file4.m4a" nil error)))) + (should (= 2 (cj/--count-active-transcriptions))))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-cj/--count-active-transcriptions-only-complete () + "Test count when all transcriptions are complete." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil complete) + (proc2 "file2.m4a" nil complete)))) + (should (= 0 (cj/--count-active-transcriptions))))) + +(ert-deftest test-cj/--count-active-transcriptions-only-error () + "Test count when all transcriptions errored." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil error) + (proc2 "file2.m4a" nil error)))) + (should (= 0 (cj/--count-active-transcriptions))))) + +;; ----------------------------- Modeline Tests -------------------------------- + +(ert-deftest test-cj/--transcription-modeline-string-none-active () + "Test modeline string when no transcriptions active." + (let ((cj/transcriptions-list '())) + (should-not (cj/--transcription-modeline-string)))) + +(ert-deftest test-cj/--transcription-modeline-string-one-active () + "Test modeline string with one active transcription." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running)))) + (let ((result (cj/--transcription-modeline-string))) + (should result) + (should (string-match-p "⏺1" result))))) + +(ert-deftest test-cj/--transcription-modeline-string-multiple-active () + "Test modeline string with multiple active transcriptions." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running) + (proc2 "file2.m4a" nil running) + (proc3 "file3.m4a" nil running)))) + (let ((result (cj/--transcription-modeline-string))) + (should result) + (should (string-match-p "⏺3" result))))) + +(ert-deftest test-cj/--transcription-modeline-string-has-help-echo () + "Test that modeline string has help-echo property." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running)))) + (let ((result (cj/--transcription-modeline-string))) + (should (get-text-property 0 'help-echo result))))) + +(ert-deftest test-cj/--transcription-modeline-string-has-face () + "Test that modeline string has warning face." + (let ((cj/transcriptions-list + '((proc1 "file1.m4a" nil running)))) + (let ((result (cj/--transcription-modeline-string))) + (should (eq 'warning (get-text-property 0 'face result)))))) + +(provide 'test-transcription-counter) +;;; test-transcription-counter.el ends here diff --git a/tests/test-transcription-duration.el b/tests/test-transcription-duration.el new file mode 100644 index 00000000..370c439b --- /dev/null +++ b/tests/test-transcription-duration.el @@ -0,0 +1,58 @@ +;;; test-transcription-duration.el --- Tests for duration calculation -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--transcription-duration function +;; Categories: Normal cases, Boundary cases + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-cj/--transcription-duration-zero-seconds () + "Test duration calculation for current time (should be 00:00)." + (let ((now (current-time))) + (should (string= (cj/--transcription-duration now) "00:00")))) + +(ert-deftest test-cj/--transcription-duration-30-seconds () + "Test duration calculation for 30 seconds ago." + (let ((start-time (time-subtract (current-time) (seconds-to-time 30)))) + (should (string= (cj/--transcription-duration start-time) "00:30")))) + +(ert-deftest test-cj/--transcription-duration-1-minute () + "Test duration calculation for 1 minute ago." + (let ((start-time (time-subtract (current-time) (seconds-to-time 60)))) + (should (string= (cj/--transcription-duration start-time) "01:00")))) + +(ert-deftest test-cj/--transcription-duration-2-minutes-30-seconds () + "Test duration calculation for 2:30 ago." + (let ((start-time (time-subtract (current-time) (seconds-to-time 150)))) + (should (string= (cj/--transcription-duration start-time) "02:30")))) + +(ert-deftest test-cj/--transcription-duration-10-minutes () + "Test duration calculation for 10 minutes ago." + (let ((start-time (time-subtract (current-time) (seconds-to-time 600)))) + (should (string= (cj/--transcription-duration start-time) "10:00")))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-cj/--transcription-duration-59-seconds () + "Test duration just before 1 minute." + (let ((start-time (time-subtract (current-time) (seconds-to-time 59)))) + (should (string= (cj/--transcription-duration start-time) "00:59")))) + +(ert-deftest test-cj/--transcription-duration-1-hour () + "Test duration for 1 hour (60 minutes)." + (let ((start-time (time-subtract (current-time) (seconds-to-time 3600)))) + (should (string= (cj/--transcription-duration start-time) "60:00")))) + +(ert-deftest test-cj/--transcription-duration-format () + "Test that duration is always in MM:SS format with zero-padding." + (let ((start-time (time-subtract (current-time) (seconds-to-time 65)))) + (let ((result (cj/--transcription-duration start-time))) + (should (string-match-p "^[0-9][0-9]:[0-9][0-9]$" result))))) + +(provide 'test-transcription-duration) +;;; test-transcription-duration.el ends here diff --git a/tests/test-transcription-log-cleanup.el b/tests/test-transcription-log-cleanup.el new file mode 100644 index 00000000..82c902d8 --- /dev/null +++ b/tests/test-transcription-log-cleanup.el @@ -0,0 +1,44 @@ +;;; test-transcription-log-cleanup.el --- Tests for log cleanup logic -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--should-keep-log function +;; Categories: Normal cases, Boundary cases + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-cj/--should-keep-log-success-keep-disabled () + "Test that logs are deleted on success when keep-log is nil." + (let ((cj/transcription-keep-log-when-done nil)) + (should-not (cj/--should-keep-log t)))) + +(ert-deftest test-cj/--should-keep-log-success-keep-enabled () + "Test that logs are kept on success when keep-log is t." + (let ((cj/transcription-keep-log-when-done t)) + (should (cj/--should-keep-log t)))) + +(ert-deftest test-cj/--should-keep-log-error-keep-disabled () + "Test that logs are always kept on error, even if keep-log is nil." + (let ((cj/transcription-keep-log-when-done nil)) + (should (cj/--should-keep-log nil)))) + +(ert-deftest test-cj/--should-keep-log-error-keep-enabled () + "Test that logs are kept on error when keep-log is t." + (let ((cj/transcription-keep-log-when-done t)) + (should (cj/--should-keep-log nil)))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-cj/--should-keep-log-default-behavior () + "Test default behavior (should not keep on success)." + ;; Default is nil based on defcustom + (let ((cj/transcription-keep-log-when-done nil)) + (should-not (cj/--should-keep-log t)) + (should (cj/--should-keep-log nil)))) + +(provide 'test-transcription-log-cleanup) +;;; test-transcription-log-cleanup.el ends here diff --git a/tests/test-transcription-paths.el b/tests/test-transcription-paths.el new file mode 100644 index 00000000..5ee80e67 --- /dev/null +++ b/tests/test-transcription-paths.el @@ -0,0 +1,80 @@ +;;; test-transcription-paths.el --- Tests for transcription file path logic -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for cj/--transcription-output-files and cj/--transcription-script-path +;; Categories: Normal cases, Boundary cases, Error cases + +;;; Code: + +(require 'ert) +(require 'transcription-config) + +;; ----------------------------- Normal Cases ---------------------------------- + +(ert-deftest test-cj/--transcription-output-files-simple () + "Test output file paths for simple filename." + (let ((result (cj/--transcription-output-files "meeting.m4a"))) + (should (string= (car result) "meeting.txt")) + (should (string= (cdr result) "meeting.log")))) + +(ert-deftest test-cj/--transcription-output-files-with-path () + "Test output file paths with full path." + (let ((result (cj/--transcription-output-files "/home/user/audio/podcast.mp3"))) + (should (string= (car result) "/home/user/audio/podcast.txt")) + (should (string= (cdr result) "/home/user/audio/podcast.log")))) + +(ert-deftest test-cj/--transcription-output-files-different-extensions () + "Test output files for various audio extensions." + (dolist (ext '("m4a" "mp3" "wav" "flac" "ogg")) + (let* ((input (format "audio.%s" ext)) + (result (cj/--transcription-output-files input))) + (should (string= (car result) "audio.txt")) + (should (string= (cdr result) "audio.log"))))) + +;; ----------------------------- Boundary Cases -------------------------------- + +(ert-deftest test-cj/--transcription-output-files-multiple-dots () + "Test output files for filename with multiple dots." + (let ((result (cj/--transcription-output-files "meeting.2025-11-04.final.m4a"))) + (should (string= (car result) "meeting.2025-11-04.final.txt")) + (should (string= (cdr result) "meeting.2025-11-04.final.log")))) + +(ert-deftest test-cj/--transcription-output-files-no-extension () + "Test output files for filename without extension." + (let ((result (cj/--transcription-output-files "meeting"))) + (should (string= (car result) "meeting.txt")) + (should (string= (cdr result) "meeting.log")))) + +(ert-deftest test-cj/--transcription-output-files-spaces-in-name () + "Test output files for filename with spaces." + (let ((result (cj/--transcription-output-files "team meeting 2025.m4a"))) + (should (string= (car result) "team meeting 2025.txt")) + (should (string= (cdr result) "team meeting 2025.log")))) + +(ert-deftest test-cj/--transcription-output-files-special-chars () + "Test output files for filename with special characters." + (let ((result (cj/--transcription-output-files "meeting_(final).m4a"))) + (should (string= (car result) "meeting_(final).txt")) + (should (string= (cdr result) "meeting_(final).log")))) + +;; ----------------------------- Script Path Tests ----------------------------- + +(ert-deftest test-cj/--transcription-script-path-local-whisper () + "Test script path for local-whisper backend." + (let ((cj/transcribe-backend 'local-whisper)) + (should (string-suffix-p "scripts/local-whisper" + (cj/--transcription-script-path))))) + +(ert-deftest test-cj/--transcription-script-path-openai-api () + "Test script path for openai-api backend." + (let ((cj/transcribe-backend 'openai-api)) + (should (string-suffix-p "scripts/oai-transcribe" + (cj/--transcription-script-path))))) + +(ert-deftest test-cj/--transcription-script-path-absolute () + "Test that script path is absolute." + (let ((path (cj/--transcription-script-path))) + (should (file-name-absolute-p path)))) + +(provide 'test-transcription-paths) +;;; test-transcription-paths.el ends here |
