r10k 1.3.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +1 -1
  3. data/CHANGELOG.mkd +210 -0
  4. data/CONTRIBUTING.mkd +105 -0
  5. data/Gemfile +2 -6
  6. data/README.mkd +97 -0
  7. data/doc/common-patterns.mkd +44 -0
  8. data/doc/dynamic-environments.mkd +12 -5
  9. data/doc/dynamic-environments/configuration.mkd +16 -1
  10. data/doc/dynamic-environments/{git-environments.markdown → git-environments.mkd} +13 -9
  11. data/doc/dynamic-environments/introduction.mkd +1 -2
  12. data/doc/dynamic-environments/master-configuration.mkd +70 -0
  13. data/doc/dynamic-environments/quickstart.mkd +241 -0
  14. data/doc/dynamic-environments/svn-environments.mkd +45 -0
  15. data/doc/dynamic-environments/usage.mkd +44 -5
  16. data/doc/dynamic-environments/workflow-guide.mkd +247 -0
  17. data/doc/faq.mkd +52 -0
  18. data/doc/puppetfile.mkd +203 -0
  19. data/lib/r10k/action/cri_runner.rb +75 -0
  20. data/lib/r10k/action/deploy.rb +9 -0
  21. data/lib/r10k/action/deploy/display.rb +104 -0
  22. data/lib/r10k/action/deploy/environment.rb +92 -0
  23. data/lib/r10k/action/deploy/module.rb +70 -0
  24. data/lib/r10k/action/puppetfile.rb +10 -0
  25. data/lib/r10k/action/puppetfile/check.rb +41 -0
  26. data/lib/r10k/action/puppetfile/cri_runner.rb +32 -0
  27. data/lib/r10k/action/puppetfile/install.rb +53 -0
  28. data/lib/r10k/action/puppetfile/purge.rb +37 -0
  29. data/lib/r10k/action/runner.rb +36 -0
  30. data/lib/r10k/action/visitor.rb +31 -0
  31. data/lib/r10k/cli/deploy.rb +14 -45
  32. data/lib/r10k/cli/puppetfile.rb +15 -53
  33. data/lib/r10k/deployment.rb +113 -58
  34. data/lib/r10k/deployment/basedir.rb +3 -38
  35. data/lib/r10k/deployment/config.rb +2 -1
  36. data/lib/r10k/deployment/source.rb +2 -0
  37. data/lib/r10k/environment/base.rb +40 -0
  38. data/lib/r10k/environment/git.rb +14 -17
  39. data/lib/r10k/environment/svn.rb +31 -15
  40. data/lib/r10k/errors.rb +33 -22
  41. data/lib/r10k/errors/formatting.rb +28 -0
  42. data/lib/r10k/execution.rb +2 -0
  43. data/lib/r10k/git/cache.rb +1 -6
  44. data/lib/r10k/git/errors.rb +1 -2
  45. data/lib/r10k/git/ref.rb +1 -1
  46. data/lib/r10k/module.rb +1 -1
  47. data/lib/r10k/module/base.rb +94 -2
  48. data/lib/r10k/module/forge.rb +33 -30
  49. data/lib/r10k/module/git.rb +13 -9
  50. data/lib/r10k/module/svn.rb +41 -28
  51. data/lib/r10k/puppetfile.rb +17 -1
  52. data/lib/r10k/semver.rb +2 -0
  53. data/lib/r10k/source/base.rb +8 -0
  54. data/lib/r10k/source/git.rb +1 -1
  55. data/lib/r10k/source/svn.rb +23 -5
  56. data/lib/r10k/svn/remote.rb +23 -3
  57. data/lib/r10k/svn/working_dir.rb +60 -9
  58. data/lib/r10k/task.rb +1 -0
  59. data/lib/r10k/task/deployment.rb +9 -1
  60. data/lib/r10k/task/environment.rb +2 -0
  61. data/lib/r10k/task/module.rb +1 -0
  62. data/lib/r10k/task/puppetfile.rb +3 -0
  63. data/lib/r10k/task_runner.rb +1 -0
  64. data/lib/r10k/util/attempt.rb +84 -0
  65. data/lib/r10k/util/basedir.rb +65 -0
  66. data/lib/r10k/util/purgeable.rb +55 -45
  67. data/lib/r10k/util/setopts.rb +53 -0
  68. data/lib/r10k/util/subprocess.rb +6 -30
  69. data/lib/r10k/util/subprocess/posix/runner.rb +29 -2
  70. data/lib/r10k/util/subprocess/result.rb +17 -4
  71. data/lib/r10k/util/subprocess/subprocess_error.rb +24 -0
  72. data/lib/r10k/version.rb +1 -1
  73. data/r10k.gemspec +7 -29
  74. data/spec/fixtures/unit/puppetfile/invalid-syntax/Puppetfile +1 -0
  75. data/spec/fixtures/unit/puppetfile/load-error/Puppetfile +1 -0
  76. data/spec/matchers/exit_with.rb +28 -0
  77. data/spec/r10k-mocks.rb +3 -0
  78. data/spec/r10k-mocks/mock_config.rb +28 -0
  79. data/spec/r10k-mocks/mock_env.rb +7 -0
  80. data/spec/r10k-mocks/mock_source.rb +10 -0
  81. data/spec/shared-examples/git-ref.rb +7 -7
  82. data/spec/spec_helper.rb +17 -5
  83. data/spec/unit/action/cri_runner_spec.rb +76 -0
  84. data/spec/unit/action/puppetfile/cri_action_spec.rb +65 -0
  85. data/spec/unit/action/runner_spec.rb +64 -0
  86. data/spec/unit/action/visitor_spec.rb +39 -0
  87. data/spec/unit/deployment_spec.rb +142 -0
  88. data/spec/unit/environment/base_spec.rb +38 -0
  89. data/spec/unit/environment/git_spec.rb +40 -10
  90. data/spec/unit/environment/svn_spec.rb +41 -4
  91. data/spec/unit/errors/formatting_spec.rb +84 -0
  92. data/spec/unit/git/alternates_spec.rb +1 -1
  93. data/spec/unit/git/head_spec.rb +1 -1
  94. data/spec/unit/git/ref_spec.rb +1 -1
  95. data/spec/unit/git/working_dir_spec.rb +1 -1
  96. data/spec/unit/module/base_spec.rb +72 -0
  97. data/spec/unit/module/forge_spec.rb +49 -8
  98. data/spec/unit/module/git_spec.rb +78 -0
  99. data/spec/unit/module/svn_spec.rb +40 -4
  100. data/spec/unit/module_spec.rb +3 -3
  101. data/spec/unit/puppetfile_spec.rb +84 -0
  102. data/spec/unit/settings/container_spec.rb +1 -1
  103. data/spec/unit/source/base_spec.rb +31 -0
  104. data/spec/unit/source/git_spec.rb +7 -7
  105. data/spec/unit/source/svn_spec.rb +1 -1
  106. data/spec/unit/svn/working_dir_spec.rb +56 -0
  107. data/spec/unit/util/attempt_spec.rb +82 -0
  108. data/spec/unit/util/setopts_spec.rb +59 -0
  109. data/spec/unit/util/subprocess/result_spec.rb +36 -0
  110. data/spec/unit/util/subprocess/subprocess_error_spec.rb +26 -0
  111. data/spec/unit/util/subprocess_spec.rb +2 -7
  112. metadata +83 -100
  113. data/.nodeset.yml +0 -7
  114. data/.rspec +0 -1
  115. data/README.markdown +0 -276
  116. data/Rakefile +0 -1
  117. data/doc/puppetfile.markdown +0 -87
  118. data/spec/rspec-system-r10k/puppetfile.rb +0 -24
  119. data/spec/rspec-system-r10k/tmpdir.rb +0 -32
  120. data/spec/system-provisioning/el.rb +0 -38
  121. data/spec/system/module/forge/install_spec.rb +0 -51
  122. data/spec/system/module/git/install_spec.rb +0 -117
  123. data/spec/system/module/svn/install_spec.rb +0 -51
  124. data/spec/system/module/svn/update_spec.rb +0 -38
  125. data/spec/system/spec_helper.rb +0 -60
  126. data/spec/system/system-helpers.rb +0 -4
  127. data/spec/system/version_spec.rb +0 -7
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'r10k/action/cri_runner'
3
+
4
+ describe R10K::Action::CriRunner do
5
+
6
+ let(:action_class) do
7
+ Class.new do
8
+ attr_reader :opts
9
+ attr_reader :argv
10
+
11
+ def initialize(opts, argv)
12
+ @opts = opts
13
+ @argv = argv
14
+ end
15
+
16
+ def call
17
+ @opts[:runok]
18
+ end
19
+ end
20
+ end
21
+
22
+ subject(:cri_runner) { described_class.wrap(action_class) }
23
+
24
+ let(:opts) { {:value => :yep} }
25
+ let(:argv) { %w[value yes] }
26
+
27
+ describe "handling options" do
28
+ it "adapts the :verbose flag to :loglevel" do
29
+ input = {:value => :yep, :verbose => 'DEBUG'}
30
+ output = {:value => :yep, :loglevel => 'DEBUG'}
31
+ expect(cri_runner.handle_opts(input)).to eq(output)
32
+ end
33
+
34
+ it "sets the non-argument form of :verbose to :loglevel => 'INFO'" do
35
+ input = {:value => :yep, :verbose => true}
36
+ output = {:value => :yep, :loglevel => 'INFO'}
37
+ expect(cri_runner.handle_opts(input)).to eq(output)
38
+ end
39
+ end
40
+
41
+ describe "handling arguments" do
42
+ it "sets the arguments as-is" do
43
+ expect(cri_runner.handle_argv(%w[one two])).to eq(%w[one two])
44
+ end
45
+ end
46
+
47
+ describe "proxying invocations to .new" do
48
+ it "returns itself" do
49
+ expect(cri_runner.new(opts, argv, :cri_cmd)).to eql cri_runner
50
+ end
51
+
52
+ it "handles options" do
53
+ expect(cri_runner).to receive(:handle_opts)
54
+ cri_runner.new({:value => :yep, :verbose => 'DEBUG'}, argv, :cri_cmd)
55
+ end
56
+
57
+ it "handles arguments" do
58
+ expect(cri_runner).to receive(:handle_argv)
59
+ cri_runner.new({:value => :yep, :verbose => 'DEBUG'}, argv, :cri_cmd)
60
+ end
61
+ end
62
+
63
+ describe "calling" do
64
+ it "exits with a return value of 0 if the action returned true" do
65
+ expect {
66
+ cri_runner.new({:runok => true}, []).call
67
+ }.to exit_with(0)
68
+ end
69
+
70
+ it "exits with a return value of 1 if the action returned false" do
71
+ expect {
72
+ cri_runner.new({:runok => false}, []).call
73
+ }.to exit_with(1)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'r10k/action/puppetfile/cri_runner'
3
+
4
+ describe R10K::Action::Puppetfile::CriRunner do
5
+
6
+ let(:action_class) do
7
+ Class.new do
8
+ attr_reader :opts
9
+ attr_reader :argv
10
+
11
+ def initialize(opts, argv)
12
+ @opts = opts
13
+ @argv = argv
14
+ end
15
+
16
+ def call
17
+ @opts[:runok]
18
+ end
19
+ end
20
+ end
21
+
22
+ subject(:cri_runner) { described_class.wrap(action_class) }
23
+
24
+ let(:opts) { {:value => :yep} }
25
+ let(:argv) { %w[value yes] }
26
+
27
+ describe "handling options" do
28
+ it "sets the root to the current wd" do
29
+ expect(cri_runner).to receive(:wd).and_return('/some/nonexistent')
30
+ expect(cri_runner.handle_opts({})).to include(:root => '/some/nonexistent')
31
+ end
32
+
33
+ describe "for the moduledir" do
34
+ before do
35
+ allow(cri_runner).to receive(:env).and_return({'PUPPETFILE_DIR' => '/some/nonexistent/modules'})
36
+ end
37
+
38
+ it "sets the option from the environment when the cli option is not given" do
39
+ opts = {}
40
+ expect(cri_runner.handle_opts(opts)).to include(:moduledir => '/some/nonexistent/modules')
41
+ end
42
+
43
+ it "doesn't set the option from the environment when the cli option is given" do
44
+ opts = {:moduledir => '/some/other/nonexistent/modules'}
45
+ expect(cri_runner.handle_opts(opts)).to include(:moduledir => '/some/other/nonexistent/modules')
46
+ end
47
+ end
48
+
49
+ describe "for the puppetfile path" do
50
+ before do
51
+ allow(cri_runner).to receive(:env).and_return({'PUPPETFILE' => '/some/nonexistent/Puppetfile'})
52
+ end
53
+
54
+ it "sets the option from the environment when the cli option is not given" do
55
+ opts = {}
56
+ expect(cri_runner.handle_opts(opts)).to include(:puppetfile => '/some/nonexistent/Puppetfile')
57
+ end
58
+
59
+ it "doesn't set the option from the environment when the cli option is given" do
60
+ opts = {:puppetfile => '/some/other/nonexistent/modules'}
61
+ expect(cri_runner.handle_opts(opts)).to include(:puppetfile => '/some/other/nonexistent/modules')
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'r10k/action/runner'
3
+
4
+
5
+ describe R10K::Action::Runner do
6
+
7
+ let(:action_class) do
8
+ Class.new do
9
+ attr_reader :opts
10
+ attr_reader :argv
11
+
12
+ def initialize(opts, argv)
13
+ @opts = opts
14
+ @argv = argv
15
+ end
16
+
17
+ def call
18
+ @argv.map(&:upcase)
19
+ end
20
+ end
21
+ end
22
+
23
+ subject(:runner) { described_class.new({:opts => :yep}, %w[args yes], action_class) }
24
+
25
+ describe "instantiating the wrapped class" do
26
+ it "creates an instance of the class" do
27
+ expect(runner.instance).to be_a_kind_of action_class
28
+ end
29
+
30
+ it "passes the opts and argv to the instance" do
31
+ expect(runner.instance.opts).to eq(:opts => :yep)
32
+ expect(runner.instance.argv).to eq(%w[args yes])
33
+ end
34
+
35
+ it "strips out options that the runner handles" do
36
+ runner = described_class.new({:opts => :yep, :loglevel => 'FATAL'}, %w[args yes], action_class)
37
+ expect(runner.instance.opts).to eq(:opts => :yep)
38
+ end
39
+ end
40
+
41
+ describe "calling" do
42
+ it "configures logging" do
43
+ expect(runner).to receive(:setup_logging)
44
+ runner.call
45
+ end
46
+
47
+ it "returns the result of the wrapped class #call method" do
48
+ expect(runner.call).to eq %w[ARGS YES]
49
+ end
50
+ end
51
+
52
+ describe "configuring logging" do
53
+ it "sets the log level if :loglevel is provided" do
54
+ runner = described_class.new({:opts => :yep, :loglevel => 'FATAL'}, %w[args yes], action_class)
55
+ expect(R10K::Logging).to receive(:level=).with('FATAL')
56
+ runner.call
57
+ end
58
+
59
+ it "does not modify the loglevel if :loglevel is not provided" do
60
+ expect(R10K::Logging).to_not receive(:level=)
61
+ runner.call
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'r10k/action/visitor'
3
+ require 'r10k/logging'
4
+
5
+ describe R10K::Action::Visitor do
6
+ let(:visitor_class) do
7
+ Class.new do
8
+ include R10K::Action::Visitor
9
+ include R10K::Logging
10
+ attr_accessor :trace
11
+
12
+ def visit_error(other)
13
+ raise ArgumentError, "no soup for you"
14
+ end
15
+ end
16
+ end
17
+
18
+ subject { visitor_class.new }
19
+
20
+ it "dispatches visit invocations to the type specific method" do
21
+ expect(subject).to receive(:visit_sym).with(:hi)
22
+ subject.visit(:sym, :hi)
23
+ end
24
+
25
+ describe "when a visit_ method raises an error" do
26
+
27
+ [true, false].each do |trace|
28
+ msg = trace ? "a" : "no"
29
+ it "logs the error with #{msg} backtrace when trace is #{trace}" do
30
+ subject.trace = trace
31
+ expect(R10K::Errors::Formatting).to(
32
+ receive(:format_exception).with(instance_of(ArgumentError), trace)
33
+ ).and_return("errmsg")
34
+ expect(subject.logger).to receive(:error).with('errmsg')
35
+ subject.visit(:error, :hi)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+ require 'r10k/deployment'
3
+ require 'tmpdir'
4
+
5
+ describe R10K::Deployment do
6
+
7
+ let(:confdir) { Dir.mktmpdir }
8
+
9
+ let(:config) do
10
+ R10K::Deployment::MockConfig.new(
11
+ :sources => {
12
+ :control => {
13
+ :type => :mock,
14
+ :basedir => File.join(confdir, 'environments'),
15
+ :environments => %w[first second third],
16
+ },
17
+ :hiera => {
18
+ :type => :mock,
19
+ :basedir => File.join(confdir, 'hiera'),
20
+ :environments => %w[fourth fifth sixth],
21
+ }
22
+ }
23
+ )
24
+ end
25
+
26
+ subject(:deployment) { described_class.new(config) }
27
+
28
+ let(:control) { deployment.sources.find { |source| source.name == :control } }
29
+ let(:hiera) { deployment.sources.find { |source| source.name == :hiera } }
30
+
31
+ describe "loading" do
32
+ describe "sources" do
33
+ it "creates a source for each key in the ':sources' config entry" do
34
+ expect(control.basedir).to eq(File.join(confdir, 'environments'))
35
+ expect(hiera.basedir).to eq(File.join(confdir, 'hiera'))
36
+ end
37
+ end
38
+
39
+ describe "loading environments" do
40
+ it "loads environments from each source" do
41
+ %w[first second third fourth fifth sixth].each do |env|
42
+ expect(deployment.environments.map(&:name)).to include(env)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "preloading" do
49
+ it "invokes #preload! on each source" do
50
+ deployment.sources.each do |source|
51
+ expect(source).to receive(:preload!)
52
+ end
53
+ deployment.preload!
54
+ end
55
+ end
56
+
57
+ describe "paths" do
58
+ it "retrieves the path for each source" do
59
+ expect(deployment.paths).to include(File.join(confdir, 'environments'))
60
+ expect(deployment.paths).to include(File.join(confdir, 'hiera'))
61
+ end
62
+ end
63
+
64
+ describe "paths and sources" do
65
+ it "retrieves the path for each source" do
66
+ p_a_s = deployment.paths_and_sources
67
+
68
+ expect(p_a_s[File.join(confdir, 'environments')]).to eq([control])
69
+ expect(p_a_s[File.join(confdir, 'hiera')]).to eq([hiera])
70
+ end
71
+ end
72
+
73
+ describe "purging" do
74
+ it "purges each managed directory" do
75
+ env_basedir = double("basedir environments")
76
+ hiera_basedir = double("basedir hiera")
77
+
78
+ expect(env_basedir).to receive(:purge!)
79
+ expect(hiera_basedir).to receive(:purge!)
80
+
81
+ expect(R10K::Util::Basedir).to receive(:new).with(File.join(confdir, 'environments'), [control]).and_return(env_basedir)
82
+ expect(R10K::Util::Basedir).to receive(:new).with(File.join(confdir, 'hiera'), [hiera]).and_return(hiera_basedir)
83
+
84
+ deployment.purge!
85
+ end
86
+ end
87
+
88
+ describe "accepting a visitor" do
89
+ it "passes itself to the visitor" do
90
+ visitor = spy('visitor')
91
+ expect(visitor).to receive(:visit).with(:deployment, subject)
92
+ subject.accept(visitor)
93
+ end
94
+
95
+ it "passes the visitor to each environment if the visitor yields" do
96
+ visitor = spy('visitor')
97
+ expect(visitor).to receive(:visit) do |type, other, &block|
98
+ expect(type).to eq :deployment
99
+ expect(other).to eq subject
100
+ block.call
101
+ end
102
+
103
+ source1 = spy('source')
104
+ expect(source1).to receive(:accept).with(visitor)
105
+ source2 = spy('source')
106
+ expect(source2).to receive(:accept).with(visitor)
107
+
108
+ expect(subject).to receive(:sources).and_return([source1, source2])
109
+ subject.accept(visitor)
110
+ end
111
+ end
112
+ end
113
+
114
+ describe R10K::Deployment, "with environment collisions" do
115
+
116
+ let(:confdir) { Dir.mktmpdir }
117
+
118
+ let(:config) do
119
+ R10K::Deployment::MockConfig.new(
120
+ :sources => {
121
+ :s1 => {
122
+ :type => :mock,
123
+ :basedir => File.join(confdir, 'environments'),
124
+ :environments => %w[first second third],
125
+ },
126
+ :s2 => {
127
+ :type => :mock,
128
+ :basedir => File.join(confdir, 'environments'),
129
+ :environments => %w[third fourth fifth],
130
+ }
131
+ }
132
+ )
133
+ end
134
+
135
+ subject(:deployment) { described_class.new(config) }
136
+
137
+ it "raises an error when validating" do
138
+ expect {
139
+ deployment.validate!
140
+ }.to raise_error(R10K::R10KError, /Environment collision at .* between s\d:third and s\d:third/)
141
+ end
142
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'r10k/environment'
3
+
4
+ describe R10K::Environment::Base do
5
+
6
+ subject(:environment) { described_class.new('envname', '/some/imaginary/path', 'env_name', {}) }
7
+
8
+ it "can return the fully qualified path" do
9
+ expect(environment.path).to eq(Pathname.new('/some/imaginary/path/env_name'))
10
+ end
11
+
12
+ it "raises an exception when #sync is called" do
13
+ expect { environment.sync }.to raise_error(NotImplementedError)
14
+ end
15
+
16
+ describe "accepting a visitor" do
17
+ it "passes itself to the visitor" do
18
+ visitor = spy('visitor')
19
+ expect(visitor).to receive(:visit).with(:environment, subject)
20
+ subject.accept(visitor)
21
+ end
22
+
23
+ it "passes the visitor to the puppetfile if the visitor yields" do
24
+ visitor = spy('visitor')
25
+ expect(visitor).to receive(:visit) do |type, other, &block|
26
+ expect(type).to eq :environment
27
+ expect(other).to eq subject
28
+ block.call
29
+ end
30
+
31
+ pf = spy('puppetfile')
32
+ expect(pf).to receive(:accept).with(visitor)
33
+
34
+ expect(subject).to receive(:puppetfile).and_return(pf)
35
+ subject.accept(visitor)
36
+ end
37
+ end
38
+ end
@@ -40,17 +40,8 @@ describe R10K::Environment::Git do
40
40
  end
