mix deps support

This commit is contained in:
2026-01-24 20:08:09 +01:00
parent 293d97884c
commit 0290e123de
2 changed files with 514 additions and 5 deletions

213
test/test-ob-elixir-deps.el Normal file
View File

@@ -0,0 +1,213 @@
;;; test-ob-elixir-deps.el --- Deps block tests -*- lexical-binding: t; -*-
;; Copyright (C) 2024 Your Name
;; Author: Your Name <your.email@example.com>
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Tests for ob-elixir Mix dependencies support.
;;; Code:
(require 'ert)
(require 'ob-elixir)
;;; Parsing Tests
(ert-deftest ob-elixir-test-deps-block-parsing ()
"Test finding deps blocks in buffer."
(with-temp-buffer
(insert "#+BEGIN_DEPS elixir\n[{:jason, \"~> 1.4\"}]\n#+END_DEPS\n\n")
(insert "#+BEGIN_SRC elixir\nJason.encode!(%{})\n#+END_SRC\n")
(goto-char (point-max))
(let ((deps (ob-elixir--find-deps-for-position (point))))
(should deps)
(should (string-match-p ":jason" deps)))))
(ert-deftest ob-elixir-test-deps-block-override ()
"Test that later deps blocks override earlier ones."
(with-temp-buffer
(insert "#+BEGIN_DEPS elixir\n[{:jason, \"~> 1.4\"}]\n#+END_DEPS\n\n")
(insert "#+BEGIN_SRC elixir\ncode1\n#+END_SRC\n\n")
(insert "#+BEGIN_DEPS elixir\n[{:httpoison, \"~> 2.0\"}]\n#+END_DEPS\n\n")
(let ((pos (point)))
(insert "#+BEGIN_SRC elixir\ncode2\n#+END_SRC\n")
(let ((deps (ob-elixir--find-deps-for-position (+ pos 10))))
(should deps)
(should (string-match-p ":httpoison" deps))
(should-not (string-match-p ":jason" deps))))))
(ert-deftest ob-elixir-test-no-deps-block ()
"Test behavior when no deps block exists."
(with-temp-buffer
(insert "#+BEGIN_SRC elixir\n1 + 1\n#+END_SRC\n")
(should (null (ob-elixir--find-deps-for-position (point))))))
(ert-deftest ob-elixir-test-deps-block-before-position ()
"Test that deps block must be before the position."
(with-temp-buffer
(insert "#+BEGIN_SRC elixir\n1 + 1\n#+END_SRC\n\n")
(let ((pos (point)))
(insert "#+BEGIN_DEPS elixir\n[{:jason, \"~> 1.4\"}]\n#+END_DEPS\n")
;; Position is before deps block, should not find it
(should (null (ob-elixir--find-deps-for-position pos))))))
(ert-deftest ob-elixir-test-deps-hash-consistency ()
"Test that same deps produce same hash."
(let ((deps1 "[{:jason, \"~> 1.4\"}]")
(deps2 "[{:jason, \"~> 1.4\"}]") ; extra space
(deps3 "[{:httpoison, \"~> 2.0\"}]"))
(should (string= (ob-elixir--hash-deps deps1)
(ob-elixir--hash-deps deps2)))
(should-not (string= (ob-elixir--hash-deps deps1)
(ob-elixir--hash-deps deps3)))))
(ert-deftest ob-elixir-test-normalize-deps ()
"Test deps normalization."
;; Extra spaces between tokens get normalized to single space
(should (string= (ob-elixir--normalize-deps "[{:a, \"1\"}]")
(ob-elixir--normalize-deps "[{:a, \"1\"}]")))
;; Comments are stripped
(should (string= (ob-elixir--normalize-deps "[{:a, \"1\"}] # comment")
(ob-elixir--normalize-deps "[{:a, \"1\"}]"))))
(ert-deftest ob-elixir-test-normalize-deps-multiline ()
"Test normalization of multiline deps."
;; Multiline deps with same content should produce the same hash
(let ((multiline1 "[\n {:jason, \"~> 1.4\"},\n {:decimal, \"~> 2.0\"}\n]")
(multiline2 "[\n{:jason, \"~> 1.4\"},\n{:decimal, \"~> 2.0\"}\n]"))
;; Both should hash to the same value since they have equivalent whitespace normalization
(should (string= (ob-elixir--hash-deps multiline1)
(ob-elixir--hash-deps multiline2)))))
(ert-deftest ob-elixir-test-deps-block-with-comments ()
"Test deps block parsing with Elixir comments."
(with-temp-buffer
(insert "#+BEGIN_DEPS elixir\n")
(insert "[\n")
(insert " # JSON library\n")
(insert " {:jason, \"~> 1.4\"}\n")
(insert "]\n")
(insert "#+END_DEPS\n\n")
(insert "#+BEGIN_SRC elixir\ncode\n#+END_SRC\n")
(goto-char (point-max))
(let ((deps (ob-elixir--find-deps-for-position (point))))
(should deps)
(should (string-match-p ":jason" deps)))))
;;; Mix Project Template Tests
(ert-deftest ob-elixir-test-mix-exs-template ()
"Test that mix.exs template is valid."
(should (stringp ob-elixir--mix-exs-template))
(should (string-match-p "defmodule" ob-elixir--mix-exs-template))
(should (string-match-p "use Mix.Project" ob-elixir--mix-exs-template))
(should (string-match-p "deps()" ob-elixir--mix-exs-template))
(should (string-match-p "%s" ob-elixir--mix-exs-template)))
(ert-deftest ob-elixir-test-mix-exs-generation ()
"Test generating mix.exs content."
(let ((deps "[{:jason, \"~> 1.4\"}]")
(mix-exs (format ob-elixir--mix-exs-template "[{:jason, \"~> 1.4\"}]")))
(should (string-match-p ":jason" mix-exs))
(should (string-match-p "~> 1.4" mix-exs))))
;;; Cache Directory Tests
(ert-deftest ob-elixir-test-cache-dir-default ()
"Test default cache directory."
(should (stringp ob-elixir-deps-cache-dir))
(should (string-match-p "ob-elixir" ob-elixir-deps-cache-dir)))
;;; Integration Tests (require network and mix)
(ert-deftest ob-elixir-test-deps-project-creation ()
"Test creating a temporary Mix project with deps."
:tags '(:integration :network)
(skip-unless (and (executable-find "mix")
(executable-find "elixir")))
(let ((ob-elixir-deps-cache-dir (make-temp-file "ob-elixir-test-" t))
(deps "[{:jason, \"~> 1.4\"}]"))
(unwind-protect
(let ((project-dir (ob-elixir--get-deps-project deps)))
(should (file-directory-p project-dir))
(should (file-exists-p (expand-file-name "mix.exs" project-dir)))
(should (file-directory-p (expand-file-name "deps/jason" project-dir))))
;; Cleanup
(ob-elixir-cleanup-deps-projects)
(when (file-directory-p ob-elixir-deps-cache-dir)
(delete-directory ob-elixir-deps-cache-dir t)))))
(ert-deftest ob-elixir-test-deps-execution ()
"Test executing code with deps."
:tags '(:integration :network)
(skip-unless (and (executable-find "mix")
(executable-find "elixir")))
(let ((ob-elixir-deps-cache-dir (make-temp-file "ob-elixir-test-" t))
(deps "[{:jason, \"~> 1.4\"}]"))
(unwind-protect
(let ((result (ob-elixir--execute-with-deps
"Jason.encode!(%{a: 1})"
'value
deps)))
;; Result should contain JSON output with key "a"
;; Jason.encode! returns "{\"a\":1}" which when inspected becomes "\"{\\\"a\\\":1}\""
(should (string-match-p "a" result)))
;; Cleanup
(ob-elixir-cleanup-deps-projects)
(when (file-directory-p ob-elixir-deps-cache-dir)
(delete-directory ob-elixir-deps-cache-dir t)))))
(ert-deftest ob-elixir-test-deps-caching ()
"Test that deps projects are cached and reused."
:tags '(:integration :network)
(skip-unless (executable-find "mix"))
(let ((ob-elixir-deps-cache-dir (make-temp-file "ob-elixir-test-" t))
(deps "[{:jason, \"~> 1.4\"}]"))
(unwind-protect
(let ((project1 (ob-elixir--get-deps-project deps))
(project2 (ob-elixir--get-deps-project deps)))
;; Should return same directory
(should (string= project1 project2))
;; Should only have one entry in hash table
(should (= 1 (hash-table-count ob-elixir--deps-projects))))
;; Cleanup
(ob-elixir-cleanup-deps-projects)
(when (file-directory-p ob-elixir-deps-cache-dir)
(delete-directory ob-elixir-deps-cache-dir t)))))
(ert-deftest ob-elixir-test-different-deps-different-projects ()
"Test that different deps create different projects."
:tags '(:integration :network)
(skip-unless (executable-find "mix"))
(let ((ob-elixir-deps-cache-dir (make-temp-file "ob-elixir-test-" t))
(deps1 "[{:jason, \"~> 1.4\"}]")
(deps2 "[{:decimal, \"~> 2.0\"}]"))
(unwind-protect
(let ((project1 (ob-elixir--get-deps-project deps1))
(project2 (ob-elixir--get-deps-project deps2)))
;; Should be different directories
(should-not (string= project1 project2))
;; Should have two entries in hash table
(should (= 2 (hash-table-count ob-elixir--deps-projects))))
;; Cleanup
(ob-elixir-cleanup-deps-projects)
(when (file-directory-p ob-elixir-deps-cache-dir)
(delete-directory ob-elixir-deps-cache-dir t)))))
;;; Cleanup Tests
(ert-deftest ob-elixir-test-cleanup-clears-hash-table ()
"Test that cleanup clears the hash table."
(let ((ob-elixir--deps-projects (make-hash-table :test 'equal)))
(puthash "test-hash" "/tmp/test-dir" ob-elixir--deps-projects)
(should (= 1 (hash-table-count ob-elixir--deps-projects)))
;; Cleanup should clear the table (dir doesn't exist, so no error)
(ob-elixir-cleanup-deps-projects)
(should (= 0 (hash-table-count ob-elixir--deps-projects)))))
(provide 'test-ob-elixir-deps)
;;; test-ob-elixir-deps.el ends here