1Code.require_file("../../test_helper.exs", __DIR__) 2 3defmodule Mix.Tasks.ArchiveTest do 4 use MixTest.Case 5 6 defmodule ArchiveProject do 7 def project do 8 [app: :archive, version: "0.1.0", elixir: "~> 0.1.0"] 9 end 10 end 11 12 defmodule ArchiveProject2 do 13 def project do 14 [app: :archive, version: "0.2.0"] 15 end 16 end 17 18 setup do 19 File.rm_rf!(tmp_path("userhome")) 20 System.put_env("MIX_ARCHIVES", tmp_path("userhome/.mix/archives/")) 21 Mix.Project.push(ArchiveProject) 22 23 on_exit(fn -> 24 Mix.Local.remove_archives() 25 System.delete_env("MIX_ARCHIVES") 26 end) 27 end 28 29 test "archive build" do 30 in_fixture("archive", fn -> 31 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 32 message = "Generated archive \"archive-0.1.0.ez\" with MIX_ENV=dev" 33 assert_received {:mix_shell, :info, [^message]} 34 assert File.regular?('archive-0.1.0.ez') 35 36 assert_archive_content_default() 37 refute has_in_zip_file?('archive-0.1.0.ez', 'archive-0.1.0/priv/.dot_file') 38 end) 39 end 40 41 test "archive build with include-dot-files" do 42 in_fixture("archive", fn -> 43 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check", "--include-dot-files"]) 44 message = "Generated archive \"archive-0.1.0.ez\" with MIX_ENV=dev" 45 assert_received {:mix_shell, :info, [^message]} 46 assert File.regular?('archive-0.1.0.ez') 47 48 assert_archive_content_default() 49 assert has_in_zip_file?('archive-0.1.0.ez', 'archive-0.1.0/priv/.dot_file') 50 end) 51 end 52 53 def assert_archive_content_default() do 54 assert File.regular?('archive-0.1.0.ez') 55 assert has_in_zip_file?('archive-0.1.0.ez', 'archive-0.1.0/.elixir') 56 assert has_in_zip_file?('archive-0.1.0.ez', 'archive-0.1.0/priv/not_really_an.so') 57 58 assert has_in_zip_file?( 59 'archive-0.1.0.ez', 60 'archive-0.1.0/ebin/Elixir.Mix.Tasks.Local.Sample.beam' 61 ) 62 63 assert has_in_zip_file?('archive-0.1.0.ez', 'archive-0.1.0/ebin/archive.app') 64 end 65 66 test "archive install" do 67 in_fixture("archive", fn -> 68 # Build and install archive 69 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 70 71 send(self(), {:mix_shell_input, :yes?, true}) 72 Mix.Tasks.Archive.Install.run([]) 73 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.1.0.ez")) 74 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 75 76 # Check that the version warning is printed after installation 77 version_error = 78 "warning: the archive archive-0.1.0 requires Elixir \"~> 0.1.0\" " <> 79 "but you are running on v#{System.version()}" 80 81 assert_received {:mix_shell, :error, [^version_error]} 82 83 archive = tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin") 84 assert to_charlist(archive) in :code.get_path() 85 86 # Loading the archive should emit warning again 87 Mix.Local.append_archives() 88 assert_received {:mix_shell, :error, [^version_error]} 89 90 # List archive 91 Mix.Tasks.Local.run([]) 92 info = "mix local.sample # A local install sample" 93 assert_received {:mix_shell, :info, [^info]} 94 95 Mix.Tasks.Archive.run([]) 96 assert_received {:mix_shell, :info, ["* archive-0.1.0"]} 97 98 # Run archived task 99 Mix.Task.run("local.sample") 100 assert_received {:mix_shell, :info, ["sample"]} 101 end) 102 end 103 104 test "archive install invalid file" do 105 in_fixture("archive", fn -> 106 file_name = "invalid-archive-0.1.0.ez" 107 assert File.regular?(file_name) 108 109 send(self(), {:mix_shell_input, :yes?, true}) 110 111 assert_raise Mix.Error, ~r/invalid archive file/, fn -> 112 Mix.Tasks.Archive.Install.run([file_name]) 113 end 114 end) 115 end 116 117 test "archive install missing file" do 118 message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a local file path] 119 120 assert_raise Mix.Error, message, fn -> 121 Mix.Tasks.Archive.Install.run(["./unlikely-to-exist-0.1.0.ez"]) 122 end 123 end 124 125 test "archive install --force" do 126 in_fixture("archive", fn -> 127 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 128 Mix.Tasks.Archive.Install.run(["--force"]) 129 130 message = "Generated archive \"archive-0.1.0.ez\" with MIX_ENV=dev" 131 assert_received {:mix_shell, :info, [^message]} 132 133 Mix.Tasks.Archive.Uninstall.run(["archive-0.1.0", "--force"]) 134 refute File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 135 end) 136 end 137 138 test "archive.install from Git" do 139 in_fixture("git_repo", fn -> 140 send(self(), {:mix_shell_input, :yes?, true}) 141 Mix.Tasks.Archive.Install.run(["git", File.cwd!()]) 142 143 message = "Generated archive \"git_repo-0.1.0.ez\" with MIX_ENV=prod" 144 assert_received {:mix_shell, :info, [^message]} 145 146 refute File.regular?(tmp_path("userhome/.mix/archives/git_repo-0.1.0.ez")) 147 assert File.dir?(tmp_path("userhome/.mix/archives/git_repo-0.1.0/git_repo-0.1.0/ebin")) 148 end) 149 after 150 purge([GitRepo, GitRepo.MixProject]) 151 end 152 153 test "archive install, update, and uninstall life-cycle" do 154 in_fixture("archive", fn -> 155 # Install previous version 156 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 157 assert File.regular?("archive-0.1.0.ez") 158 send(self(), {:mix_shell_input, :yes?, true}) 159 Mix.Tasks.Archive.Install.run([]) 160 assert_received {:mix_shell, :error, [_]} 161 162 # Build new version 163 Mix.Project.push(ArchiveProject2) 164 Mix.Tasks.Archive.Build.run(["--no-compile"]) 165 assert File.regular?("archive-0.2.0.ez") 166 167 message = "Generated archive \"archive-0.2.0.ez\" with MIX_ENV=dev" 168 assert_received {:mix_shell, :info, [^message]} 169 170 # Install new version 171 send(self(), {:mix_shell_input, :yes?, true}) 172 Mix.Tasks.Archive.Install.run([]) 173 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.2.0.ez")) 174 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 175 176 # Re-install current version should not change system 177 send(self(), {:mix_shell_input, :yes?, true}) 178 Mix.Tasks.Archive.Install.run([]) 179 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.2.0.ez")) 180 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 181 182 # Try to install a missing version does not remove archive 183 assert_raise Mix.Error, fn -> 184 Mix.Tasks.Archive.Install.run(["./archive-0.0.0.ez"]) 185 end 186 187 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 188 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.1.0.ez")) 189 190 # Load archive without warnings because there is no :elixir requirement in mix.exs 191 Mix.Local.append_archives() 192 refute_received {:mix_shell, :error, [_]} 193 194 # Check uninstall confirmation 195 send(self(), {:mix_shell_input, :yes?, false}) 196 Mix.Tasks.Archive.Uninstall.run(["archive-0.2.0"]) 197 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 198 199 # Remove it! 200 send(self(), {:mix_shell_input, :yes?, true}) 201 Mix.Tasks.Archive.Uninstall.run(["archive-0.2.0"]) 202 refute File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 203 204 # Check old paths are unloaded 205 paths = Enum.map(:code.get_path(), &List.to_string/1) 206 refute tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin") in paths 207 end) 208 end 209 210 test "archive uninstall without version" do 211 in_fixture("archive", fn -> 212 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 213 send(self(), {:mix_shell_input, :yes?, true}) 214 Mix.Tasks.Archive.Install.run([]) 215 216 send(self(), {:mix_shell_input, :yes?, false}) 217 Mix.Tasks.Archive.Uninstall.run(["archive"]) 218 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 219 220 send(self(), {:mix_shell_input, :yes?, true}) 221 Mix.Tasks.Archive.Uninstall.run(["archive"]) 222 refute File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 223 end) 224 end 225 226 test "archive checksum" do 227 in_fixture("archive", fn -> 228 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 229 assert File.regular?("archive-0.1.0.ez") 230 send(self(), {:mix_shell_input, :yes?, true}) 231 232 # Install with wrong checksum 233 assert_raise Mix.Error, ~r"Data does not match the given SHA-512 checksum", fn -> 234 send(self(), {:mix_shell_input, :yes?, true}) 235 Mix.Tasks.Archive.Install.run(["--sha512", "wrong"]) 236 end 237 238 # Install with correct checksum 239 send(self(), {:mix_shell_input, :yes?, true}) 240 Mix.Tasks.Archive.Install.run(["--sha512", sha512("archive-0.1.0.ez")]) 241 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.1.0.ez")) 242 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 243 end) 244 end 245 246 test "archive check" do 247 # Install the archive 248 in_fixture("archive", fn -> 249 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 250 send(self(), {:mix_shell_input, :yes?, true}) 251 Mix.Tasks.Archive.Install.run([]) 252 Application.unload(:archive) 253 end) 254 255 assert_raise Mix.Error, ~r/Expected archive to be in the format/, fn -> 256 archive_check([:archive]) 257 end 258 259 assert_raise Mix.Error, ~r/Archive "archive" could not be found/, fn -> 260 archive_check([{:archive, ">= 1.0.0"}]) 261 end 262 263 # Load the archive 264 Mix.Local.append_archives() 265 266 message = ~r/Archive \"archive-0.1.0\" does not match requirement >= 1.0.0/ 267 268 assert_raise Mix.Error, message, fn -> 269 archive_check([{:archive, ">= 1.0.0"}]) 270 end 271 272 archive_check([{:archive, ">= 0.0.0"}]) 273 end 274 275 defp archive_check(archives) do 276 Mix.Project.pop() 277 Mix.ProjectStack.post_config(archives: archives) 278 Mix.Project.push(MixTest.Case.Sample) 279 Mix.Tasks.Archive.Check.run([]) 280 end 281 282 defp sha512(file) do 283 Base.encode16(:crypto.hash(:sha512, File.read!(file)), case: :lower) 284 end 285 286 defp has_in_zip_file?(archive, name) do 287 {:ok, files} = :zip.list_dir(archive) 288 Enum.find(files, &match?({:zip_file, ^name, _, _, _, _}, &1)) 289 end 290end 291