librarianp 0.1.2

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +255 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +235 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +55 -0
  10. data/Rakefile +28 -0
  11. data/VERSION +1 -0
  12. data/lib/librarian/action/base.rb +24 -0
  13. data/lib/librarian/action/clean.rb +44 -0
  14. data/lib/librarian/action/ensure.rb +24 -0
  15. data/lib/librarian/action/install.rb +95 -0
  16. data/lib/librarian/action/persist_resolution_mixin.rb +51 -0
  17. data/lib/librarian/action/resolve.rb +46 -0
  18. data/lib/librarian/action/update.rb +44 -0
  19. data/lib/librarian/action.rb +5 -0
  20. data/lib/librarian/algorithms.rb +133 -0
  21. data/lib/librarian/cli/manifest_presenter.rb +89 -0
  22. data/lib/librarian/cli.rb +225 -0
  23. data/lib/librarian/config/database.rb +205 -0
  24. data/lib/librarian/config/file_source.rb +47 -0
  25. data/lib/librarian/config/hash_source.rb +33 -0
  26. data/lib/librarian/config/source.rb +149 -0
  27. data/lib/librarian/config.rb +7 -0
  28. data/lib/librarian/dependency.rb +153 -0
  29. data/lib/librarian/dsl/receiver.rb +42 -0
  30. data/lib/librarian/dsl/target.rb +171 -0
  31. data/lib/librarian/dsl.rb +102 -0
  32. data/lib/librarian/environment/runtime_cache.rb +101 -0
  33. data/lib/librarian/environment.rb +230 -0
  34. data/lib/librarian/error.rb +4 -0
  35. data/lib/librarian/helpers.rb +29 -0
  36. data/lib/librarian/linter/source_linter.rb +55 -0
  37. data/lib/librarian/lockfile/compiler.rb +66 -0
  38. data/lib/librarian/lockfile/parser.rb +123 -0
  39. data/lib/librarian/lockfile.rb +29 -0
  40. data/lib/librarian/logger.rb +46 -0
  41. data/lib/librarian/manifest.rb +146 -0
  42. data/lib/librarian/manifest_set.rb +150 -0
  43. data/lib/librarian/mock/cli.rb +19 -0
  44. data/lib/librarian/mock/dsl.rb +15 -0
  45. data/lib/librarian/mock/environment.rb +21 -0
  46. data/lib/librarian/mock/extension.rb +9 -0
  47. data/lib/librarian/mock/source/mock/registry.rb +83 -0
  48. data/lib/librarian/mock/source/mock.rb +80 -0
  49. data/lib/librarian/mock/source.rb +1 -0
  50. data/lib/librarian/mock/version.rb +5 -0
  51. data/lib/librarian/mock.rb +1 -0
  52. data/lib/librarian/posix.rb +129 -0
  53. data/lib/librarian/resolution.rb +46 -0
  54. data/lib/librarian/resolver/implementation.rb +238 -0
  55. data/lib/librarian/resolver.rb +94 -0
  56. data/lib/librarian/rspec/support/cli_macro.rb +120 -0
  57. data/lib/librarian/source/basic_api.rb +45 -0
  58. data/lib/librarian/source/git/repository.rb +193 -0
  59. data/lib/librarian/source/git.rb +172 -0
  60. data/lib/librarian/source/local.rb +54 -0
  61. data/lib/librarian/source/path.rb +56 -0
  62. data/lib/librarian/source.rb +2 -0
  63. data/lib/librarian/spec.rb +13 -0
  64. data/lib/librarian/spec_change_set.rb +173 -0
  65. data/lib/librarian/specfile.rb +19 -0
  66. data/lib/librarian/support/abstract_method.rb +21 -0
  67. data/lib/librarian/ui.rb +64 -0
  68. data/lib/librarian/version.rb +3 -0
  69. data/lib/librarian.rb +11 -0
  70. data/librarian.gemspec +47 -0
  71. data/spec/functional/cli_spec.rb +27 -0
  72. data/spec/functional/posix_spec.rb +32 -0
  73. data/spec/functional/source/git/repository_spec.rb +199 -0
  74. data/spec/functional/source/git_spec.rb +174 -0
  75. data/spec/support/fakefs.rb +37 -0
  76. data/spec/support/method_patch_macro.rb +30 -0
  77. data/spec/support/project_path_macro.rb +14 -0
  78. data/spec/support/with_env_macro.rb +22 -0
  79. data/spec/unit/action/base_spec.rb +18 -0
  80. data/spec/unit/action/clean_spec.rb +102 -0
  81. data/spec/unit/action/ensure_spec.rb +37 -0
  82. data/spec/unit/action/install_spec.rb +111 -0
  83. data/spec/unit/algorithms_spec.rb +131 -0
  84. data/spec/unit/config/database_spec.rb +320 -0
  85. data/spec/unit/dependency/requirement_spec.rb +12 -0
  86. data/spec/unit/dependency_spec.rb +212 -0
  87. data/spec/unit/dsl_spec.rb +173 -0
  88. data/spec/unit/environment/runtime_cache_spec.rb +73 -0
  89. data/spec/unit/environment_spec.rb +209 -0
  90. data/spec/unit/lockfile/parser_spec.rb +162 -0
  91. data/spec/unit/lockfile_spec.rb +65 -0
  92. data/spec/unit/manifest/version_spec.rb +11 -0
  93. data/spec/unit/manifest_set_spec.rb +202 -0
  94. data/spec/unit/manifest_spec.rb +36 -0
  95. data/spec/unit/mock/environment_spec.rb +25 -0
  96. data/spec/unit/mock/source/mock_spec.rb +22 -0
  97. data/spec/unit/resolver_spec.rb +299 -0
  98. data/spec/unit/source/git_spec.rb +29 -0
  99. data/spec/unit/spec_change_set_spec.rb +169 -0
  100. metadata +257 -0
