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 @compile {:no_warn_undefined, GitRepo.Archive} 139 test "archive.install from Git" do 140 in_fixture("git_repo", fn -> 141 File.mkdir_p!("config") 142 143 File.write!("config/config.exs", """ 144 import Config 145 config :git_repo, :archive_config, true 146 """) 147 148 File.write!("lib/git_repo.ex", """ 149 require Application 150 true = Application.compile_env!(:git_repo, :archive_config) 151 152 defmodule GitRepo.Archive do 153 def hello do 154 "World" 155 end 156 end 157 """) 158 159 System.cmd("git", ~w[init]) 160 System.cmd("git", ~w[add .]) 161 System.cmd("git", ~w[commit -m first-commit]) 162 163 send(self(), {:mix_shell_input, :yes?, true}) 164 Mix.Tasks.Archive.Install.run(["git", File.cwd!()]) 165 assert GitRepo.Archive.hello() == "World" 166 167 message = "Generated archive \"git_repo-0.1.0.ez\" with MIX_ENV=prod" 168 assert_received {:mix_shell, :info, [^message]} 169 170 refute File.regular?(tmp_path("userhome/.mix/archives/git_repo-0.1.0.ez")) 171 assert File.dir?(tmp_path("userhome/.mix/archives/git_repo-0.1.0/git_repo-0.1.0/ebin")) 172 end) 173 after 174 purge([GitRepo.Archive, GitRepo.MixProject]) 175 end 176 177 test "archive install, update, and uninstall life-cycle" do 178 in_fixture("archive", fn -> 179 # Install previous version 180 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 181 assert File.regular?("archive-0.1.0.ez") 182 send(self(), {:mix_shell_input, :yes?, true}) 183 Mix.Tasks.Archive.Install.run([]) 184 assert_received {:mix_shell, :error, [_]} 185 186 # Build new version 187 Mix.Project.push(ArchiveProject2) 188 Mix.Tasks.Archive.Build.run(["--no-compile"]) 189 assert File.regular?("archive-0.2.0.ez") 190 191 message = "Generated archive \"archive-0.2.0.ez\" with MIX_ENV=dev" 192 assert_received {:mix_shell, :info, [^message]} 193 194 # Install new version 195 send(self(), {:mix_shell_input, :yes?, true}) 196 Mix.Tasks.Archive.Install.run([]) 197 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.2.0.ez")) 198 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 199 200 # Re-install current version should not change system 201 send(self(), {:mix_shell_input, :yes?, true}) 202 Mix.Tasks.Archive.Install.run([]) 203 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.2.0.ez")) 204 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 205 206 # Try to install a missing version does not remove archive 207 assert_raise Mix.Error, fn -> 208 Mix.Tasks.Archive.Install.run(["./archive-0.0.0.ez"]) 209 end 210 211 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 212 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.1.0.ez")) 213 214 # Load archive without warnings because there is no :elixir requirement in mix.exs 215 Mix.Local.append_archives() 216 refute_received {:mix_shell, :error, [_]} 217 218 # Check uninstall confirmation 219 send(self(), {:mix_shell_input, :yes?, false}) 220 Mix.Tasks.Archive.Uninstall.run(["archive-0.2.0"]) 221 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 222 223 # Remove it! 224 send(self(), {:mix_shell_input, :yes?, true}) 225 Mix.Tasks.Archive.Uninstall.run(["archive-0.2.0"]) 226 refute File.dir?(tmp_path("userhome/.mix/archives/archive-0.2.0/archive-0.2.0/ebin")) 227 228 # Check old paths are unloaded 229 paths = Enum.map(:code.get_path(), &List.to_string/1) 230 refute tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin") in paths 231 end) 232 end 233 234 test "archive uninstall without version" do 235 in_fixture("archive", fn -> 236 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 237 send(self(), {:mix_shell_input, :yes?, true}) 238 Mix.Tasks.Archive.Install.run([]) 239 240 send(self(), {:mix_shell_input, :yes?, false}) 241 Mix.Tasks.Archive.Uninstall.run(["archive"]) 242 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 243 244 send(self(), {:mix_shell_input, :yes?, true}) 245 Mix.Tasks.Archive.Uninstall.run(["archive"]) 246 refute File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 247 end) 248 end 249 250 test "archive checksum" do 251 in_fixture("archive", fn -> 252 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 253 assert File.regular?("archive-0.1.0.ez") 254 send(self(), {:mix_shell_input, :yes?, true}) 255 256 # Install with wrong checksum 257 assert_raise Mix.Error, ~r"Data does not match the given SHA-512 checksum", fn -> 258 send(self(), {:mix_shell_input, :yes?, true}) 259 Mix.Tasks.Archive.Install.run(["--sha512", "wrong"]) 260 end 261 262 # Install with correct checksum 263 send(self(), {:mix_shell_input, :yes?, true}) 264 Mix.Tasks.Archive.Install.run(["--sha512", sha512("archive-0.1.0.ez")]) 265 refute File.regular?(tmp_path("userhome/.mix/archives/archive-0.1.0.ez")) 266 assert File.dir?(tmp_path("userhome/.mix/archives/archive-0.1.0/archive-0.1.0/ebin")) 267 end) 268 end 269 270 test "archive check" do 271 # Install the archive 272 in_fixture("archive", fn -> 273 Mix.Tasks.Archive.Build.run(["--no-elixir-version-check"]) 274 send(self(), {:mix_shell_input, :yes?, true}) 275 Mix.Tasks.Archive.Install.run([]) 276 Application.unload(:archive) 277 end) 278 279 assert_raise Mix.Error, ~r/Expected archive to be in the format/, fn -> 280 archive_check([:archive]) 281 end 282 283 assert_raise Mix.Error, ~r/Archive "archive" could not be found/, fn -> 284 archive_check([{:archive, ">= 1.0.0"}]) 285 end 286 287 # Load the archive 288 Mix.Local.append_archives() 289 290 message = ~r/Archive \"archive-0.1.0\" does not match requirement >= 1.0.0/ 291 292 assert_raise Mix.Error, message, fn -> 293 archive_check([{:archive, ">= 1.0.0"}]) 294 end 295 296 archive_check([{:archive, ">= 0.0.0"}]) 297 end 298 299 defp archive_check(archives) do 300 Mix.Project.pop() 301 Mix.ProjectStack.post_config(archives: archives) 302 Mix.Project.push(MixTest.Case.Sample) 303 Mix.Tasks.Archive.Check.run([]) 304 end 305 306 defp sha512(file) do 307 Base.encode16(:crypto.hash(:sha512, File.read!(file)), case: :lower) 308 end 309 310 defp has_in_zip_file?(archive, name) do 311 {:ok, files} = :zip.list_dir(archive) 312 Enum.find(files, &match?({:zip_file, ^name, _, _, _, _}, &1)) 313 end 314end 315