diff options
Diffstat (limited to 'llm-tools-hl-test.el')
| -rw-r--r-- | llm-tools-hl-test.el | 180 |
1 files changed, 179 insertions, 1 deletions
diff --git a/llm-tools-hl-test.el b/llm-tools-hl-test.el index 4bc0fa0..85777c8 100644 --- a/llm-tools-hl-test.el +++ b/llm-tools-hl-test.el | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | (require 'llm-tools-hl) | 2 | (require 'llm-tools-hl) |
| 3 | 3 | ||
| 4 | (ert-deftest llm-tools--hl-file-read-test () | 4 | (ert-deftest llm-tools--hl-file-read-test () |
| 5 | "Test that 'llm-tools--hl-file-read' produces correct hashline format." | 5 | "Test that 'llm-tools--hl-file-read' produces correct hashline format." |
| 6 | (let* ((content "\ | 6 | (let* ((content "\ |
| 7 | const TITLE = \"Mr\"; | 7 | const TITLE = \"Mr\"; |
| 8 | export function greet(name) { | 8 | export function greet(name) { |
| @@ -23,3 +23,181 @@ export function greet(name) { | |||
| 23 | (unwind-protect | 23 | (unwind-protect |
| 24 | (should (equal actual expected)) | 24 | (should (equal actual expected)) |
| 25 | (delete-file file)))) | 25 | (delete-file file)))) |
| 26 | |||
| 27 | (ert-deftest llm-tools--hl-anchor-to-line-num-test () | ||
| 28 | (should (= (llm-tools--hl-anchor-to-line-num "5ff") 5)) | ||
| 29 | (should (= (llm-tools--hl-anchor-to-line-num "123ab") 123)) | ||
| 30 | (should (= (llm-tools--hl-anchor-to-line-num "1vx") 1))) | ||
| 31 | |||
| 32 | (ert-deftest llm-tools--hl-parse-section-single-replace () | ||
| 33 | (let ((section "= 1vx..1vx\n~const TITLE = \"Mrs\";")) | ||
| 34 | (should (= (length (llm-tools--hl-parse-section section)) 1)) | ||
| 35 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 36 | (should (eq (hl-op-type op) 'replace)) | ||
| 37 | (should (equal (hl-op-range op) '("1vx" "1vx"))) | ||
| 38 | (should (equal (hl-op-payload op) '("const TITLE = \"Mrs\";")))))) | ||
| 39 | |||
| 40 | (ert-deftest llm-tools--hl-parse-section-multi-line-payload () | ||
| 41 | (let ((section "= 3gv..6be\n~\treturn [\n~\t\t\"Mrs\",\n~\t\tname?.trim() || \"guest\",\n~\t].join(\" \");")) | ||
| 42 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 43 | (should (= (length (hl-op-payload op)) 4))))) | ||
| 44 | |||
| 45 | (ert-deftest llm-tools--hl-parse-section-blank-replace () | ||
| 46 | "Replace with no payload should produce nil payload, not empty list." | ||
| 47 | (let ((section "= 5ff..5ff")) | ||
| 48 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 49 | (should (eq (hl-op-type op) 'replace)) | ||
| 50 | (should (null (hl-op-payload op)))))) | ||
| 51 | |||
| 52 | (ert-deftest llm-tools--hl-parse-section-insert-after () | ||
| 53 | (let ((section "+ 4ei\n~\t\t\"Dr\",")) | ||
| 54 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 55 | (should (eq (hl-op-type op) 'insert-after)) | ||
| 56 | (should (string= (hl-op-anchor op) "4ei")) | ||
| 57 | (should (equal (hl-op-payload op) '("\t\t\"Dr\",")))))) | ||
| 58 | |||
| 59 | (ert-deftest llm-tools--hl-parse-section-insert-before () | ||
| 60 | (let ((section "< 5ff\n~\t\t\"Dr\",")) | ||
| 61 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 62 | (should (eq (hl-op-type op) 'insert-before)) | ||
| 63 | (should (string= (hl-op-anchor op) "5ff"))))) | ||
| 64 | |||
| 65 | (ert-deftest llm-tools--hl-parse-section-delete () | ||
| 66 | (let ((section "- 5ff..5ff")) | ||
| 67 | (let ((op (car (llm-tools--hl-parse-section section)))) | ||
| 68 | (should (eq (hl-op-type op) 'delete)) | ||
| 69 | (should (equal (hl-op-range op) '("5ff" "5ff")))))) | ||
| 70 | |||
| 71 | (ert-deftest llm-tools--hl-parse-section-skip-comments-and-blanks () | ||
| 72 | (let ((section "# comment\n\n+ 4ei\n~line")) | ||
| 73 | (should (= (length (llm-tools--hl-parse-section section)) 1)))) | ||
| 74 | |||
| 75 | (ert-deftest llm-tools--hl-parse-section-multiple-ops () | ||
| 76 | "Two separate insert ops in one section." | ||
| 77 | (let ((section "+ 4ei\n~first\n< 5ff\n~second")) | ||
| 78 | (let ((ops (llm-tools--hl-parse-section section))) | ||
| 79 | (should (= (length ops) 2)) | ||
| 80 | (should (eq (hl-op-type (car ops)) 'insert-after)) | ||
| 81 | (should (eq (hl-op-type (cadr ops)) 'insert-before))))) | ||
| 82 | |||
| 83 | (ert-deftest llm-tools--hl-apply-replace-single-line () | ||
| 84 | (let* ((content "line1\nline2\nline3\n") | ||
| 85 | (file (make-temp-file nil nil nil content)) | ||
| 86 | (patch (format "@@ test\n= 2%s..2%s\n~REPLACED" (elt 1 (llm-tools--hl-hash (string-lines content)))))) | ||
| 87 | (unwind-protect | ||
| 88 | (progn | ||
| 89 | (llm-tools--hl-apply patch) | ||
| 90 | (should (string= (llm-tools--hl-file-read file) | ||
| 91 | (string-join | ||
| 92 | (list (format "1%s|line1" (llm-tools--hl-hash "line1")) | ||
| 93 | (format "2%s|REPLACED" (llm-tools--hl-hash "REPLACED")) | ||
| 94 | (format "3%s|line3" (llm-tools--hl-hash "line3"))) | ||
| 95 | "\n")))) | ||
| 96 | (delete-file file)))) | ||
| 97 | |||
| 98 | (ert-deftest llm-tools--hl-apply-insert-after () | ||
| 99 | (let* ((content "A\nB\nC\n") | ||
| 100 | (file (make-temp-file nil nil nil content)) | ||
| 101 | (anchor (format "+ %s" (llm-tools--hl-file-line-hash file 1))) | ||
| 102 | (patch (concat "@@ test\n" anchor "\n~NEW"))) | ||
| 103 | (unwind-protect | ||
| 104 | (progn | ||
| 105 | (llm-tools--hl-apply patch) | ||
| 106 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 107 | (should (equal lines '("A" "NEW" "B" "C"))))) | ||
| 108 | (delete-file file)))) | ||
| 109 | |||
| 110 | (ert-deftest llm-tools--hl-apply-insert-before () | ||
| 111 | (let* ((content "A\nB\nC\n") | ||
| 112 | (file (make-temp-file nil nil nil content)) | ||
| 113 | (anchor (format "< %s" (llm-tools--hl-file-line-hash file 3))) | ||
| 114 | (patch (concat "@@ test\n" anchor "\n~NEW"))) | ||
| 115 | (unwind-protect | ||
| 116 | (progn | ||
| 117 | (llm-tools--hl-apply patch) | ||
| 118 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 119 | (should (equal lines '("A" "B" "NEW" "C"))))) | ||
| 120 | (delete-file file)))) | ||
| 121 | |||
| 122 | (ert-deftest llm-tools--hl-apply-append-to-eof () | ||
| 123 | (let* ((content "A\nB\n") | ||
| 124 | (file (make-temp-file nil nil nil content)) | ||
| 125 | (patch "@@ test\n+ EOF\n~APPENDED")) | ||
| 126 | (unwind-protect | ||
| 127 | (progn | ||
| 128 | (llm-tools--hl-apply patch) | ||
| 129 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 130 | (should (equal lines '("A" "B" "APPENDED"))))) | ||
| 131 | (delete-file file)))) | ||
| 132 | |||
| 133 | (ert-deftest llm-tools--hl-apply-delete () | ||
| 134 | (let* ((content "A\nB\nC\n") | ||
| 135 | (file (make-temp-file nil nil nil content)) | ||
| 136 | (anchor (llm-tools--hl-file-line-hash file 2)) | ||
| 137 | (patch (format "@@ test\n- 2%s..2%s" anchor anchor))) | ||
| 138 | (unwind-protect | ||
| 139 | (progn | ||
| 140 | (llm-tools--hl-apply patch) | ||
| 141 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 142 | (should (equal lines '("A" "C"))))) | ||
| 143 | (delete-file file)))) | ||
| 144 | |||
| 145 | (ert-deftest llm-tools--hl-apply-blank-line () | ||
| 146 | "Replace with no payload produces a blank line." | ||
| 147 | (let* ((content "A\nB\nC\n") | ||
| 148 | (file (make-temp-file nil nil nil content)) | ||
| 149 | (anchor (llm-tools--hl-file-line-hash file 2)) | ||
| 150 | (patch (format "@@ test\n= 2%s..2%s" anchor anchor))) | ||
| 151 | (unwind-protect | ||
| 152 | (progn | ||
| 153 | (llm-tools--hl-apply patch) | ||
| 154 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 155 | (should (equal lines '("A" "" "C"))))) | ||
| 156 | (delete-file file)))) | ||
| 157 | |||
| 158 | (ert-deftest llm-tools--hl-apply-offset-tracking () | ||
| 159 | "Multiple ops in one file: verify offset adjusts subsequent line numbers." | ||
| 160 | (let* ((content "A\nB\nC\nD\nE\n") | ||
| 161 | (file (make-temp-file nil nil nil content)) | ||
| 162 | (a1 (llm-tools--hl-file-line-hash file 1)) | ||
| 163 | (a3 (llm-tools--hl-file-line-hash file 3)) | ||
| 164 | ;; Insert 2 lines after line 1, then delete original line 3 | ||
| 165 | (patch (format "@@ test\n+ 1%s\n~X\n~Y\n- 3%s..3%s" a1 a3 a3))) | ||
| 166 | (unwind-protect | ||
| 167 | (progn | ||
| 168 | (llm-tools--hl-apply patch) | ||
| 169 | ;; After insert: A X Y B C D E (offset = +2) | ||
| 170 | ;; Original line 3 (C) is now at index 5 (1-based). Delete it. | ||
| 171 | ;; Result: A X Y B D E | ||
| 172 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 173 | (should (equal lines '("A" "X" "Y" "B" "D" "E"))))) | ||
| 174 | (delete-file file)))) | ||
| 175 | |||
| 176 | (ert-deftest llm-tools--hl-apply-multi-file () | ||
| 177 | "Two files in one patch both get modified." | ||
| 178 | (let* ((file1 (make-temp-file nil nil nil "A\nB\n")) | ||
| 179 | (file2 (make-temp-file nil nil nil "X\nY\n")) | ||
| 180 | (a1 (llm-tools--hl-file-line-hash file1 1)) | ||
| 181 | (a2 (llm-tools--hl-file-line-hash file2 2)) | ||
| 182 | (patch (format "@@ %s\n+ 1%s\n~NEW1\n@@ %s\n= 2%s..2%s\n~NEW2" | ||
| 183 | file1 a1 file2 a2 a2))) | ||
| 184 | (unwind-protect | ||
| 185 | (progn | ||
| 186 | (llm-tools--hl-apply patch) | ||
| 187 | (should (equal (llm-tools--hl-file-lines file1) '("A" "NEW1" "B"))) | ||
| 188 | (should (equal (llm-tools--hl-file-lines file2) '("X" "NEW2")))) | ||
| 189 | (delete-file file1) | ||
| 190 | (delete-file file2)))) | ||
| 191 | |||
| 192 | (ert-deftest llm-tools--hl-apply-multi-line-payload () | ||
| 193 | "Replace with many payload lines." | ||
| 194 | (let* ((content "A\nB\n") | ||
| 195 | (file (make-temp-file nil nil nil content)) | ||
| 196 | (a1 (llm-tools--hl-file-line-hash file 1)) | ||
| 197 | (patch (format "@@ test\n= 1%s..1%s\n~ONE\n~TWO\n~THREE" a1 a1))) | ||
| 198 | (unwind-protect | ||
| 199 | (progn | ||
| 200 | (llm-tools--hl-apply patch) | ||
| 201 | (let ((lines (llm-tools--hl-file-lines file))) | ||
| 202 | (should (equal lines '("ONE" "TWO" "THREE" "B"))))) | ||
| 203 | (delete-file file)))) | ||
