;;; test-ob-elixir-deps.el --- Deps block tests -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Your Name ;; Author: Your Name ;; 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