defmodule CljElixir.Phase4Test do use ExUnit.Case, async: false # Evaluate CljElixir code with PersistentVector enabled defp eval!(source) do case CljElixir.Compiler.eval_string(source) do {:ok, result, _bindings} -> result {:error, errors} -> raise "CljElixir eval error: #{inspect(errors)}" end end # ========================================================================== # tuple function # ========================================================================== describe "tuple function" do test "empty tuple" do result = eval!("(tuple)") assert result == {} end test "single element tuple" do result = eval!("(tuple :ok)") assert result == {:ok} end test "two element tuple" do result = eval!("(tuple :ok \"data\")") assert result == {:ok, "data"} end test "three element tuple" do result = eval!("(tuple 1 2 3)") assert result == {1, 2, 3} end test "tuple with mixed types" do result = eval!("(tuple :error 404 \"not found\")") assert result == {:error, 404, "not found"} end test "tuple with nested tuple" do result = eval!("(tuple :ok (tuple 1 2))") assert result == {:ok, {1, 2}} end test "tuple-size on constructed tuple" do result = eval!("(tuple-size (tuple :a :b :c))") assert result == 3 end test "elem on constructed tuple" do result = eval!("(elem (tuple :a :b :c) 1)") assert result == :b end test "tuple in let binding" do result = eval!(""" (let [t (tuple :ok 42)] (elem t 1)) """) assert result == 42 end test "tuple with expressions as arguments" do result = eval!("(tuple (+ 1 2) (* 3 4))") assert result == {3, 12} end end # ========================================================================== # clojurify # ========================================================================== describe "clojurify" do test "tuple to vector" do result = eval!("(clojurify #el[:ok \"data\"])") assert result.__struct__ == CljElixir.PersistentVector list = CljElixir.PersistentVector.to_list(result) assert list == [:ok, "data"] end test "list to vector" do result = eval!("(clojurify '(1 2 3))") assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [1, 2, 3] end test "nested tuple deep conversion" do result = eval!("(clojurify #el[:ok #el[:nested \"data\"]])") assert result.__struct__ == CljElixir.PersistentVector list = CljElixir.PersistentVector.to_list(result) assert hd(list) == :ok inner = hd(tl(list)) assert inner.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(inner) == [:nested, "data"] end test "map values walked" do result = eval!("(clojurify {:a #el[1 2]})") assert is_map(result) inner = Map.get(result, :a) assert inner.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(inner) == [1, 2] end test "vector idempotent" do result = eval!("(clojurify [1 2 3])") assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [1, 2, 3] end test "scalar passthrough - integer" do assert eval!("(clojurify 42)") == 42 end test "scalar passthrough - string" do assert eval!("(clojurify \"hello\")") == "hello" end test "scalar passthrough - atom" do assert eval!("(clojurify :foo)") == :foo end test "scalar passthrough - nil" do assert eval!("(clojurify nil)") == nil end end # ========================================================================== # elixirify # ========================================================================== describe "elixirify" do test "vector to list" do result = eval!("(elixirify [1 2 3])") assert is_list(result) assert result == [1, 2, 3] end test "nested vector deep conversion" do result = eval!("(elixirify [:ok [:nested \"data\"]])") assert is_list(result) assert result == [:ok, [:nested, "data"]] end test "map values walked" do result = eval!("(elixirify {:a [1 2]})") assert is_map(result) assert Map.get(result, :a) == [1, 2] end test "list idempotent" do result = eval!("(elixirify '(1 2 3))") assert is_list(result) assert result == [1, 2, 3] end test "tuple elements walked" do result = eval!("(elixirify #el[:ok [1 2]])") assert is_tuple(result) assert elem(result, 0) == :ok assert is_list(elem(result, 1)) assert elem(result, 1) == [1, 2] end test "scalar passthrough - integer" do assert eval!("(elixirify 42)") == 42 end test "scalar passthrough - string" do assert eval!("(elixirify \"hello\")") == "hello" end test "scalar passthrough - atom" do assert eval!("(elixirify :foo)") == :foo end end # ========================================================================== # Integration: roundtrips and composition # ========================================================================== describe "roundtrip conversions" do test "clojurify then elixirify roundtrip on tuple" do result = eval!(""" (elixirify (clojurify #el[:ok "data"])) """) assert is_list(result) assert result == [:ok, "data"] end test "elixirify then clojurify roundtrip on vector" do result = eval!(""" (clojurify (elixirify [1 2 3])) """) assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [1, 2, 3] end test "deep nested roundtrip" do result = eval!(""" (elixirify (clojurify #el[:ok #el[1 #el[2 3]]])) """) assert is_list(result) assert result == [:ok, [1, [2, 3]]] end test "map with nested roundtrip" do result = eval!(""" (elixirify (clojurify {:a #el[1 2] :b #el[3 4]})) """) assert is_map(result) assert Map.get(result, :a) == [1, 2] assert Map.get(result, :b) == [3, 4] end end describe "tuple function with clojurify/elixirify" do test "tuple function result can be clojurified" do result = eval!(""" (clojurify (tuple :ok "data")) """) assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [:ok, "data"] end test "elixirify vector matches tuple construction" do # elixirify produces a list, not a tuple (by spec) result = eval!("(elixirify [1 2 3])") assert is_list(result) assert result == [1, 2, 3] end test "tuple-size on tuple function result" do result = eval!("(tuple-size (tuple :a :b :c :d))") assert result == 4 end end describe "composition with core functions" do test "map over list then clojurify" do result = eval!(""" (clojurify (Enum/map '(1 2 3) (fn [x] (* x 2)))) """) assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [2, 4, 6] end test "elixirify vector for Enum interop" do result = eval!(""" (Enum/sum (elixirify [1 2 3 4 5])) """) assert result == 15 end test "clojurify in let binding" do result = eval!(""" (let [v (clojurify #el[:ok 42])] (nth v 1)) """) assert result == 42 end test "elixirify in let binding" do result = eval!(""" (let [lst (elixirify [10 20 30])] (hd lst)) """) assert result == 10 end end # ========================================================================== # SubVector clojurify/elixirify # ========================================================================== describe "SubVector clojurify" do test "clojurify subvec returns vector" do result = eval!(""" (let [v [1 2 3 4 5] sv (subvec v 1 4)] (clojurify sv)) """) assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [2, 3, 4] end end describe "SubVector elixirify" do test "elixirify subvec returns list" do result = eval!(""" (let [v [1 2 3 4 5] sv (subvec v 1 4)] (elixirify sv)) """) assert is_list(result) assert result == [2, 3, 4] end end # ========================================================================== # Protocol extensibility # ========================================================================== describe "protocol extensibility" do test "defrecord can extend IElixirify" do result = eval!(""" (defmodule TestUser (defrecord User [name age] CljElixir.IElixirify (-elixirify [u] {:name (Map/get u :name) :age (Map/get u :age) :type "user"}))) (let [u (TestUser.User/new "Alice" 30)] (elixirify u)) """) assert is_map(result) assert Map.get(result, :name) == "Alice" assert Map.get(result, :age) == 30 assert Map.get(result, :type) == "user" end test "defrecord can extend IClojurify" do result = eval!(""" (defmodule TestPoint (defrecord Point [x y] CljElixir.IClojurify (-clojurify [p] [(Map/get p :x) (Map/get p :y)]))) (let [p (TestPoint.Point/new 10 20)] (clojurify p)) """) assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [10, 20] end end # ========================================================================== # Tuple sequence and collection operations # ========================================================================== describe "tuple sequence operations" do test "seq on tuple" do result = eval!("(seq #el[1 2 3])") assert is_list(result) assert result == [1, 2, 3] end test "seq on empty tuple" do result = eval!("(seq (tuple))") assert result == nil end test "first on tuple" do result = eval!("(first #el[:a :b :c])") assert result == :a end test "rest on tuple" do result = eval!("(rest #el[:a :b :c])") assert is_list(result) assert result == [:b, :c] end test "conj on tuple" do result = eval!("(conj #el[1 2] 3)") assert is_tuple(result) assert result == {1, 2, 3} end test "into empty tuple from vector" do result = eval!("(into (tuple) [1 2 3])") assert is_tuple(result) assert result == {1, 2, 3} end test "into vector from tuple" do result = eval!("(into [] #el[1 2 3])") assert result.__struct__ == CljElixir.PersistentVector assert CljElixir.PersistentVector.to_list(result) == [1, 2, 3] end test "into empty tuple from list" do result = eval!("(into (tuple) '(1 2 3))") assert is_tuple(result) assert result == {1, 2, 3} end test "count on tuple via seq" do result = eval!("(count #el[1 2 3 4])") assert result == 4 end end end