entitlements 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/deploy-entitlements +10 -1
- data/lib/contracts-ruby2/CHANGELOG.markdown +115 -0
- data/lib/contracts-ruby2/Gemfile +17 -0
- data/lib/contracts-ruby2/LICENSE +23 -0
- data/lib/contracts-ruby2/README.md +108 -0
- data/lib/contracts-ruby2/Rakefile +8 -0
- data/lib/contracts-ruby2/TODO.markdown +6 -0
- data/lib/contracts-ruby2/TUTORIAL.md +773 -0
- data/lib/contracts-ruby2/benchmarks/bench.rb +67 -0
- data/lib/contracts-ruby2/benchmarks/hash.rb +69 -0
- data/lib/contracts-ruby2/benchmarks/invariants.rb +91 -0
- data/lib/contracts-ruby2/benchmarks/io.rb +62 -0
- data/lib/contracts-ruby2/benchmarks/wrap_test.rb +57 -0
- data/lib/contracts-ruby2/contracts.gemspec +17 -0
- data/lib/contracts-ruby2/cucumber.yml +1 -0
- data/lib/contracts-ruby2/dependabot.yml +20 -0
- data/lib/contracts-ruby2/features/README.md +17 -0
- data/lib/contracts-ruby2/features/basics/functype.feature +71 -0
- data/lib/contracts-ruby2/features/basics/pretty-print.feature +241 -0
- data/lib/contracts-ruby2/features/basics/simple_example.feature +210 -0
- data/lib/contracts-ruby2/features/builtin_contracts/README.md +22 -0
- data/lib/contracts-ruby2/features/builtin_contracts/and.feature +103 -0
- data/lib/contracts-ruby2/features/builtin_contracts/any.feature +44 -0
- data/lib/contracts-ruby2/features/builtin_contracts/args.feature +80 -0
- data/lib/contracts-ruby2/features/builtin_contracts/array_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/bool.feature +64 -0
- data/lib/contracts-ruby2/features/builtin_contracts/enum.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/eq.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/exactly.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/func.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/hash_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/int.feature +93 -0
- data/lib/contracts-ruby2/features/builtin_contracts/keyword_args.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/maybe.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/nat.feature +115 -0
- data/lib/contracts-ruby2/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts-ruby2/features/builtin_contracts/neg.feature +115 -0
- data/lib/contracts-ruby2/features/builtin_contracts/none.feature +145 -0
- data/lib/contracts-ruby2/features/builtin_contracts/not.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/num.feature +64 -0
- data/lib/contracts-ruby2/features/builtin_contracts/or.feature +83 -0
- data/lib/contracts-ruby2/features/builtin_contracts/pos.feature +116 -0
- data/lib/contracts-ruby2/features/builtin_contracts/range_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/respond_to.feature +78 -0
- data/lib/contracts-ruby2/features/builtin_contracts/send.feature +147 -0
- data/lib/contracts-ruby2/features/builtin_contracts/set_of.feature +1 -0
- data/lib/contracts-ruby2/features/builtin_contracts/xor.feature +99 -0
- data/lib/contracts-ruby2/features/support/env.rb +6 -0
- data/lib/contracts-ruby2/lib/contracts/attrs.rb +24 -0
- data/lib/contracts-ruby2/lib/contracts/builtin_contracts.rb +542 -0
- data/lib/contracts-ruby2/lib/contracts/call_with.rb +108 -0
- data/lib/contracts-ruby2/lib/contracts/core.rb +52 -0
- data/lib/contracts-ruby2/lib/contracts/decorators.rb +47 -0
- data/lib/contracts-ruby2/lib/contracts/engine/base.rb +136 -0
- data/lib/contracts-ruby2/lib/contracts/engine/eigenclass.rb +50 -0
- data/lib/contracts-ruby2/lib/contracts/engine/target.rb +70 -0
- data/lib/contracts-ruby2/lib/contracts/engine.rb +26 -0
- data/lib/contracts-ruby2/lib/contracts/errors.rb +71 -0
- data/lib/contracts-ruby2/lib/contracts/formatters.rb +136 -0
- data/lib/contracts-ruby2/lib/contracts/invariants.rb +68 -0
- data/lib/contracts-ruby2/lib/contracts/method_handler.rb +187 -0
- data/lib/contracts-ruby2/lib/contracts/method_reference.rb +100 -0
- data/lib/contracts-ruby2/lib/contracts/support.rb +61 -0
- data/lib/contracts-ruby2/lib/contracts/validators.rb +139 -0
- data/lib/contracts-ruby2/lib/contracts/version.rb +3 -0
- data/lib/contracts-ruby2/lib/contracts.rb +281 -0
- data/lib/contracts-ruby2/script/docs-release +3 -0
- data/lib/contracts-ruby2/script/docs-staging +3 -0
- data/lib/contracts-ruby2/script/rubocop.rb +5 -0
- data/lib/contracts-ruby2/spec/attrs_spec.rb +119 -0
- data/lib/contracts-ruby2/spec/builtin_contracts_spec.rb +461 -0
- data/lib/contracts-ruby2/spec/contracts_spec.rb +770 -0
- data/lib/contracts-ruby2/spec/fixtures/fixtures.rb +730 -0
- data/lib/contracts-ruby2/spec/invariants_spec.rb +17 -0
- data/lib/contracts-ruby2/spec/methods_spec.rb +54 -0
- data/lib/contracts-ruby2/spec/module_spec.rb +18 -0
- data/lib/contracts-ruby2/spec/override_validators_spec.rb +162 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
- data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
- data/lib/contracts-ruby2/spec/spec_helper.rb +102 -0
- data/lib/contracts-ruby2/spec/support.rb +10 -0
- data/lib/contracts-ruby2/spec/support_spec.rb +21 -0
- data/lib/contracts-ruby2/spec/validators_spec.rb +47 -0
- data/lib/contracts-ruby3/CHANGELOG.markdown +117 -0
- data/lib/contracts-ruby3/Gemfile +21 -0
- data/lib/contracts-ruby3/LICENSE +23 -0
- data/lib/contracts-ruby3/README.md +114 -0
- data/lib/contracts-ruby3/Rakefile +10 -0
- data/lib/contracts-ruby3/TODO.markdown +6 -0
- data/lib/contracts-ruby3/TUTORIAL.md +773 -0
- data/lib/contracts-ruby3/benchmarks/bench.rb +67 -0
- data/lib/contracts-ruby3/benchmarks/hash.rb +69 -0
- data/lib/contracts-ruby3/benchmarks/invariants.rb +91 -0
- data/lib/contracts-ruby3/benchmarks/io.rb +62 -0
- data/lib/contracts-ruby3/benchmarks/wrap_test.rb +57 -0
- data/lib/contracts-ruby3/contracts.gemspec +20 -0
- data/lib/contracts-ruby3/cucumber.yml +1 -0
- data/lib/contracts-ruby3/dependabot.yml +20 -0
- data/lib/contracts-ruby3/features/README.md +17 -0
- data/lib/contracts-ruby3/features/basics/functype.feature +71 -0
- data/lib/contracts-ruby3/features/basics/pretty-print.feature +241 -0
- data/lib/contracts-ruby3/features/basics/simple_example.feature +210 -0
- data/lib/contracts-ruby3/features/builtin_contracts/README.md +22 -0
- data/lib/contracts-ruby3/features/builtin_contracts/and.feature +103 -0
- data/lib/contracts-ruby3/features/builtin_contracts/any.feature +44 -0
- data/lib/contracts-ruby3/features/builtin_contracts/args.feature +80 -0
- data/lib/contracts-ruby3/features/builtin_contracts/array_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/bool.feature +64 -0
- data/lib/contracts-ruby3/features/builtin_contracts/enum.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/eq.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/exactly.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/func.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/hash_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/int.feature +93 -0
- data/lib/contracts-ruby3/features/builtin_contracts/keyword_args.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/maybe.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/nat.feature +115 -0
- data/lib/contracts-ruby3/features/builtin_contracts/nat_pos.feature +119 -0
- data/lib/contracts-ruby3/features/builtin_contracts/neg.feature +115 -0
- data/lib/contracts-ruby3/features/builtin_contracts/none.feature +145 -0
- data/lib/contracts-ruby3/features/builtin_contracts/not.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/num.feature +64 -0
- data/lib/contracts-ruby3/features/builtin_contracts/or.feature +83 -0
- data/lib/contracts-ruby3/features/builtin_contracts/pos.feature +116 -0
- data/lib/contracts-ruby3/features/builtin_contracts/range_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/respond_to.feature +78 -0
- data/lib/contracts-ruby3/features/builtin_contracts/send.feature +147 -0
- data/lib/contracts-ruby3/features/builtin_contracts/set_of.feature +1 -0
- data/lib/contracts-ruby3/features/builtin_contracts/xor.feature +99 -0
- data/lib/contracts-ruby3/features/support/env.rb +8 -0
- data/lib/contracts-ruby3/lib/contracts/attrs.rb +26 -0
- data/lib/contracts-ruby3/lib/contracts/builtin_contracts.rb +575 -0
- data/lib/contracts-ruby3/lib/contracts/call_with.rb +119 -0
- data/lib/contracts-ruby3/lib/contracts/core.rb +54 -0
- data/lib/contracts-ruby3/lib/contracts/decorators.rb +50 -0
- data/lib/contracts-ruby3/lib/contracts/engine/base.rb +137 -0
- data/lib/contracts-ruby3/lib/contracts/engine/eigenclass.rb +51 -0
- data/lib/contracts-ruby3/lib/contracts/engine/target.rb +72 -0
- data/lib/contracts-ruby3/lib/contracts/engine.rb +28 -0
- data/lib/contracts-ruby3/lib/contracts/errors.rb +74 -0
- data/lib/contracts-ruby3/lib/contracts/formatters.rb +140 -0
- data/lib/contracts-ruby3/lib/contracts/invariants.rb +72 -0
- data/lib/contracts-ruby3/lib/contracts/method_handler.rb +197 -0
- data/lib/contracts-ruby3/lib/contracts/method_reference.rb +102 -0
- data/lib/contracts-ruby3/lib/contracts/support.rb +63 -0
- data/lib/contracts-ruby3/lib/contracts/validators.rb +143 -0
- data/lib/contracts-ruby3/lib/contracts/version.rb +5 -0
- data/lib/contracts-ruby3/lib/contracts.rb +290 -0
- data/lib/contracts-ruby3/script/docs-release +3 -0
- data/lib/contracts-ruby3/script/docs-staging +3 -0
- data/lib/contracts-ruby3/script/rubocop.rb +5 -0
- data/lib/contracts-ruby3/spec/attrs_spec.rb +119 -0
- data/lib/contracts-ruby3/spec/builtin_contracts_spec.rb +457 -0
- data/lib/contracts-ruby3/spec/contracts_spec.rb +773 -0
- data/lib/contracts-ruby3/spec/fixtures/fixtures.rb +725 -0
- data/lib/contracts-ruby3/spec/invariants_spec.rb +17 -0
- data/lib/contracts-ruby3/spec/methods_spec.rb +54 -0
- data/lib/contracts-ruby3/spec/module_spec.rb +18 -0
- data/lib/contracts-ruby3/spec/override_validators_spec.rb +162 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
- data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
- data/lib/contracts-ruby3/spec/spec_helper.rb +102 -0
- data/lib/contracts-ruby3/spec/support.rb +10 -0
- data/lib/contracts-ruby3/spec/support_spec.rb +21 -0
- data/lib/contracts-ruby3/spec/validators_spec.rb +47 -0
- data/lib/entitlements/data/groups/calculated/yaml.rb +7 -1
- data/lib/entitlements/data/people/yaml.rb +9 -1
- data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +5 -1
- data/lib/entitlements/extras/orgchart/person_methods.rb +7 -1
- data/lib/entitlements.rb +13 -2
- data/lib/ruby_version_check.rb +17 -0
- metadata +209 -14
@@ -0,0 +1,773 @@
|
|
1
|
+
RSpec.describe "Contracts:" do
|
2
|
+
before :all do
|
3
|
+
@o = GenericExample.new
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "basic" do
|
7
|
+
it "should fail for insufficient arguments" do
|
8
|
+
expect do
|
9
|
+
@o.hello
|
10
|
+
end.to raise_error ArgumentError
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should fail for insufficient contracts" do
|
14
|
+
expect { @o.bad_double(2) }.to raise_error(ContractError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "contracts for functions with no arguments" do
|
19
|
+
it "should work for functions with no args" do
|
20
|
+
expect { @o.no_args }.to_not raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should still work for old-style contracts for functions with no args" do
|
24
|
+
expect { @o.old_style_no_args }.to_not raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not work for a function with a bad contract" do
|
28
|
+
expect do
|
29
|
+
Class.new(GenericExample) do
|
30
|
+
Contract Num, Num
|
31
|
+
def no_args_bad_contract
|
32
|
+
1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end.to raise_error NameError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "pattern matching" do
|
40
|
+
let(:string_with_hello) { "Hello, world" }
|
41
|
+
let(:string_without_hello) { "Hi, world" }
|
42
|
+
let(:expected_decorated_string) { "Hello, world!" }
|
43
|
+
subject { PatternMatchingExample.new }
|
44
|
+
|
45
|
+
it "should work as expected when there is no contract violation" do
|
46
|
+
expect(
|
47
|
+
subject.process_request(PatternMatchingExample::Success.new(string_with_hello))
|
48
|
+
).to eq(PatternMatchingExample::Success.new(expected_decorated_string))
|
49
|
+
|
50
|
+
expect(
|
51
|
+
subject.process_request(PatternMatchingExample::Failure.new)
|
52
|
+
).to be_a(PatternMatchingExample::Failure)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not fall through to next pattern when there is a deep contract violation" do
|
56
|
+
expect(PatternMatchingExample::Failure).not_to receive(:is_a?)
|
57
|
+
expect do
|
58
|
+
subject.process_request(PatternMatchingExample::Success.new(string_without_hello))
|
59
|
+
end.to raise_error(ContractError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should fail when the pattern-matched method's contract fails" do
|
63
|
+
expect do
|
64
|
+
subject.process_request("bad input")
|
65
|
+
end.to raise_error(ContractError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should work for differing arities" do
|
69
|
+
expect(
|
70
|
+
subject.do_stuff(1, "abc", 2)
|
71
|
+
).to eq("bar")
|
72
|
+
|
73
|
+
expect(
|
74
|
+
subject.do_stuff(3, "def")
|
75
|
+
).to eq("foo")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "if the return contract for a pattern match fails, it should fail instead of trying the next pattern match" do
|
79
|
+
expect do
|
80
|
+
subject.double(1)
|
81
|
+
end.to raise_error(ContractError)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should fail if multiple methods are defined with the same contract (for pattern-matching)" do
|
85
|
+
expect do
|
86
|
+
Class.new(GenericExample) do
|
87
|
+
Contract Contracts::Num => Contracts::Num
|
88
|
+
def same_param_contract x
|
89
|
+
x + 2
|
90
|
+
end
|
91
|
+
|
92
|
+
Contract Contracts::Num => String
|
93
|
+
def same_param_contract x
|
94
|
+
"sdf"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end.to raise_error(ContractError)
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when failure_callback was overriden" do
|
101
|
+
before do
|
102
|
+
::Contract.override_failure_callback do |_data|
|
103
|
+
fail "contract violation"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "calls a method when first pattern matches" do
|
108
|
+
expect(
|
109
|
+
subject.process_request(PatternMatchingExample::Success.new(string_with_hello))
|
110
|
+
).to eq(PatternMatchingExample::Success.new(expected_decorated_string))
|
111
|
+
end
|
112
|
+
|
113
|
+
it "falls through to 2nd pattern when first pattern does not match" do
|
114
|
+
expect(
|
115
|
+
subject.process_request(PatternMatchingExample::Failure.new)
|
116
|
+
).to be_a(PatternMatchingExample::Failure)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "if the return contract for a pattern match fails, it should fail instead of trying the next pattern match, even with the failure callback" do
|
120
|
+
expect do
|
121
|
+
subject.double(1)
|
122
|
+
end.to raise_error(ContractError)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "uses overriden failure_callback when pattern matching fails" do
|
126
|
+
expect do
|
127
|
+
subject.process_request("hello")
|
128
|
+
end.to raise_error(RuntimeError, /contract violation/)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "usage in singleton class" do
|
134
|
+
it "should work normally when there is no contract violation" do
|
135
|
+
expect(SingletonClassExample.hoge("hoge")).to eq("superhoge")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should fail with proper error when there is contract violation" do
|
139
|
+
expect do
|
140
|
+
SingletonClassExample.hoge(3)
|
141
|
+
end.to raise_error(ContractError, /Expected: String/)
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "builtin contracts usage" do
|
145
|
+
it "allows to use builtin contracts without namespacing and redundant Contracts inclusion" do
|
146
|
+
expect do
|
147
|
+
SingletonClassExample.add("55", 5.6)
|
148
|
+
end.to raise_error(ContractError, /Expected: Num/)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "usage in the singleton class of a subclass" do
|
154
|
+
subject { SingletonInheritanceExampleSubclass }
|
155
|
+
|
156
|
+
it "should work with a valid contract on a singleton method" do
|
157
|
+
expect(subject.num(1)).to eq(1)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "no contracts feature" do
|
162
|
+
it "disables normal contract checks" do
|
163
|
+
object = NoContractsSimpleExample.new
|
164
|
+
expect { object.some_method(3) }.not_to raise_error
|
165
|
+
end
|
166
|
+
|
167
|
+
it "disables invariants" do
|
168
|
+
object = NoContractsInvariantsExample.new
|
169
|
+
object.day = 7
|
170
|
+
expect { object.next_day }.not_to raise_error
|
171
|
+
end
|
172
|
+
|
173
|
+
it "does not disable pattern matching" do
|
174
|
+
object = NoContractsPatternMatchingExample.new
|
175
|
+
|
176
|
+
expect(object.on_response(200, "hello")).to eq("hello!")
|
177
|
+
expect(object.on_response(404, "Not found")).to eq("error 404: Not found")
|
178
|
+
expect { object.on_response(nil, "junk response") }.to raise_error(ContractError)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "module usage" do
|
183
|
+
context "with instance methods" do
|
184
|
+
it "should check contract" do
|
185
|
+
expect { KlassWithModuleExample.new.plus(3, nil) }.to raise_error(ContractError)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "with singleton methods" do
|
190
|
+
it "should check contract" do
|
191
|
+
expect { ModuleExample.hoge(nil) }.to raise_error(ContractError)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "with singleton class methods" do
|
196
|
+
it "should check contract" do
|
197
|
+
expect { ModuleExample.eat(:food) }.to raise_error(ContractError)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "singleton methods self in inherited methods" do
|
203
|
+
it "should be a proper self" do
|
204
|
+
expect(SingletonInheritanceExampleSubclass.a_contracted_self).to eq(SingletonInheritanceExampleSubclass)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "anonymous classes" do
|
209
|
+
let(:klass) do
|
210
|
+
Class.new do
|
211
|
+
include Contracts::Core
|
212
|
+
|
213
|
+
Contract String => String
|
214
|
+
def greeting(name)
|
215
|
+
"hello, #{name}"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
let(:obj) { klass.new }
|
221
|
+
|
222
|
+
it "does not fail when contract is satisfied" do
|
223
|
+
expect(obj.greeting("world")).to eq("hello, world")
|
224
|
+
end
|
225
|
+
|
226
|
+
it "fails with error when contract is violated" do
|
227
|
+
expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "anonymous modules" do
|
232
|
+
let(:mod) do
|
233
|
+
Module.new do
|
234
|
+
include Contracts::Core
|
235
|
+
|
236
|
+
Contract String => String
|
237
|
+
def greeting(name)
|
238
|
+
"hello, #{name}"
|
239
|
+
end
|
240
|
+
|
241
|
+
Contract String => String
|
242
|
+
def self.greeting(name)
|
243
|
+
"hello, #{name}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
let(:klass) do
|
249
|
+
Class.new.tap { |klass| klass.send(:include, mod) }
|
250
|
+
end
|
251
|
+
|
252
|
+
let(:obj) { klass.new }
|
253
|
+
|
254
|
+
it "does not fail when contract is satisfied" do
|
255
|
+
expect(obj.greeting("world")).to eq("hello, world")
|
256
|
+
end
|
257
|
+
|
258
|
+
it "fails with error when contract is violated" do
|
259
|
+
expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/)
|
260
|
+
end
|
261
|
+
|
262
|
+
context "when called on module itself" do
|
263
|
+
let(:obj) { mod }
|
264
|
+
|
265
|
+
it "does not fail when contract is satisfied" do
|
266
|
+
expect(obj.greeting("world")).to eq("hello, world")
|
267
|
+
end
|
268
|
+
|
269
|
+
it "fails with error when contract is violated" do
|
270
|
+
expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe "instance methods" do
|
276
|
+
it "should allow two classes to have the same method with different contracts" do
|
277
|
+
a = A.new
|
278
|
+
b = B.new
|
279
|
+
expect do
|
280
|
+
a.triple(5)
|
281
|
+
b.triple("a string")
|
282
|
+
end.to_not raise_error
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "instance and class methods" do
|
287
|
+
it "should allow a class to have an instance method and a class method with the same name" do
|
288
|
+
a = A.new
|
289
|
+
expect do
|
290
|
+
a.instance_and_class_method(5)
|
291
|
+
A.instance_and_class_method("a string")
|
292
|
+
end.to_not raise_error
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "class methods" do
|
297
|
+
it "should pass for correct input" do
|
298
|
+
expect { GenericExample.a_class_method(2) }.to_not raise_error
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should fail for incorrect input" do
|
302
|
+
expect { GenericExample.a_class_method("bad") }.to raise_error(ContractError)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "classes" do
|
307
|
+
it "should pass for correct input" do
|
308
|
+
expect { @o.hello("calvin") }.to_not raise_error
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should fail for incorrect input" do
|
312
|
+
expect { @o.hello(1) }.to raise_error(ContractError)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe "classes with a valid? class method" do
|
317
|
+
it "should pass for correct input" do
|
318
|
+
expect { @o.double(2) }.to_not raise_error
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should fail for incorrect input" do
|
322
|
+
expect { @o.double("bad") }.to raise_error(ContractError)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe "Procs" do
|
327
|
+
it "should pass for correct input" do
|
328
|
+
expect { @o.square(2) }.to_not raise_error
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should fail for incorrect input" do
|
332
|
+
expect { @o.square("bad") }.to raise_error(ContractError)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe "Arrays" do
|
337
|
+
it "should pass for correct input" do
|
338
|
+
expect { @o.sum_three([1, 2, 3]) }.to_not raise_error
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should fail for insufficient items" do
|
342
|
+
expect { @o.square([1, 2]) }.to raise_error(ContractError)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should fail for some incorrect elements" do
|
346
|
+
expect { @o.sum_three([1, 2, "three"]) }.to raise_error(ContractError)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe "Hashes" do
|
351
|
+
it "should pass for exact correct input" do
|
352
|
+
expect { @o.person({ :name => "calvin", :age => 10 }) }.to_not raise_error
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should pass even if some keys don't have contracts" do
|
356
|
+
expect { @o.person({ :name => "calvin", :age => 10, :foo => "bar" }) }.to_not raise_error
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should fail if a key with a contract on it isn't provided" do
|
360
|
+
expect { @o.person({ :name => "calvin" }) }.to raise_error(ContractError)
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should fail for incorrect input" do
|
364
|
+
expect { @o.person({ :name => 50, :age => 10 }) }.to raise_error(ContractError)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
describe "blocks" do
|
369
|
+
it "should pass for correct input" do
|
370
|
+
expect do
|
371
|
+
@o.do_call do
|
372
|
+
2 + 2
|
373
|
+
end
|
374
|
+
end.to_not raise_error
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should fail for incorrect input" do
|
378
|
+
expect do
|
379
|
+
@o.do_call(nil)
|
380
|
+
end.to raise_error(ContractError)
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should handle properly lack of block when there are other arguments" do
|
384
|
+
expect do
|
385
|
+
@o.double_with_proc(4)
|
386
|
+
end.to raise_error(ContractError, /Actual: nil/)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should succeed for maybe proc with no proc" do
|
390
|
+
expect do
|
391
|
+
@o.maybe_call(5)
|
392
|
+
end.to_not raise_error
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should succeed for maybe proc with proc" do
|
396
|
+
expect do
|
397
|
+
@o.maybe_call(5) do
|
398
|
+
2 + 2
|
399
|
+
end
|
400
|
+
end.to_not raise_error
|
401
|
+
end
|
402
|
+
|
403
|
+
it "should fail for maybe proc with invalid input" do
|
404
|
+
expect do
|
405
|
+
@o.maybe_call("bad")
|
406
|
+
end.to raise_error(ContractError)
|
407
|
+
end
|
408
|
+
|
409
|
+
describe "varargs are given with a maybe block" do
|
410
|
+
it "when a block is passed in, varargs should be correct" do
|
411
|
+
expect(@o.maybe_call(1, 2, 3) { 1 + 1 }).to eq([1, 2, 3])
|
412
|
+
end
|
413
|
+
|
414
|
+
it "when a block is NOT passed in, varargs should still be correct" do
|
415
|
+
expect(@o.maybe_call(1, 2, 3)).to eq([1, 2, 3])
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
describe "varargs" do
|
421
|
+
it "should pass for correct input" do
|
422
|
+
expect do
|
423
|
+
@o.sum(1, 2, 3)
|
424
|
+
end.to_not raise_error
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should fail for incorrect input" do
|
428
|
+
expect do
|
429
|
+
@o.sum(1, 2, "bad")
|
430
|
+
end.to raise_error(ContractError)
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should work with arg before splat" do
|
434
|
+
expect do
|
435
|
+
@o.arg_then_splat(3, "hello", "world")
|
436
|
+
end.to_not raise_error
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
describe "varargs with block" do
|
441
|
+
it "should pass for correct input" do
|
442
|
+
expect do
|
443
|
+
@o.with_partial_sums(1, 2, 3) do |partial_sum|
|
444
|
+
2 * partial_sum + 1
|
445
|
+
end
|
446
|
+
end.not_to raise_error
|
447
|
+
expect do
|
448
|
+
@o.with_partial_sums_contracted(1, 2, 3) do |partial_sum|
|
449
|
+
2 * partial_sum + 1
|
450
|
+
end
|
451
|
+
end.not_to raise_error
|
452
|
+
end
|
453
|
+
|
454
|
+
it "should fail for incorrect input" do
|
455
|
+
expect do
|
456
|
+
@o.with_partial_sums(1, 2, "bad") do |partial_sum|
|
457
|
+
2 * partial_sum + 1
|
458
|
+
end
|
459
|
+
end.to raise_error(ContractError, /Actual: "bad"/)
|
460
|
+
|
461
|
+
expect do
|
462
|
+
@o.with_partial_sums(1, 2, 3)
|
463
|
+
end.to raise_error(ContractError, /Actual: nil/)
|
464
|
+
|
465
|
+
expect do
|
466
|
+
@o.with_partial_sums(1, 2, 3, lambda { |x| x })
|
467
|
+
end.to raise_error(ContractError, /Actual: nil/)
|
468
|
+
end
|
469
|
+
|
470
|
+
context "when block has Func contract" do
|
471
|
+
it "should fail for incorrect input" do
|
472
|
+
expect do
|
473
|
+
@o.with_partial_sums_contracted(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 }
|
474
|
+
end.to raise_error(ContractError, /Actual: "bad"/)
|
475
|
+
|
476
|
+
expect do
|
477
|
+
@o.with_partial_sums_contracted(1, 2, 3)
|
478
|
+
end.to raise_error(ContractError, /Actual: nil/)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
describe "contracts on functions" do
|
484
|
+
it "should pass for a function that passes the contract" do
|
485
|
+
expect { @o.map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error
|
486
|
+
end
|
487
|
+
|
488
|
+
it "should pass for a function that passes the contract as in tutorial" do
|
489
|
+
expect { @o.tutorial_map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error
|
490
|
+
end
|
491
|
+
|
492
|
+
it "should fail for a function that doesn't pass the contract" do
|
493
|
+
expect { @o.map([1, 2, 3], lambda { |_| "bad return value" }) }.to raise_error(ContractError)
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should pass for a function that passes the contract with weak other args" do
|
497
|
+
expect { @o.map_plain(["hello", "joe"], lambda { |x| x.size }) }.to_not raise_error
|
498
|
+
end
|
499
|
+
|
500
|
+
it "should fail for a function that doesn't pass the contract with weak other args" do
|
501
|
+
expect { @o.map_plain(["hello", "joe"], lambda { |_| nil }) }.to raise_error(ContractError)
|
502
|
+
end
|
503
|
+
|
504
|
+
it "should fail for a returned function that doesn't pass the contract" do
|
505
|
+
expect { @o.lambda_with_wrong_return.call("hello") }.to raise_error(ContractError)
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should fail for a returned function that receives the wrong argument type" do
|
509
|
+
expect { @o.lambda_with_correct_return.call(123) }.to raise_error(ContractError)
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should not fail for a returned function that passes the contract" do
|
513
|
+
expect { @o.lambda_with_correct_return.call("hello") }.to_not raise_error
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
describe "default args to functions" do
|
518
|
+
it "should work for a function call that relies on default args" do
|
519
|
+
expect { @o.default_args }.to_not raise_error
|
520
|
+
expect { @o.default_args("foo") }.to raise_error(ContractError)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
describe "classes" do
|
525
|
+
it "should not fail for an object that is the exact type as the contract" do
|
526
|
+
p = Parent.new
|
527
|
+
expect { @o.id_(p) }.to_not raise_error
|
528
|
+
end
|
529
|
+
|
530
|
+
it "should not fail for an object that is a subclass of the type in the contract" do
|
531
|
+
c = Child.new
|
532
|
+
expect { @o.id_(c) }.to_not raise_error
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe "failure callbacks" do
|
537
|
+
before :each do
|
538
|
+
::Contract.override_failure_callback do |_data|
|
539
|
+
should_call
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
context "when failure_callback returns false" do
|
544
|
+
let(:should_call) { false }
|
545
|
+
|
546
|
+
it "does not call a function for which the contract fails" do
|
547
|
+
res = @o.double("bad")
|
548
|
+
expect(res).to eq(nil)
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
context "when failure_callback returns true" do
|
553
|
+
let(:should_call) { true }
|
554
|
+
|
555
|
+
it "calls a function for which the contract fails" do
|
556
|
+
res = @o.double("bad")
|
557
|
+
expect(res).to eq("badbad")
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
describe "module contracts" do
|
563
|
+
it "passes for instance of class including module" do
|
564
|
+
expect(
|
565
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithModule.new)
|
566
|
+
).to eq(:world)
|
567
|
+
end
|
568
|
+
|
569
|
+
it "passes for instance of class including inherited module" do
|
570
|
+
expect(
|
571
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithInheritedModule.new)
|
572
|
+
).to eq(:world)
|
573
|
+
end
|
574
|
+
|
575
|
+
it "does not pass for instance of class not including module" do
|
576
|
+
expect do
|
577
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithoutModule.new)
|
578
|
+
end.to raise_error(ContractError, /Expected: ModuleContractExample::AModule/)
|
579
|
+
end
|
580
|
+
|
581
|
+
it "does not pass for instance of class including another module" do
|
582
|
+
expect do
|
583
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithAnotherModule.new)
|
584
|
+
end.to raise_error(ContractError, /Expected: ModuleContractExample::AModule/)
|
585
|
+
end
|
586
|
+
|
587
|
+
it "passes for instance of class including both modules" do
|
588
|
+
expect(
|
589
|
+
ModuleContractExample.hello(ModuleContractExample::AClassWithBothModules.new)
|
590
|
+
).to eq(:world)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
describe "Contracts to_s formatting in expected" do
|
595
|
+
def not_s(match)
|
596
|
+
Regexp.new "[^\"\']#{match}[^\"\']"
|
597
|
+
end
|
598
|
+
|
599
|
+
def delim(match)
|
600
|
+
"(#{match})"
|
601
|
+
end
|
602
|
+
|
603
|
+
it "should not stringify native types" do
|
604
|
+
expect do
|
605
|
+
@o.constanty("bad", nil)
|
606
|
+
end.to raise_error(ContractError, not_s(123))
|
607
|
+
|
608
|
+
expect do
|
609
|
+
@o.constanty(123, "bad")
|
610
|
+
end.to raise_error(ContractError, not_s(nil))
|
611
|
+
end
|
612
|
+
|
613
|
+
it "should contain to_s representation within a Hash contract" do
|
614
|
+
expect do
|
615
|
+
@o.hash_complex_contracts({ :rigged => "bad" })
|
616
|
+
end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
|
617
|
+
end
|
618
|
+
|
619
|
+
it "should contain to_s representation within a nested Hash contract" do
|
620
|
+
expect do
|
621
|
+
@o.nested_hash_complex_contracts({
|
622
|
+
:rigged => true,
|
623
|
+
:contents => {
|
624
|
+
:kind => 0,
|
625
|
+
:total => 42,
|
626
|
+
},
|
627
|
+
})
|
628
|
+
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
|
629
|
+
end
|
630
|
+
|
631
|
+
it "should contain to_s representation within an Array contract" do
|
632
|
+
expect do
|
633
|
+
@o.array_complex_contracts(["bad"])
|
634
|
+
end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
|
635
|
+
end
|
636
|
+
|
637
|
+
it "should contain to_s representation within a nested Array contract" do
|
638
|
+
expect do
|
639
|
+
@o.nested_array_complex_contracts([true, [0]])
|
640
|
+
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
|
641
|
+
end
|
642
|
+
|
643
|
+
it "should wrap and pretty print for long param contracts" do
|
644
|
+
expect do
|
645
|
+
@o.long_array_param_contracts(true)
|
646
|
+
end.to(
|
647
|
+
raise_error(
|
648
|
+
ParamContractError,
|
649
|
+
/\[\(String or Symbol\),\n \(String or Symbol\),/
|
650
|
+
)
|
651
|
+
)
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should wrap and pretty print for long return contracts" do
|
655
|
+
expect do
|
656
|
+
@o.long_array_return_contracts
|
657
|
+
end.to(
|
658
|
+
raise_error(
|
659
|
+
ReturnContractError,
|
660
|
+
/\[\(String or Symbol\),\n \(String or Symbol\),/
|
661
|
+
)
|
662
|
+
)
|
663
|
+
end
|
664
|
+
|
665
|
+
it "should not contain Contracts:: module prefix" do
|
666
|
+
expect do
|
667
|
+
@o.double("bad")
|
668
|
+
end.to raise_error(ContractError, /Expected: Num/)
|
669
|
+
end
|
670
|
+
|
671
|
+
it "should still show nils, not just blank space" do
|
672
|
+
expect do
|
673
|
+
@o.no_args("bad")
|
674
|
+
end.to raise_error(ContractError, /Expected: nil/)
|
675
|
+
end
|
676
|
+
|
677
|
+
it 'should show empty quotes as ""' do
|
678
|
+
expect do
|
679
|
+
@o.no_args("")
|
680
|
+
end.to raise_error(ContractError, /Actual: ""/)
|
681
|
+
end
|
682
|
+
|
683
|
+
it "should not use custom to_s if empty string" do
|
684
|
+
expect do
|
685
|
+
@o.using_empty_contract("bad")
|
686
|
+
end.to raise_error(ContractError, /Expected: EmptyCont/)
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
describe "functype" do
|
691
|
+
it "should correctly print out a instance method's type" do
|
692
|
+
expect(@o.functype(:double)).not_to eq("")
|
693
|
+
end
|
694
|
+
|
695
|
+
it "should correctly print out a class method's type" do
|
696
|
+
expect(A.functype(:a_class_method)).not_to eq("")
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
describe "private methods" do
|
701
|
+
it "should raise an error if you try to access a private method" do
|
702
|
+
expect { @o.a_private_method }.to raise_error(NoMethodError, /private/)
|
703
|
+
end
|
704
|
+
|
705
|
+
it "should raise an error if you try to access a private method" do
|
706
|
+
expect { @o.a_really_private_method }.to raise_error(NoMethodError, /private/)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
describe "protected methods" do
|
711
|
+
it "should raise an error if you try to access a protected method" do
|
712
|
+
expect { @o.a_protected_method }.to raise_error(NoMethodError, /protected/)
|
713
|
+
end
|
714
|
+
|
715
|
+
it "should raise an error if you try to access a protected method" do
|
716
|
+
expect { @o.a_really_protected_method }.to raise_error(NoMethodError, /protected/)
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
describe "inherited methods" do
|
721
|
+
it "should apply the contract to an inherited method" do
|
722
|
+
c = Child.new
|
723
|
+
expect { c.double(2) }.to_not raise_error
|
724
|
+
expect { c.double("asd") }.to raise_error ParamContractError
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
describe "classes with extended modules" do
|
729
|
+
let(:klass) do
|
730
|
+
m = Module.new do
|
731
|
+
include Contracts::Core
|
732
|
+
end
|
733
|
+
|
734
|
+
Class.new do
|
735
|
+
include Contracts::Core
|
736
|
+
extend m
|
737
|
+
|
738
|
+
Contract String => nil
|
739
|
+
def foo(x)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
it "is possible to define it" do
|
745
|
+
expect { klass }.not_to raise_error
|
746
|
+
end
|
747
|
+
|
748
|
+
it "works correctly with methods with passing contracts" do
|
749
|
+
expect { klass.new.foo("bar") }.not_to raise_error
|
750
|
+
end
|
751
|
+
|
752
|
+
it "works correctly with methods with passing contracts" do
|
753
|
+
expect { klass.new.foo(42) }.to raise_error(ContractError, /Expected: String/)
|
754
|
+
end
|
755
|
+
|
756
|
+
# See the discussion on this issue:
|
757
|
+
# https://github.com/egonSchiele/contracts.ruby/issues/229
|
758
|
+
it "should not fail with 'undefined method 'Contract''" do
|
759
|
+
expect do
|
760
|
+
class ModuleThenContracts
|
761
|
+
include ModuleWithContracts
|
762
|
+
include Contracts::Core
|
763
|
+
|
764
|
+
# fails on this line
|
765
|
+
Contract C::Num => C::Num
|
766
|
+
def double(x)
|
767
|
+
x * 2
|
768
|
+
end
|
769
|
+
end
|
770
|
+
end.to_not raise_error
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|