dply 0.2.19 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +4 -0
  3. data/Rakefile +0 -14
  4. data/TODO +0 -1
  5. data/code_dump/old_remote_task.rb +2 -0
  6. data/{dev_bin → dev_exe}/dplyr +0 -0
  7. data/{dev_bin → dev_exe}/drake +1 -1
  8. data/dply.gemspec +2 -2
  9. data/{bin → exe}/dplyr +0 -0
  10. data/{bin → exe}/drake +12 -14
  11. data/lib/dply/TEST_TODO +50 -0
  12. data/lib/dply/app_config.rb +108 -0
  13. data/lib/dply/base_config.rb +110 -0
  14. data/lib/dply/build.rb +17 -11
  15. data/lib/dply/build_config.rb +28 -96
  16. data/lib/dply/bundle.rb +7 -30
  17. data/lib/dply/cli/build.rb +5 -12
  18. data/lib/dply/cli/ctl.rb +7 -8
  19. data/lib/dply/cli/deploy.rb +6 -10
  20. data/lib/dply/cli/devbuild.rb +6 -10
  21. data/lib/dply/cli/install_pkgs.rb +2 -3
  22. data/lib/dply/cli/run.rb +27 -0
  23. data/lib/dply/cli/status.rb +1 -2
  24. data/lib/dply/cli/task.rb +6 -12
  25. data/lib/dply/code_archive.rb +123 -0
  26. data/lib/dply/command.rb +57 -0
  27. data/lib/dply/config_downloader.rb +3 -2
  28. data/lib/dply/curl.rb +1 -5
  29. data/lib/dply/custom_logger.rb +18 -1
  30. data/lib/dply/deplist.rb +16 -48
  31. data/lib/dply/deploy_config.rb +34 -0
  32. data/lib/dply/elf.rb +60 -0
  33. data/lib/dply/env.rb +9 -0
  34. data/lib/dply/git.rb +15 -8
  35. data/lib/dply/helper.rb +21 -33
  36. data/lib/dply/linker.rb +27 -27
  37. data/lib/dply/lock.rb +2 -9
  38. data/lib/dply/logger.rb +1 -1
  39. data/lib/dply/pkgs.rb +9 -11
  40. data/lib/dply/release.rb +2 -2
  41. data/lib/dply/{archive.rb → remote_archive.rb} +1 -1
  42. data/lib/dply/repo.rb +3 -3
  43. data/lib/dply/rpm.rb +12 -20
  44. data/lib/dply/scripts/depcheck.rb +4 -0
  45. data/lib/dply/shared_dirs.rb +1 -1
  46. data/lib/dply/strategy/archive.rb +15 -22
  47. data/lib/dply/strategy/base.rb +82 -0
  48. data/lib/dply/strategy/git.rb +18 -19
  49. data/lib/dply/task_dsl.rb +101 -0
  50. data/lib/dply/util.rb +75 -0
  51. data/lib/dply/venv.rb +53 -0
  52. data/lib/dply/version.rb +1 -1
  53. data/lib/dply/yum.rb +21 -31
  54. data/lib/dplyr/consul.rb +1 -1
  55. data/spec/dply/base_config_spec.rb +178 -0
  56. data/spec/dply/bundle_spec.rb +100 -0
  57. data/spec/dply/command_spec.rb +190 -0
  58. data/spec/dply/curl_spec.rb +41 -0
  59. data/spec/dply/deplist_spec.rb +48 -0
  60. data/spec/dply/elf_spec.rb +64 -0
  61. data/spec/dply/env_spec.rb +57 -0
  62. data/spec/dply/git_spec.rb +136 -0
  63. data/spec/dply/helper_spec.rb +168 -0
  64. data/spec/dply/linker_spec.rb +81 -0
  65. data/spec/dply/lock_spec.rb +24 -0
  66. data/spec/dply/pkgs_spec.rb +105 -0
  67. data/spec/dply/repo_spec.rb +58 -0
  68. data/spec/dply/rpm_spec.rb +32 -0
  69. data/spec/dply/yum_spec.rb +29 -0
  70. data/spec/integration/archive_flow_spec.rb +87 -0
  71. data/spec/integration/git_flow_spec.rb +63 -0
  72. data/spec/repo.rb +27 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/test_data/build.tar.gz +0 -0
  75. data/spec/test_data/build.tar.gz.md5 +1 -0
  76. data/spec/test_data/bundle/gems_installed/Gemfile +1 -0
  77. data/spec/test_data/bundle/gems_not_installed/Gemfile +2 -0
  78. data/spec/test_data/bundle/no_gemfile/.gitkeep +0 -0
  79. data/spec/test_data/command/test.rb +7 -0
  80. data/spec/test_data/elf/elf +0 -0
  81. data/spec/test_data/elf/libpgtypes.so.3 +0 -0
  82. data/spec/test_data/elf/not_elf +1 -0
  83. data/spec/test_data/sample_repo/.dply.lock +0 -0
  84. data/spec/test_data/sample_repo/Gemfile +2 -0
  85. data/spec/test_data/sample_repo/Rakefile +3 -0
  86. data/spec/test_data/sample_repo/app.rb +1 -0
  87. data/spec/test_data/sample_repo/dply/app.rb +33 -0
  88. data/spec/test_data/sample_repo/lib/libacl.so.1 +0 -0
  89. data/spec/test_data/sample_repo/pkgs.yml +2 -0
  90. data/spec/webserver.rb +21 -0
  91. metadata +96 -28
  92. data/lib/dply/cli/app_task.rb +0 -38
  93. data/lib/dply/config.rb +0 -120
  94. data/lib/dply/config_struct.rb +0 -52
  95. data/lib/dply/rakelib/drake.rake +0 -33
  96. data/lib/dply/tasks.rb +0 -136
