@@ -411,6 +411,261 @@ and ensures everything is cleaned up afterward."
411411 (when (file-exists-p test-file-2) (delete-file test-file-2))
412412 (when (file-exists-p test-file-3) (delete-file test-file-3))))))
413413
414+ (ert-deftest ai-code-test-normalize-path ()
415+ " Test that ai-code--normalize-path returns correct normalized paths."
416+ (ai-code-with-test-repo
417+ (let ((existing-file mock-file-in-repo)
418+ (non-existing-file (expand-file-name " non-existent.el" git-root)))
419+ ; ; Test with existing file - should return truename
420+ (let ((result (ai-code--normalize-path existing-file)))
421+ (should (string= result (file-truename existing-file))))
422+
423+ ; ; Test with non-existing file - should return expanded path
424+ (let ((result (ai-code--normalize-path non-existing-file)))
425+ (should (string= result (expand-file-name non-existing-file)))))))
426+
427+ (ert-deftest ai-code-test-candidate-path-in-repo ()
428+ " Test that ai-code--candidate-path returns relative path for in-repo files."
429+ (ai-code-with-test-repo
430+ (let ((test-file (expand-file-name " src/test.el" git-root)))
431+ (unwind-protect
432+ (progn
433+ ; ; Create test file
434+ (make-directory (file-name-directory test-file) t )
435+ (with-temp-file test-file (insert " content" ))
436+
437+ (let ((result (ai-code--candidate-path test-file (file-truename git-root))))
438+ ; ; Should return relative path with @ prefix
439+ (should (string= result " @src/test.el" ))))
440+
441+ ; ; Cleanup
442+ (when (file-exists-p test-file) (delete-file test-file))))))
443+
444+ (ert-deftest ai-code-test-candidate-path-out-of-repo ()
445+ " Test that ai-code--candidate-path returns absolute path for out-of-repo files."
446+ (ai-code-with-test-repo
447+ (let ((out-file (expand-file-name " outside.el" temporary-file-directory)))
448+ (unwind-protect
449+ (progn
450+ ; ; Create file outside repo
451+ (with-temp-file out-file (insert " content" ))
452+
453+ (let ((result (ai-code--candidate-path out-file (file-truename git-root))))
454+ ; ; Should return absolute path (truename)
455+ (should (string= result (file-truename out-file)))))
456+
457+ ; ; Cleanup
458+ (when (file-exists-p out-file) (delete-file out-file))))))
459+
460+ (ert-deftest ai-code-test-visible-window-files ()
461+ " Test that ai-code--visible-window-files returns files from visible windows."
462+ (ai-code-with-test-repo
463+ (let ((test-file-1 (expand-file-name " file1.el" git-root))
464+ (test-file-2 (expand-file-name " file2.el" git-root)))
465+ (unwind-protect
466+ (progn
467+ ; ; Create test files
468+ (with-temp-file test-file-1 (insert " content1" ))
469+ (with-temp-file test-file-2 (insert " content2" ))
470+
471+ ; ; Open files in buffers
472+ (let ((buf1 (find-file-noselect test-file-1))
473+ (buf2 (find-file-noselect test-file-2)))
474+ (unwind-protect
475+ (progn
476+ ; ; Mock window-list to simulate visible windows
477+ (cl-letf (((symbol-function 'window-list )
478+ (lambda (&optional frame no-minibuf )
479+ (list (selected-window ))))
480+ ((symbol-function 'window-buffer )
481+ (lambda (win )
482+ (if (eq win (selected-window ))
483+ buf1
484+ buf2)))
485+ ((symbol-function 'selected-window )
486+ (lambda () 'mock-window )))
487+ (let ((result (ai-code--visible-window-files)))
488+ ; ; Should contain the file from the mocked window
489+ (should (member test-file-1 result))
490+ ; ; Should not filter by git repo (unlike old implementation)
491+ (should (= 1 (length result))))))
492+
493+ ; ; Kill buffers
494+ (when (buffer-live-p buf1) (kill-buffer buf1))
495+ (when (buffer-live-p buf2) (kill-buffer buf2)))))
496+
497+ ; ; Cleanup
498+ (when (file-exists-p test-file-1) (delete-file test-file-1))
499+ (when (file-exists-p test-file-2) (delete-file test-file-2))))))
500+
501+ (ert-deftest ai-code-test-recent-buffer-paths ()
502+ " Test that ai-code--recent-buffer-paths returns recent buffer paths."
503+ (ai-code-with-test-repo
504+ (let ((test-file-1 (expand-file-name " recent1.el" git-root))
505+ (test-file-2 (expand-file-name " recent2.el" git-root))
506+ (test-file-3 (expand-file-name " recent3.el" git-root)))
507+ (unwind-protect
508+ (progn
509+ ; ; Create test files
510+ (with-temp-file test-file-1 (insert " content1" ))
511+ (with-temp-file test-file-2 (insert " content2" ))
512+ (with-temp-file test-file-3 (insert " content3" ))
513+
514+ ; ; Open files in buffers (most recent first in buffer-list)
515+ (let ((buf1 (find-file-noselect test-file-1))
516+ (buf2 (find-file-noselect test-file-2))
517+ (buf3 (find-file-noselect test-file-3)))
518+ (unwind-protect
519+ (progn
520+ (let ((result (ai-code--recent-buffer-paths (file-truename git-root))))
521+ ; ; Should return candidate paths (relative with @ prefix for in-repo)
522+ (should (member " @recent1.el" result))
523+ (should (member " @recent2.el" result))
524+ (should (member " @recent3.el" result))
525+ ; ; Should limit to 5 files
526+ (should (<= (length result) 5 ))))
527+
528+ ; ; Kill buffers
529+ (when (buffer-live-p buf1) (kill-buffer buf1))
530+ (when (buffer-live-p buf2) (kill-buffer buf2))
531+ (when (buffer-live-p buf3) (kill-buffer buf3)))))
532+
533+ ; ; Cleanup
534+ (when (file-exists-p test-file-1) (delete-file test-file-1))
535+ (when (file-exists-p test-file-2) (delete-file test-file-2))
536+ (when (file-exists-p test-file-3) (delete-file test-file-3))))))
537+
538+ (ert-deftest ai-code-test-recent-buffer-paths-includes-dired ()
539+ " Test that ai-code--recent-buffer-paths includes dired directories."
540+ (ai-code-with-test-repo
541+ (let ((dired-dir (expand-file-name " testdir/" git-root))
542+ (dired-buf nil ))
543+ (unwind-protect
544+ (progn
545+ ; ; Create test directory
546+ (make-directory dired-dir t )
547+
548+ ; ; Open dired buffer
549+ (setq dired-buf (dired-noselect dired-dir))
550+
551+ (let ((result (ai-code--recent-buffer-paths (file-truename git-root))))
552+ ; ; Should include the dired directory
553+ (should (member " @testdir/" result))))
554+
555+ ; ; Cleanup
556+ (when (buffer-live-p dired-buf) (kill-buffer dired-buf))
557+ (when (file-directory-p dired-dir) (delete-directory dired-dir))))))
558+
559+ (ert-deftest ai-code-test-current-frame-dired-paths ()
560+ " Test that ai-code--current-frame-dired-paths returns dired directories."
561+ (ai-code-with-test-repo
562+ (let ((dired-dir-1 (expand-file-name " src/" git-root))
563+ (dired-dir-2 (expand-file-name " test/" git-root))
564+ (dired-buf-1 nil )
565+ (dired-buf-2 nil ))
566+ (unwind-protect
567+ (progn
568+ ; ; Create test directories
569+ (make-directory dired-dir-1 t )
570+ (make-directory dired-dir-2 t )
571+
572+ ; ; Open dired buffers
573+ (setq dired-buf-1 (dired-noselect dired-dir-1))
574+ (setq dired-buf-2 (dired-noselect dired-dir-2))
575+
576+ ; ; Mock window-list and git-ignored check
577+ (cl-letf (((symbol-function 'window-list )
578+ (lambda (&optional frame no-minibuf )
579+ (list 'win1 'win2 )))
580+ ((symbol-function 'window-buffer )
581+ (lambda (win )
582+ (if (eq win 'win1 ) dired-buf-1 dired-buf-2)))
583+ ((symbol-function 'ai-code--git-ignored-repo-file-p )
584+ (lambda (file root ) nil )))
585+
586+ (let ((result (ai-code--current-frame-dired-paths (file-truename git-root))))
587+ ; ; Should include both dired directories
588+ (should (member " @src/" result))
589+ (should (member " @test/" result)))))
590+
591+ ; ; Cleanup
592+ (when (buffer-live-p dired-buf-1) (kill-buffer dired-buf-1))
593+ (when (buffer-live-p dired-buf-2) (kill-buffer dired-buf-2))
594+ (when (file-directory-p dired-dir-1) (delete-directory dired-dir-1))
595+ (when (file-directory-p dired-dir-2) (delete-directory dired-dir-2))))))
596+
597+ (ert-deftest ai-code-test-prompt-filepath-candidates-prioritizes-visible-windows ()
598+ " Test that ai-code--prompt-filepath-candidates prioritizes visible window files."
599+ (ai-code-with-test-repo
600+ (let ((visible-file (expand-file-name " visible.el" git-root))
601+ (buffer-file (expand-file-name " buffer.el" git-root)))
602+ (unwind-protect
603+ (progn
604+ ; ; Create test files
605+ (with-temp-file visible-file (insert " visible" ))
606+ (with-temp-file buffer-file (insert " buffer" ))
607+
608+ ; ; Mock dependencies
609+ (cl-letf (((symbol-function 'ai-code--git-ignored-repo-file-p )
610+ (lambda (file root ) nil ))
611+ ((symbol-function 'ai-code--visible-window-files )
612+ (lambda () (list visible-file)))
613+ ((symbol-function 'ai-code--current-frame-dired-paths )
614+ (lambda (root ) '()))
615+ ((symbol-function 'ai-code--recent-buffer-paths )
616+ (lambda (root ) '()))
617+ ((symbol-function 'ai-code--buffer-file-list )
618+ (lambda (root skip ) (list buffer-file)))
619+ ((symbol-function 'ai-code--repo-recent-files )
620+ (lambda (root ) '())))
621+
622+ (let ((candidates (ai-code--prompt-filepath-candidates)))
623+ ; ; Visible file should come before buffer file
624+ (should (equal candidates '(" @visible.el" " @buffer.el" ))))))
625+
626+ ; ; Cleanup
627+ (when (file-exists-p visible-file) (delete-file visible-file))
628+ (when (file-exists-p buffer-file) (delete-file buffer-file))))))
629+
630+ (ert-deftest ai-code-test-prompt-filepath-candidates-includes-dired-directories ()
631+ " Test that ai-code--prompt-filepath-candidates includes dired directories from current frame."
632+ (ai-code-with-test-repo
633+ (let ((test-file (expand-file-name " file.el" git-root)))
634+ (unwind-protect
635+ (progn
636+ ; ; Create test file
637+ (with-temp-file test-file (insert " content" ))
638+
639+ ; ; Mock dependencies
640+ (cl-letf (((symbol-function 'ai-code--git-ignored-repo-file-p )
641+ (lambda (_file _root ) nil ))
642+ ((symbol-function 'ai-code--visible-window-files )
643+ (lambda () '()))
644+ ((symbol-function 'ai-code--current-frame-dired-paths )
645+ (lambda (_root ) '(" @src/" " @test/" )))
646+ ((symbol-function 'ai-code--recent-buffer-paths )
647+ (lambda (_root ) '()))
648+ ((symbol-function 'ai-code--buffer-file-list )
649+ (lambda (_root _skip ) (list test-file)))
650+ ((symbol-function 'ai-code--repo-recent-files )
651+ (lambda (_root ) '())))
652+
653+ (let ((candidates (ai-code--prompt-filepath-candidates)))
654+ ; ; Both dired directories should be included in candidates
655+ (should (member " @src/" candidates))
656+ (should (member " @test/" candidates))
657+ ; ; Test file should also be included
658+ (should (member " @file.el" candidates))
659+ ; ; Dired directories should come before buffer files
660+ (let ((src-pos (cl-position " @src/" candidates :test #'string= ))
661+ (test-pos (cl-position " @test/" candidates :test #'string= ))
662+ (file-pos (cl-position " @file.el" candidates :test #'string= )))
663+ (should (< src-pos file-pos))
664+ (should (< test-pos file-pos))))))
665+
666+ ; ; Cleanup
667+ (when (file-exists-p test-file) (delete-file test-file))))))
668+
414669(ert-deftest ai-code-test-prompt-filepath-candidates-excludes-current-file ()
415670 " Test that ai-code--prompt-filepath-candidates excludes the current file."
416671 (ai-code-with-test-repo
0 commit comments