@@ -0,0 +1,202 @@
1
+ require 'librarian'
2
+
3
+ module Librarian
4
+ describe ManifestSet do
5
+
6
+ describe ".new" do
7
+ let(:jelly) { double(:name => "jelly") }
8
+ let(:butter) { double(:name => "butter") }
9
+ let(:jam) { double(:name => "jam") }
10
+
11
+ let(:array) { [jelly, butter, jam] }
12
+ let(:hash) { {"jelly" => jelly, "butter" => butter, "jam" => jam} }
13
+
14
+ context "with an array" do
15
+ let(:set) { described_class.new(array) }
16
+
17
+ it "should give back the array" do
18
+ expect(set.to_a).to match_array( array )
19
+ end
20
+
21
+ it "should give back the hash" do
22
+ expect(set.to_hash).to eq hash
23
+ end
24
+ end
25
+
26
+ context "with a hash" do
27
+ let(:set) { described_class.new(hash) }
28
+
29
+ it "should give back the array" do
30
+ expect(set.to_a).to match_array( array )
31
+ end
32
+
33
+ it "should give back the hash" do
34
+ expect(set.to_hash).to eq hash
35
+ end
36
+ end
37
+ end
38
+
39
+ # Does not trace dependencies.
40
+ # That's why it's "shallow".
41
+ describe "#shallow_strip!" do
42
+ let(:jelly) { double(:name => "jelly") }
43
+ let(:butter) { double(:name => "butter") }
44
+ let(:jam) { double(:name => "jam") }
45
+
46
+ let(:set) { described_class.new([jelly, butter, jam]) }
47
+
48
+ it "should not do anything when given no names" do
49
+ set.shallow_strip!([])
50
+
51
+ expect(set.to_a).to match_array( [jelly, butter, jam] )
52
+ end
53
+
54
+ it "should remove only the named elements" do
55
+ set.shallow_strip!(["butter", "jam"])
56
+
57
+ expect(set.to_a).to match_array( [jelly] )
58
+ end
59
+
60
+ it "should allow removing all the elements" do
61
+ set.shallow_strip!(["jelly", "butter", "jam"])
62
+
63
+ expect(set.to_a).to match_array( [] )
64
+ end
65
+ end
66
+
67
+ # Does not trace dependencies.
68
+ # That's why it's "shallow".
69
+ describe "#shallow_keep!" do
70
+ let(:jelly) { double(:name => "jelly") }
71
+ let(:butter) { double(:name => "butter") }
72
+ let(:jam) { double(:name => "jam") }
73
+
74
+ let(:set) { described_class.new([jelly, butter, jam]) }
75
+
76
+ it "should empty the set when given no names" do
77
+ set.shallow_keep!([])
78
+
79
+ expect(set.to_a).to match_array( [] )
80
+ end
81
+
82
+ it "should keep only the named elements" do
83
+ set.shallow_keep!(["butter", "jam"])
84
+
85
+ expect(set.to_a).to match_array( [butter, jam] )
86
+ end
87
+
88
+ it "should allow keeping all the elements" do
89
+ set.shallow_keep!(["jelly", "butter", "jam"])
90
+
91
+ expect(set.to_a).to match_array( [jelly, butter, jam] )
92
+ end
93
+ end
94
+
95
+ describe "#deep_strip!" do
96
+ def man(o)
97
+ k, v = o.keys.first, o.values.first
98
+ double(k, :name => k, :dependencies => deps(v))
99
+ end
100
+
101
+ def deps(names)
102
+ names.map{|n| double(:name => n)}
103
+ end
104
+
105
+ let(:a) { man("a" => %w[b c]) }
106
+ let(:b) { man("b" => %w[c d]) }
107
+ let(:c) { man("c" => %w[ ]) }
108
+ let(:d) { man("d" => %w[ ]) }
109
+
110
+ let(:e) { man("e" => %w[f g]) }
111
+ let(:f) { man("f" => %w[g h]) }
112
+ let(:g) { man("g" => %w[ ]) }
113
+ let(:h) { man("h" => %w[ ]) }
114
+
115
+ let(:set) { described_class.new([a, b, c, d, e, f, g, h]) }
116
+
117
+ it "should not do anything when given no names" do
118
+ set.deep_strip!([])
119
+
120
+ expect(set.to_a).to match_array( [a, b, c, d, e, f, g, h] )
121
+ end
122
+
123
+ it "should remove just the named elements if they have no dependencies" do
124
+ set.deep_strip!(["c", "h"])
125
+
126
+ expect(set.to_a).to match_array( [a, b, d, e, f, g] )
127
+ end
128
+
129
+ it "should remove the named elements and all their dependencies" do
130
+ set.deep_strip!(["b"])
131
+
132
+ expect(set.to_a).to match_array( [a, e, f, g, h] )
133
+ end
134
+
135
+ it "should remove an entire tree of dependencies" do
136
+ set.deep_strip!(["e"])
137
+
138
+ expect(set.to_a).to match_array( [a, b, c, d] )
139
+ end
140
+
141
+ it "should allow removing all the elements" do
142
+ set.deep_strip!(["a", "e"])
143
+
144
+ expect(set.to_a).to match_array( [] )
145
+ end
146
+ end
147
+
148
+ describe "#deep_keep!" do
149
+ def man(o)
150
+ k, v = o.keys.first, o.values.first
151
+ double(k, :name => k, :dependencies => deps(v))
152
+ end
153
+
154
+ def deps(names)
155
+ names.map{|n| double(:name => n)}
156
+ end
157
+
158
+ let(:a) { man("a" => %w[b c]) }
159
+ let(:b) { man("b" => %w[c d]) }
160
+ let(:c) { man("c" => %w[ ]) }
161
+ let(:d) { man("d" => %w[ ]) }
162
+
163
+ let(:e) { man("e" => %w[f g]) }
164
+ let(:f) { man("f" => %w[g h]) }
165
+ let(:g) { man("g" => %w[ ]) }
166
+ let(:h) { man("h" => %w[ ]) }
167
+
168
+ let(:set) { described_class.new([a, b, c, d, e, f, g, h]) }
169
+
170
+ it "should remove all the elements when given no names" do
171
+ set.deep_keep!([])
172
+
173
+ expect(set.to_a).to match_array( [] )
174
+ end
175
+
176
+ it "should keep just the named elements if they have no dependencies" do
177
+ set.deep_keep!(["c", "h"])
178
+
179
+ expect(set.to_a).to match_array( [c, h] )
180
+ end
181
+
182
+ it "should keep the named elements and all their dependencies" do
183
+ set.deep_keep!(["b"])
184
+
185
+ expect(set.to_a).to match_array( [b, c, d] )
186
+ end
187
+
188
+ it "should keep an entire tree of dependencies" do
189
+ set.deep_keep!(["e"])
190
+
191
+ expect(set.to_a).to match_array( [e, f, g, h] )
192
+ end
193
+
194
+ it "should allow keeping all the elements" do
195
+ set.deep_keep!(["a", "e"])
196
+
197
+ expect(set.to_a).to match_array( [a, b, c, d, e, f, g, h] )
198
+ end
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,36 @@
1
+ require "librarian/manifest"
2
+
3
+ describe Librarian::Manifest do
4
+
5
+ describe "validations" do
6
+
7
+ context "when the name is blank" do
8
+ it "raises" do
9
+ expect { described_class.new(nil, "") }.
10
+ to raise_error(ArgumentError, %{name ("") must be sensible})
11
+ end
12
+ end
13
+
14
+ context "when the name has leading whitespace" do
15
+ it "raises" do
16
+ expect { described_class.new(nil, " the-name") }.
17
+ to raise_error(ArgumentError, %{name (" the-name") must be sensible})
18
+ end
19
+ end
20
+
21
+ context "when the name has trailing whitespace" do
22
+ it "raises" do
23
+ expect { described_class.new(nil, "the-name ") }.
24
+ to raise_error(ArgumentError, %{name ("the-name ") must be sensible})
25
+ end
26
+ end
27
+
28
+ context "when the name is a single character" do
29
+ it "passes" do
30
+ described_class.new(nil, "R")
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,25 @@
1
+ require "librarian/mock/environment"
2
+
3
+ module Librarian::Mock
4
+ describe Environment do
5
+
6
+ let(:env) { described_class.new }
7
+
8
+ describe "#version" do
9
+ specify { expect(env.version).to eq Librarian::VERSION }
10
+ end
11
+
12
+ describe "#adapter_module" do
13
+ specify { expect(env.adapter_module).to eq Librarian::Mock }
14
+ end
15
+
16
+ describe "#adapter_name" do
17
+ specify { expect(env.adapter_name).to eq "mock" }
18
+ end
19
+
20
+ describe "#adapter_version" do
21
+ specify { expect(env.adapter_version).to eq Librarian::Mock::VERSION }
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ require "librarian/mock"
2
+
3
+ module Librarian
4
+ module Mock
5
+ module Source
6
+ describe Mock do
7
+
8
+ let(:env) { Librarian::Mock::Environment.new }
9
+
10
+ describe ".new" do
11
+
12
+ let(:source) { described_class.new(env, "source-a", {}) }
13
+ subject { source }
14
+
15
+ its(:environment) { should_not be_nil }
16
+
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,299 @@
1
+ require "pathname"
2
+ require "tmpdir"
3
+
4
+ require "support/fakefs"
5
+
6
+ require 'librarian/resolver'
7
+ require 'librarian/spec_change_set'
8
+ require 'librarian/mock'
9
+
10
+ module Librarian
11
+ describe Resolver do
12
+ include ::Support::FakeFS
13
+
14
+ let(:env) { Mock::Environment.new }
15
+ let(:resolver) { env.resolver }
16
+
17
+ context "a simple specfile" do
18
+
19
+ before do
20
+ env.registry :clear => true do
21
+ source 'source-1' do
22
+ spec 'butter', '1.1'
23
+ end
24
+ end
25
+ end
26
+
27
+ let(:spec) do
28
+ env.dsl do
29
+ src 'source-1'
30
+ dep 'butter'
31
+ end
32
+ end
33
+
34
+ let(:resolution) { resolver.resolve(spec) }
35
+
36
+ specify { expect(resolution).to be_correct }
37
+
38
+ end
39
+
40
+ context "a specfile with a dep from one src depending on a dep from another src" do
41
+
42
+ before do
43
+ env.registry :clear => true do
44
+ source 'source-1' do
45
+ spec 'butter', '1.1'
46
+ end
47
+ source 'source-2' do
48
+ spec 'jam', '1.2' do
49
+ dependency 'butter', '>= 1.0'
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ let(:spec) do
56
+ env.dsl do
57
+ src 'source-1'
58
+ src 'source-2' do
59
+ dep 'jam'
60
+ end
61
+ end
62
+ end
63
+
64
+ let(:resolution) { resolver.resolve(spec) }
65
+
66
+ specify { expect(resolution).to be_correct }
67
+
68
+ end
69
+
70
+ context "a specfile with a dep in multiple sources" do
71
+
72
+ before do
73
+ env.registry :clear => true do
74
+ source 'source-1' do
75
+ spec 'butter', '1.0'
76
+ spec 'butter', '1.1'
77
+ end
78
+ source 'source-2' do
79
+ spec 'butter', '1.0'
80
+ spec 'butter', '1.1'
81
+ end
82
+ source 'source-3' do
83
+ spec 'butter', '1.0'
84
+ end
85
+ end
86
+ end
87
+
88
+ let(:spec) do
89
+ env.dsl do
90
+ src 'source-1'
91
+ src 'source-2'
92
+ dep 'butter', '>= 1.1'
93
+ end
94
+ end
95
+
96
+ it "should have the expected number of sources" do
97
+ expect(spec).to have(2).sources
98
+ end
99
+
100
+ let(:resolution) { resolver.resolve(spec) }
101
+
102
+ specify { expect(resolution).to be_correct }
103
+
104
+ it "should have the manifest from the final source with a matching manifest" do
105
+ manifest = resolution.manifests.find{|m| m.name == "butter"}
106
+ expect(manifest.source.name).to eq "source-2"
107
+ end
108
+
109
+ end
110
+
111
+ context "a specfile with a dep depending on a nonexistent dep" do
112
+
113
+ before do
114
+ env.registry :clear => true do
115
+ source 'source-1' do
116
+ spec 'jam', '1.2' do
117
+ dependency 'butter', '>= 1.0'
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ let(:spec) do
124
+ env.dsl do
125
+ src 'source-1'
126
+ dep 'jam'
127
+ end
128
+ end
129
+
130
+ let(:resolution) { resolver.resolve(spec) }
131
+
132
+ specify { expect(resolution).to be_nil }
133
+
134
+ end
135
+
136
+ context "a specfile with conflicting constraints" do
137
+
138
+ before do
139
+ env.registry :clear => true do
140
+ source 'source-1' do
141
+ spec 'butter', '1.0'
142
+ spec 'butter', '1.1'
143
+ spec 'jam', '1.2' do
144
+ dependency 'butter', '1.1'
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ let(:spec) do
151
+ env.dsl do
152
+ src 'source-1'
153
+ dep 'butter', '1.0'
154
+ dep 'jam'
155
+ end
156
+ end
157
+
158
+ let(:resolution) { resolver.resolve(spec) }
159
+
160
+ specify { expect(resolution).to be_nil }
161
+
162
+ end
163
+
164
+ context "a specfile with cyclic constraints" do
165
+
166
+ before do
167
+ env.registry :clear => true do
168
+ source 'source-1' do
169
+ spec 'butter', '1.0' do
170
+ dependency 'jam', '2.0'
171
+ end
172
+ spec 'jam', '2.0' do
173
+ dependency 'butter', '1.0'
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ let(:spec) do
180
+ env.dsl do
181
+ src 'source-1'
182
+ dep 'butter'
183
+ end
184
+ end
185
+
186
+ let(:resolution) { resolver.resolve(spec) }
187
+
188
+ context "when cyclic resolutions are forbidden" do
189
+ let(:resolver) { env.resolver(:cyclic => false) }
190
+
191
+ specify { expect(resolution).to be_nil }
192
+ end
193
+
194
+ context "when cyclic resolutions are permitted" do
195
+ let(:resolver) { env.resolver(:cyclic => true) }
196
+
197
+ it "should have all the manifests" do
198
+ manifest_names = resolution.manifests.map(&:name).sort
199
+ expect(manifest_names).to be == %w[butter jam]
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ context "updating" do
206
+
207
+ it "should not work" do
208
+ env.registry :clear => true do
209
+ source 'source-1' do
210
+ spec 'butter', '1.0'
211
+ spec 'butter', '1.1'
212
+ spec 'jam', '1.2' do
213
+ dependency 'butter'
214
+ end
215
+ end
216
+ end
217
+ first_spec = env.dsl do
218
+ src 'source-1'
219
+ dep 'butter', '1.1'
220
+ dep 'jam'
221
+ end
222
+ first_resolution = resolver.resolve(first_spec)
223
+ expect(first_resolution).to be_correct
224
+ first_manifests = first_resolution.manifests
225
+ first_manifests_index = Hash[first_manifests.map{|m| [m.name, m]}]
226
+ expect(first_manifests_index['butter'].version.to_s).to eq '1.1'
227
+
228
+ second_spec = env.dsl do
229
+ src 'source-1'
230
+ dep 'butter', '1.0'
231
+ dep 'jam'
232
+ end
233
+ locked_manifests = ManifestSet.deep_strip(first_manifests, ['butter'])
234
+ second_resolution =resolver.resolve(second_spec, locked_manifests)
235
+ expect(second_resolution).to be_correct
236
+ second_manifests = second_resolution.manifests
237
+ second_manifests_index = Hash[second_manifests.map{|m| [m.name, m]}]
238
+ expect(second_manifests_index['butter'].version.to_s).to eq '1.0'
239
+ end
240
+
241
+ end
242
+
243
+ context "a change to the spec" do
244
+
245
+ it "should work" do
246
+ env.registry :clear => true do
247
+ source 'source-1' do
248
+ spec 'butter', '1.0'
249
+ end
250
+ source 'source-2' do
251
+ spec 'butter', '1.0'
252
+ end
253
+ end
254
+ spec = env.dsl do
255
+ src 'source-1'
256
+ dep 'butter'
257
+ end
258
+ lock = resolver.resolve(spec)
259
+ expect(lock).to be_correct
260
+
261
+ spec = env.dsl do
262
+ src 'source-1'
263
+ dep 'butter', :src => 'source-2'
264
+ end
265
+ changes = SpecChangeSet.new(env, spec, lock)
266
+ expect(changes).to_not be_same
267
+ manifests = ManifestSet.new(changes.analyze).to_hash
268
+ expect(manifests).to_not have_key('butter')
269
+ lock = resolver.resolve(spec, changes.analyze)
270
+ expect(lock).to be_correct
271
+ expect(lock.manifests.map{|m| m.name}).to include('butter')
272
+ manifest = lock.manifests.find{|m| m.name == 'butter'}
273
+ expect(manifest).to_not be_nil
274
+ expect(manifest.source.name).to eq 'source-2'
275
+ end
276
+
277
+ end
278
+
279
+ context "a pathname to a simple specfile" do
280
+ let(:pwd) { Pathname(Dir.tmpdir) }
281
+ let(:specfile_path) { pwd + "Mockfile" }
282
+ before { FileUtils.mkpath(pwd) }
283
+
284
+ def write!(path, text)
285
+ Pathname(path).open("wb"){|f| f.write(text)}
286
+ end
287
+
288
+ it "loads the specfile with the __FILE__" do
289
+ write! specfile_path, "src __FILE__"
290
+ spec = env.dsl(specfile_path)
291
+ expect(spec.sources).to have(1).item
292
+ source = spec.sources.first
293
+ expect(source.name).to eq specfile_path.to_s
294
+ end
295
+
296
+ end
297
+
298
+ end
299
+ end
@@ -0,0 +1,29 @@
1
+ require "librarian"
2
+
3
+ module Librarian
4
+ module Source
5
+ describe Git do
6
+
7
+ let(:env) { Environment.new }
8
+
9
+ describe "validating options for the specfile" do
10
+
11
+ context "with only known options" do
12
+ it "should not raise" do
13
+ expect { described_class.from_spec_args(env, "some://git/repo.git", :ref => "megapatches") }.
14
+ to_not raise_error
15
+ end
16
+ end
17
+
18
+ context "with an unknown option" do
19
+ it "should raise" do
20
+ expect { described_class.from_spec_args(env, "some://git/repo.git", :branch => "megapatches") }.
21
+ to raise_error Error, "unrecognized options: branch"
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end