@@ -0,0 +1,41 @@
1
+ require 'dply/curl'
2
+
3
+ module Dply
4
+ describe Curl do
5
+
6
+ let(:url) { "http://127.0.0.1:8000/build.tar.gz" }
7
+
8
+ describe "#download(url, file)" do
9
+ it "downloads the url to file" do
10
+ Dir.mktmpdir do |dir|
11
+ f = "#{dir}/f"
12
+ curl = Curl.new
13
+ expect(Logger.logger).to receive(:bullet).with("downloading #{url}")
14
+ curl.download(url, f)
15
+ expect(File).to exist(f)
16
+ end
17
+ end
18
+
19
+ it "raises error when url doesn't exist" do
20
+ url = "http://127.0.0.1:8000/build"
21
+ Dir.mktmpdir do |dir|
22
+ f = "#{dir}/f"
23
+ curl = Curl.new
24
+ expect(Logger.logger).to receive(:bullet).with("downloading #{url}")
25
+ expect { curl.download(url, f) }.to raise_error(Error)
26
+ end
27
+ end
28
+
29
+ it "logs message to debug when quiet: true" do
30
+ Dir.mktmpdir do |dir|
31
+ f = "#{dir}/f"
32
+ curl = Curl.new(quiet: true)
33
+ expect(Logger.logger).to receive(:debug).with("downloading #{url}")
34
+ curl.download(url, f)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,48 @@
1
+ silence_warnings do
2
+ require 'dply/deplist'
3
+ end
4
+
5
+ module Dply
6
+ describe Deplist do
7
+
8
+ before :all do
9
+ @work_dir = "tmp/deplist"
10
+ FileUtils.rm_rf @work_dir
11
+ FileUtils.mkdir_p @work_dir
12
+ end
13
+
14
+ def tmp_dir(&block)
15
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
16
+ FileUtils.cp "spec/test_data/sample_repo/lib/libacl.so.1", "#{d}/libacl.so.1"
17
+ Dir.chdir(d) { yield d }
18
+ end
19
+
20
+ describe "#verify!" do
21
+
22
+ it "doesn't throw an error when pkgs.yml has the required pkgs" do
23
+ pkgs_data = { "pkgs" => ["libattr"], "build_pkgs" => [] }
24
+ tmp_dir do
25
+ File.write "pkgs.yml", YAML.dump(pkgs_data)
26
+ system! "tar czf test.tar.gz pkgs.yml libacl.so.1"
27
+ deplist = Deplist.new("test.tar.gz")
28
+ silence_warnings do
29
+ expect { deplist.verify! }.to output(/all dependencies satisfied/).to_stdout_from_any_process
30
+ end
31
+ end
32
+ end
33
+
34
+ it "throws an error when pkgs.yml doesn't have the required pkgs" do
35
+ pkgs_data = { "pkgs" => [], "build_pkgs" => [] }
36
+ tmp_dir do
37
+ File.write "pkgs.yml", YAML.dump(pkgs_data)
38
+ system! "tar czf test.tar.gz pkgs.yml libacl.so.1"
39
+ deplist = Deplist.new("test.tar.gz")
40
+ silence_warnings do
41
+ expect { deplist.verify! }.to raise_error(Error).and output(/any of \["libattr"\]/).to_stdout_from_any_process
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,64 @@
1
+ silence_warnings do
2
+ require 'elf'
3
+ end
4
+ require 'dply/elf'
5
+ require 'pathname'
6
+
7
+ module Dply
8
+ describe Elf do
9
+
10
+ before :all do
11
+ @verbose = $VERBOSE
12
+ $VERBOSE = false
13
+ end
14
+
15
+ let(:work_dir) { Pathname.new("#{__dir__}/../test_data/elf").realpath }
16
+
17
+ def map_64bit(list)
18
+ list.map { |i| "#{i}()(64bit)" }
19
+ end
20
+
21
+ describe ".elf?" do
22
+ it "returns true if file is an ELF" do
23
+ expect(Elf.elf? "#{work_dir}/elf").to eq(true)
24
+ end
25
+
26
+ it "returns false if file is not an ELF" do
27
+ expect(Elf.elf? "#{work_dir}/not_elf").to eq(false)
28
+ end
29
+
30
+ it "returns false if file doesn't exist" do
31
+ expect(Elf.elf? "#{work_dir}/nofile").to eq(false)
32
+ end
33
+ end
34
+
35
+ describe "#external_libs" do
36
+ it "returns a list of external libs when elf has no rpath section" do
37
+ libs = map_64bit ["libattr.so.1", "libc.so.6"]
38
+ elf = Elf.new("#{work_dir}/elf")
39
+ expect(elf.external_libs).to match_array(libs)
40
+ end
41
+
42
+ context "when elf has absolute rpath or runpath" do
43
+ it "returns all external libs ignoring rpath" do
44
+ libs = map_64bit ["libpq.so.5", "libc.so.6", "libpgtypes.so.3", "libpthread.so.0"]
45
+ elf = Elf.new("#{work_dir}/libecpg.so")
46
+ expect(elf.external_libs).to match_array(libs)
47
+ end
48
+ end
49
+
50
+ context "when elf contains a relative rpath or runpath" do
51
+ it "returns all external libs excluding the ones found in relative rpath" do
52
+ libs = map_64bit ["libpq.so.5", "libc.so.6", "libpthread.so.0"]
53
+ elf = Elf.new("#{work_dir}/rpath_libecpg.so")
54
+ expect(elf.external_libs).to match_array(libs)
55
+ end
56
+ end
57
+ end
58
+
59
+ after :all do
60
+ $VERBOSE = @verbose
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ require 'dply/env'
2
+
3
+ module Dply
4
+ describe Env do
5
+
6
+ ORIGINAL_ENV = { "O1" => "1", "O2" => "2" }
7
+ CURRENT_ENV = { "C1" => "1", "C2" => "2" }
8
+ EXTRA_ENV = { "O1" => "E", "C1" => "E" }
9
+
10
+ def stub_env(with_bundler:)
11
+ allow(ENV).to receive(:to_h).and_return(CURRENT_ENV)
12
+ if with_bundler
13
+ stub_const("Bundler", "bundler")
14
+ allow(Bundler).to receive(:original_env).and_return(ORIGINAL_ENV)
15
+ else
16
+ hide_const("Bundler")
17
+ end
18
+ end
19
+
20
+ describe '.build_env (extra_env, bundled_env:)' do
21
+ context "when bundler is defined and bundled_env: true" do
22
+ it "merges current_env with extra_env" do
23
+ stub_env with_bundler: true
24
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
25
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
26
+ end
27
+ end
28
+
29
+ context "when bundler is defined and bundled_env: false" do
30
+ it "merges original_env with extra_env" do
31
+ stub_env with_bundler: true
32
+ env = Env.build_env(EXTRA_ENV, bundled_env: false)
33
+ expect(env).to eq(ORIGINAL_ENV.merge(EXTRA_ENV))
34
+ end
35
+ end
36
+
37
+ context "when bundler is not defined and bundled_env: true" do
38
+ it "merges current_env with extra_env" do
39
+ stub_env with_bundler: false
40
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
41
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
42
+ end
43
+ end
44
+
45
+ context "when bundler is defined and bundled_env: false" do
46
+ it "merges current_env with extra_env" do
47
+ stub_env with_bundler: false
48
+ env = Env.build_env(EXTRA_ENV, bundled_env: true)
49
+ expect(env).to eq(CURRENT_ENV.merge(EXTRA_ENV))
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+
@@ -0,0 +1,136 @@
1
+ require 'dply/git'
2
+ require 'repo'
3
+
4
+ module Dply
5
+ describe Git do
6
+
7
+ before :all do
8
+ @work_dir = "tmp/git"
9
+ @source_repo = "#{Dir.pwd}/tmp/repo.git"
10
+ FileUtils.rm_rf @work_dir
11
+ FileUtils.mkdir_p @work_dir
12
+ end
13
+
14
+ def source_repo
15
+ @source_repo
16
+ end
17
+
18
+ def tmp_dir(&block)
19
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
20
+ Dir.chdir(d) { yield d }
21
+ end
22
+
23
+ describe ".pull" do
24
+ it "checks out the given branch and pulls any changes to it from upstream" do
25
+ tmp_dir do |d|
26
+ upstream = "#{d}/upstream"
27
+ system! "git clone #{source_repo} upstream"
28
+ system! "git clone #{upstream} repo"
29
+ Dir.chdir "upstream" do
30
+ system! "git checkout -b new_data"
31
+ system! "echo 'new_data' > new_data"
32
+ system! "git add new_data; git commit -m 'commit'"
33
+ end
34
+
35
+ Dir.chdir "repo" do
36
+ suppress_output { Git.pull "new_data" }
37
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq('new_data')
38
+ expect(File).to exist('new_data')
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".checkout" do
45
+ it "checks out the given branch" do
46
+ upstream = source_repo
47
+ tmp_dir do
48
+ system! "git clone #{upstream} repo", quiet: true
49
+ Dir.chdir "repo" do
50
+ # local tracking branch doesn't exist
51
+ suppress_output { Git.checkout "some_branch" }
52
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq("some_branch")
53
+
54
+ #local tracking branch exists
55
+ suppress_output { Git.checkout "master" }
56
+ expect(`git rev-parse --abbrev-ref HEAD`.chomp).to eq("master")
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ describe ".clone" do
63
+ it "clones the repo from mirror and sets the upstream to original" do
64
+ mirror = source_repo
65
+ original = "/some/original/repo.git"
66
+ tmp_dir do
67
+ suppress_output { Git.clone original, "repo", mirror: mirror }
68
+ Dir.chdir "repo" do
69
+ expect(Git.remote_url).to eq(original)
70
+ end
71
+ end
72
+ end
73
+
74
+ it "clones the repo from original source when mirror is not specified" do
75
+ original = source_repo
76
+ tmp_dir do
77
+ suppress_output { Git.clone original, "repo", mirror: nil }
78
+ Dir.chdir "repo" do
79
+ expect(Git.remote_url).to eq(original)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ describe ".clean" do
86
+ it "cleans any changes made to the working tree (resets HEAD state and removes untracked files)" do
87
+ upstream = source_repo
88
+ tmp_dir do
89
+ system! "git clone #{upstream} repo", quiet: true
90
+ Dir.chdir "repo" do
91
+ system! "echo asda > my_new_file" #new
92
+ system! "echo existing > Gemfile" #existing
93
+ suppress_output { Git.clean }
94
+ lines = IO.popen(%w(git status --porcelain)) { |f| f.readlines }
95
+ expect(lines.size).to eq(0)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe ".tracking_branch" do
102
+ it "returns the tracking branch for given branch" do
103
+ upstream = source_repo
104
+ branch = "some_branch"
105
+ tmp_dir do
106
+ system! "git clone #{upstream} repo", quiet: true
107
+ Dir.chdir "repo" do
108
+ system! "git checkout -b #{branch} -t origin/#{branch}", quiet: true
109
+ expect(Git.tracking_branch(branch)).to eq("origin/#{branch}")
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe ".remote_url" do
116
+ it "returns the url for origin upstream" do
117
+ upstream = source_repo
118
+ tmp_dir do |d|
119
+ system! "git clone #{upstream} repo", quiet: true
120
+ Dir.chdir("repo") do
121
+ expect(Git.remote_url).to eq(upstream)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ describe ".commit_id" do
128
+ it "returns the commit id" do
129
+ Dir.chdir source_repo do
130
+ commit_id = `git rev-parse HEAD`.chomp
131
+ expect(Git.commit_id).to eq(commit_id)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,168 @@
1
+ require 'dply/helper'
2
+ require 'pathname'
3
+
4
+ module Dply
5
+ describe Helper do
6
+
7
+ before :all do
8
+ @work_dir = "tmp/helper"
9
+ FileUtils.rm_rf @work_dir
10
+ FileUtils.mkdir_p @work_dir
11
+ end
12
+
13
+ klass = Class.new { include Helper }
14
+ subject { klass.new }
15
+
16
+ def tmp_dir(&block)
17
+ d = Dir.mktmpdir nil, "#{Dir.pwd}/#{@work_dir}"
18
+ Dir.chdir(d) { yield d }
19
+ end
20
+
21
+ describe "#cmd" do
22
+ command = "touch a"
23
+ display = :a
24
+ env = {"a" => "1" }
25
+ bundled_env = :benv
26
+ new_env = {"e" => "1"}
27
+
28
+ let(:cmd_proc) do
29
+ Proc.new do |return_output|
30
+ subject.cmd command, env: env, bundled_env: bundled_env,
31
+ return_output: return_output, display: display
32
+ end
33
+ end
34
+
35
+ it "runs the command as expected when return_output: true" do
36
+ return_output = true
37
+ cmd_double = double
38
+ output = "output"
39
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
40
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
41
+ expect(Command).to receive(:new).with(command, env: new_env, shell: false).and_return(cmd_double)
42
+ expect(cmd_double).to receive(:capture).and_return(output)
43
+ out = cmd_proc.call(return_output)
44
+ expect(out).to eq(output)
45
+ end
46
+
47
+ it "runs the command as expected when return_output: false" do
48
+ return_output = false
49
+ cmd_double = double
50
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
51
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
52
+ expect(Command).to receive(:new).with(command, env: new_env, shell: false).and_return(cmd_double)
53
+ expect(cmd_double).to receive(:run)
54
+ cmd_proc.call(return_output)
55
+ end
56
+ end
57
+
58
+ describe "#sh" do
59
+ command = "touch a"
60
+ display = :a
61
+ env = {"a" => "1" }
62
+ bundled_env = :benv
63
+ new_env = {"e" => "1"}
64
+
65
+ let(:sh_proc) do
66
+ Proc.new do |return_output|
67
+ subject.sh command, env: env, bundled_env: bundled_env,
68
+ return_output: return_output, display: display
69
+ end
70
+ end
71
+
72
+ it "runs the command as expected when return_output: true" do
73
+ return_output = true
74
+ sh_double = double
75
+ output = "output"
76
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
77
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
78
+ expect(Command).to receive(:new).with(command, env: new_env, shell: true).and_return(sh_double)
79
+ expect(sh_double).to receive(:capture).and_return(output)
80
+ out = sh_proc.call(return_output)
81
+ expect(out).to eq(output)
82
+ end
83
+
84
+ it "runs the command as expected when return_output: false" do
85
+ return_output = false
86
+ sh_double = double
87
+ expect(Logger.logger).to receive(:command).with(command, mode: display)
88
+ expect(Env).to receive(:build_env).with(env, bundled_env: bundled_env).and_return(new_env)
89
+ expect(Command).to receive(:new).with(command, env: new_env, shell: true).and_return(sh_double)
90
+ expect(sh_double).to receive(:run)
91
+ sh_proc.call(return_output)
92
+ end
93
+ end
94
+
95
+ describe "#symlink" do
96
+ it "creates symlink dst -> src" do
97
+ tmp_dir do
98
+ FileUtils.touch "src"
99
+ subject.symlink "src", "dst"
100
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
101
+ end
102
+ end
103
+
104
+ it "does nothing if the link exists" do
105
+ tmp_dir do
106
+ FileUtils.touch "src"
107
+ FileUtils.ln_s "src", "dst"
108
+ expect(FileUtils).not_to receive(:ln_s)
109
+ subject.symlink "src", "dst"
110
+ end
111
+ end
112
+
113
+ it "does not call ln_s when link exists and src/dest are Pathnames" do
114
+ tmp_dir do
115
+ FileUtils.touch "src"
116
+ FileUtils.ln_s "src", "dst"
117
+ expect(FileUtils).not_to receive(:ln_s)
118
+ subject.symlink Pathname.new("src"), Pathname.new("dst")
119
+ end
120
+ end
121
+
122
+ it "replaces existing dst -> src(file) link atomically (using move op)" do
123
+ tmp_dir do
124
+ FileUtils.touch "src"
125
+ FileUtils.touch "src1"
126
+ FileUtils.ln_s "src1", "dst"
127
+ prev_inode = File.stat("dst").ino
128
+
129
+ #atomic rename
130
+ dir = File.dirname("dst")
131
+ expect(subject).to receive(:cmd).with(array_including("mv", dir), display: false).and_call_original
132
+
133
+ subject.symlink "src", "dst"
134
+ current_inode = File.stat("dst").ino
135
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
136
+ expect(prev_inode).not_to eq(current_inode)
137
+ end
138
+ end
139
+
140
+ it "replaces existing dst -> src(dir) link atomically" do
141
+ tmp_dir do
142
+ FileUtils.mkdir "src1"
143
+ FileUtils.mkdir "src"
144
+ FileUtils.ln_s "src1", "dst"
145
+ prev_inode = File.stat("dst").ino
146
+
147
+ dir = File.dirname("dst")
148
+
149
+ expect(subject).to receive(:cmd).with(array_including("mv", dir), display: false).and_call_original
150
+
151
+ subject.symlink "src", "dst"
152
+ current_inode = File.stat("dst").ino
153
+ expect(File.realpath("dst")).to eq(File.realpath("src"))
154
+ expect(prev_inode).not_to eq(current_inode)
155
+ end
156
+ end
157
+
158
+ it "raises an error when dst file already exists" do
159
+ tmp_dir do
160
+ FileUtils.touch "src"
161
+ FileUtils.touch "dst"
162
+ expect { subject.symlink "src", "dst" }.to raise_error(Error)
163
+ end
164
+ end
165
+
166
+ end
167
+ end
168
+ end