41
41
 
42
42
  describe "synchronizing the environment" do
43
- it "updates all modules when creating a new environment" do
44
- allow(working_dir).to receive(:cloned?).and_return(false)
43
+ it "syncs the working directory" do
45
44
  expect(working_dir).to receive(:sync)
46
- expect(subject).to receive(:sync_modules)
47
- subject.sync
48
- end
49
-
50
- it "does not update all modules when updating an existing environment" do
51
- allow(working_dir).to receive(:cloned?).and_return(true)
52
- expect(working_dir).to receive(:sync)
53
- expect(subject).to_not receive(:sync_modules)
54
45
  subject.sync
55
46
  end
56
47
  end
@@ -78,4 +69,43 @@ describe R10K::Environment::Git do
78
69
  expect(subject.modules).to eq([:modules])
79
70
  end
80
71
  end
72
+
73
+ describe "determining the status" do
74
+ it "is absent when the working directory is absent" do
75
+ expect(working_dir).to receive(:exist?).and_return false
76
+ expect(subject.status).to eq :absent
77
+ end
78
+
79
+ it "is mismatched when the working directory is not git" do
80
+ expect(working_dir).to receive(:exist?).and_return true
81
+ expect(working_dir).to receive(:git?).and_return false
82
+ expect(subject.status).to eq :mismatched
83
+ end
84
+
85
+ it "is mismatched when the working directory remote doesn't match the desired remote" do
86
+ expect(working_dir).to receive(:exist?).and_return true
87
+ expect(working_dir).to receive(:git?).and_return true
88
+ expect(working_dir).to receive(:remote).and_return 'git://git-server.site/my-other-repo.git'
89
+ expect(subject.status).to eq :mismatched
90
+ end
91
+
92
+ it "is is outdated when the working directory has not been synced" do
93
+ expect(working_dir).to receive(:exist?).and_return true
94
+ expect(working_dir).to receive(:git?).and_return true
95
+ expect(working_dir).to receive(:remote).and_return 'git://git-server.site/my-repo.git'
96
+ expect(subject.status).to eq :outdated
97
+ end
98
+
99
+ it "is is insync when the working directory has been synced" do
100
+ expect(working_dir).to receive(:exist?).and_return true
101
+ expect(working_dir).to receive(:git?).and_return true
102
+ expect(working_dir).to receive(:remote).and_return 'git://git-server.site/my-repo.git'
103
+
104
+ expect(working_dir).to receive(:sync)
105
+
106
+ subject.sync
107
+
108
+ expect(subject.status).to eq :insync
109
+ end
110
+ end
81
111
  end