membrane 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Any do
4
+ describe "#validate" do
5
+ it "should always return nil" do
6
+ schema = Membrane::Schema::Any.new
7
+ # Smoke test more than anything. Cannot validate this with 100%
8
+ # certainty.
9
+ [1, "hi", :test, {}, []].each do |o|
10
+ schema.validate(o).should be_nil
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Bool do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Bool.new }
7
+
8
+ it "should return nil for {true, false}" do
9
+ [true, false].each { |v| schema.validate(v).should be_nil }
10
+ end
11
+
12
+ it "should return an error for values not in {true, false}" do
13
+ ["a", 1].each do |v|
14
+ expect_validation_failure(schema, v, /true or false/)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Class do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Class.new(String) }
7
+
8
+ it "should return nil for instances of the supplied class" do
9
+ schema.validate("test").should be_nil
10
+ end
11
+
12
+ it "should return nil for subclasses of the supplied class" do
13
+ class StrTest < String; end
14
+
15
+ schema.validate(StrTest.new("hi")).should be_nil
16
+ end
17
+
18
+ it "should return an error for non class instances" do
19
+ expect_validation_failure(schema, 10, /instance of String/)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane do
4
+ let(:schema) do
5
+ Membrane::SchemaParser.parse do
6
+ { "ints" => [Integer],
7
+ "tf" => bool,
8
+ "any" => any,
9
+ "1_or_2" => enum(1, 2),
10
+ "str_to_str_to_int" => dict(String, dict(String, Integer)),
11
+ optional("optional") => bool,
12
+ }
13
+ end
14
+ end
15
+
16
+ let(:valid) do
17
+ { "ints" => [1, 2],
18
+ "tf" => false,
19
+ "any" => nil,
20
+ "1_or_2" => 2,
21
+ "optional" => true,
22
+ "str_to_str_to_int" => { "ten" => { "twenty" => 20 } },
23
+ }
24
+ end
25
+
26
+ it "should work with complex nested schemas" do
27
+ schema.validate(valid).should be_nil
28
+ end
29
+
30
+ it "should complain about missing keys" do
31
+ required_keys = schema.schemas.keys.dup
32
+ required_keys.delete("optional")
33
+
34
+ required_keys.each do |k|
35
+ invalid = valid.dup
36
+
37
+ invalid.delete(k)
38
+
39
+ expect_validation_failure(schema, invalid, /#{k} => Missing key/)
40
+ end
41
+ end
42
+
43
+ it "should validate nested maps" do
44
+ invalid = valid.dup
45
+
46
+ invalid["str_to_str_to_int"]["ten"]["twenty"] = "invalid"
47
+
48
+ expect_validation_failure(schema, invalid, /twenty => Expected/)
49
+ end
50
+ end
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Dictionary do
4
+ describe "#validate" do
5
+ let (:data) { { "foo" => 1, "bar" => 2 } }
6
+
7
+ it "should return an error if supplied with a non-hash" do
8
+ schema = Membrane::Schema::Dictionary.new(nil, nil)
9
+
10
+ expect_validation_failure(schema, "test", /instance of Hash/)
11
+ end
12
+
13
+ it "should validate each key against the supplied key schema" do
14
+ key_schema = mock("key_schema")
15
+
16
+ data.keys.each { |k| key_schema.should_receive(:validate).with(k) }
17
+
18
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema,
19
+ Membrane::Schema::Any.new)
20
+
21
+ dict_schema.validate(data)
22
+ end
23
+
24
+ it "should validate the value for each valid key" do
25
+ key_schema = Membrane::Schema::Class.new(String)
26
+ val_schema = mock("val_schema")
27
+
28
+ data.values.each { |v| val_schema.should_receive(:validate).with(v) }
29
+
30
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
31
+
32
+ dict_schema.validate(data)
33
+ end
34
+
35
+ it "should return any errors for keys or values that didn't validate" do
36
+ bad_data = {
37
+ "foo" => "bar",
38
+ :bar => 2,
39
+ }
40
+
41
+ key_schema = Membrane::Schema::Class.new(String)
42
+ val_schema = Membrane::Schema::Class.new(Integer)
43
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
44
+
45
+ errors = nil
46
+
47
+ begin
48
+ dict_schema.validate(bad_data)
49
+ rescue Membrane::SchemaValidationError => e
50
+ errors = e.to_s
51
+ end
52
+
53
+ errors.should match(/foo/)
54
+ errors.should match(/bar/)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Enum do
4
+ describe "#validate" do
5
+ let (:int_schema) { Membrane::Schema::Class.new(Integer) }
6
+ let (:str_schema) { Membrane::Schema::Class.new(String) }
7
+ let (:enum_schema) { Membrane::Schema::Enum.new(int_schema, str_schema) }
8
+
9
+ it "should return an error if none of the schemas validate" do
10
+ expect_validation_failure(enum_schema, :sym, /doesn't validate/)
11
+ end
12
+
13
+ it "should return nil if any of the schemas validate" do
14
+ enum_schema.validate("foo").should be_nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::List do
4
+ describe "#validate" do
5
+ it "should return an error if the validated object isn't an array" do
6
+ schema = Membrane::Schema::List.new(nil)
7
+
8
+ expect_validation_failure(schema, "hi", /instance of Array/)
9
+ end
10
+
11
+ it "should invoke validate each list item against the supplied schema" do
12
+ item_schema = mock("item_schema")
13
+
14
+ data = [0, 1, 2]
15
+
16
+ data.each { |x| item_schema.should_receive(:validate).with(x) }
17
+
18
+ list_schema = Membrane::Schema::List.new(item_schema)
19
+
20
+ list_schema.validate(data)
21
+ end
22
+ end
23
+
24
+ it "should return an error if any items fail to validate" do
25
+ item_schema = Membrane::Schema::Class.new(Integer)
26
+ list_schema = Membrane::Schema::List.new(item_schema)
27
+
28
+ errors = nil
29
+
30
+ begin
31
+ list_schema.validate([1, 2, "hi", 3, :there])
32
+ rescue Membrane::SchemaValidationError => e
33
+ errors = e.to_s
34
+ end
35
+
36
+ errors.should match(/index 2/)
37
+ errors.should match(/index 4/)
38
+ end
39
+
40
+ it "should return nil if all items validate" do
41
+ item_schema = Membrane::Schema::Class.new(Integer)
42
+ list_schema = Membrane::Schema::List.new(item_schema)
43
+
44
+ list_schema.validate([1, 2, 3]).should be_nil
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Record do
4
+ describe "#validate" do
5
+ it "should return an error if the validated object isn't a hash" do
6
+ schema = Membrane::Schema::Record.new(nil)
7
+
8
+ expect_validation_failure(schema, "test", /instance of Hash/)
9
+ end
10
+
11
+ it "should return an error for missing keys" do
12
+ key_schemas = { "foo" => Membrane::Schema::ANY }
13
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
14
+
15
+ expect_validation_failure(rec_schema, {}, /foo => Missing/)
16
+ end
17
+
18
+ it "should validate the value for each key" do
19
+ data = {
20
+ "foo" => 1,
21
+ "bar" => 2,
22
+ }
23
+
24
+ key_schemas = {
25
+ "foo" => mock("foo"),
26
+ "bar" => mock("bar"),
27
+ }
28
+
29
+ key_schemas.each { |k, m| m.should_receive(:validate).with(data[k]) }
30
+
31
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
32
+
33
+ rec_schema.validate(data)
34
+ end
35
+
36
+ it "should return all errors for keys or values that didn't validate" do
37
+ key_schemas = {
38
+ "foo" => Membrane::Schema::ANY,
39
+ "bar" => Membrane::Schema::Class.new(String),
40
+ }
41
+
42
+ rec_schema = Membrane::Schema::Record.new(key_schemas)
43
+
44
+ errors = nil
45
+
46
+ begin
47
+ rec_schema.validate({ "bar" => 2 })
48
+ rescue Membrane::SchemaValidationError => e
49
+ errors = e.to_s
50
+ end
51
+
52
+ errors.should match(/foo => Missing key/)
53
+ errors.should match(/bar/)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Regexp do
4
+ let(:schema) { Membrane::Schema::Regexp.new(/bar/) }
5
+
6
+ describe "#validate" do
7
+ it "should raise an error if the validated object isn't a string" do
8
+ expect_validation_failure(schema, 5, /instance of String/)
9
+ end
10
+
11
+ it "should raise an error if the validated object doesn't match" do
12
+ expect_validation_failure(schema, "invalid", /match regex/)
13
+ end
14
+
15
+ it "should return nil if the validated object matches" do
16
+ schema.validate("barbar").should be_nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,249 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::SchemaParser do
4
+ let(:parser) { Membrane::SchemaParser.new }
5
+
6
+ describe "#deparse" do
7
+ it "should call inspect on the value of a Value schema" do
8
+ val = "test"
9
+ val.should_receive(:inspect).twice
10
+ schema = Membrane::Schema::Value.new(val)
11
+
12
+ parser.deparse(schema).should == val.inspect
13
+ end
14
+
15
+ it "should return 'any' for instance of Membrane::Schema::Any" do
16
+ schema = Membrane::Schema::Any.new
17
+
18
+ parser.deparse(schema).should == "any"
19
+ end
20
+
21
+ it "should return 'bool' for instances of Membrane::Schema::Bool" do
22
+ schema = Membrane::Schema::Bool.new
23
+
24
+ parser.deparse(schema).should == "bool"
25
+ end
26
+
27
+ it "should call inspect on the class of a Membrane::Schema::Class schema" do
28
+ klass = String
29
+ klass.should_receive(:inspect).twice
30
+ schema = Membrane::Schema::Class.new(klass)
31
+
32
+ parser.deparse(schema).should == klass.inspect
33
+ end
34
+
35
+ it "should deparse the k/v schemas of a Membrane::Schema::Dictionary schema" do
36
+ key_schema = Membrane::Schema::Class.new(String)
37
+ val_schema = Membrane::Schema::Class.new(Integer)
38
+
39
+ dict_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
40
+
41
+ parser.deparse(dict_schema).should == "dict(String, Integer)"
42
+ end
43
+
44
+ it "should deparse the element schemas of a Membrane::Schema::Enum schema" do
45
+ schemas =
46
+ [String, Integer, Float].map { |c| Membrane::Schema::Class.new(c) }
47
+
48
+ enum_schema = Membrane::Schema::Enum.new(*schemas)
49
+
50
+ parser.deparse(enum_schema).should == "enum(String, Integer, Float)"
51
+ end
52
+
53
+ it "should deparse the element schema of a Membrane::Schema::List schema" do
54
+ key_schema = Membrane::Schema::Class.new(String)
55
+ val_schema = Membrane::Schema::Class.new(Integer)
56
+ item_schema = Membrane::Schema::Dictionary.new(key_schema, val_schema)
57
+
58
+ list_schema = Membrane::Schema::List.new(item_schema)
59
+
60
+ parser.deparse(list_schema).should == "[dict(String, Integer)]"
61
+ end
62
+
63
+ it "should deparse elem schemas of a Membrane::Schema::Record schema" do
64
+ str_schema = Membrane::Schema::Class.new(String)
65
+ int_schema = Membrane::Schema::Class.new(Integer)
66
+ dict_schema = Membrane::Schema::Dictionary.new(str_schema, int_schema)
67
+
68
+ int_rec_schema = Membrane::Schema::Record.new({:str => str_schema,
69
+ :dict => dict_schema})
70
+
71
+ rec_schema = Membrane::Schema::Record.new({"str" => str_schema,
72
+ "rec" => int_rec_schema,
73
+ "int" => int_schema})
74
+ exp_deparse =<<EOT
75
+ {
76
+ "str" => String,
77
+ "rec" => {
78
+ :str => String,
79
+ :dict => dict(String, Integer),
80
+ },
81
+ "int" => Integer,
82
+ }
83
+ EOT
84
+ parser.deparse(rec_schema).should == exp_deparse.strip
85
+ end
86
+
87
+ it "should call inspect on regexps for Membrane::Schema::Regexp" do
88
+ schema = Membrane::Schema::Regexp.new(/test/)
89
+ schema.regexp.should_receive(:inspect)
90
+ parser.deparse(schema)
91
+ end
92
+
93
+ it "should deparse the element schemas of a Membrane::Schema::Tuple schema" do
94
+ schemas = [String, Integer].map { |c| Membrane::Schema::Class.new(c) }
95
+ schemas << Membrane::Schema::Value.new("test")
96
+
97
+ enum_schema = Membrane::Schema::Tuple.new(*schemas)
98
+
99
+ parser.deparse(enum_schema).should == 'tuple(String, Integer, "test")'
100
+ end
101
+
102
+ it "should raise an error if given a non-schema" do
103
+ expect do
104
+ parser.deparse({})
105
+ end.to raise_error(ArgumentError, /Expected instance/)
106
+ end
107
+ end
108
+
109
+ describe "#parse" do
110
+ it "should leave instances derived from Membrane::Schema::Base unchanged" do
111
+ old_schema = Membrane::Schema::ANY
112
+
113
+ parser.parse { old_schema }.should == old_schema
114
+ end
115
+
116
+ it "should translate 'any' into Membrane::Schema::Any" do
117
+ schema = parser.parse { any }
118
+
119
+ schema.class.should == Membrane::Schema::Any
120
+ end
121
+
122
+ it "should translate 'bool' into Membrane::Schema::Bool" do
123
+ schema = parser.parse { bool }
124
+
125
+ schema.class.should == Membrane::Schema::Bool
126
+ end
127
+
128
+ it "should translate 'enum' into Membrane::Schema::Enum" do
129
+ schema = parser.parse { enum(bool, any) }
130
+
131
+ schema.class.should == Membrane::Schema::Enum
132
+
133
+ schema.elem_schemas.length.should == 2
134
+
135
+ elem_schema_classes = schema.elem_schemas.map { |es| es.class }
136
+
137
+ expected_classes = [Membrane::Schema::Bool, Membrane::Schema::Any]
138
+ elem_schema_classes.should == expected_classes
139
+ end
140
+
141
+ it "should translate 'dict' into Membrane::Schema::Dictionary" do
142
+ schema = parser.parse { dict(String, Integer) }
143
+
144
+ schema.class.should == Membrane::Schema::Dictionary
145
+
146
+ schema.key_schema.class.should == Membrane::Schema::Class
147
+ schema.key_schema.klass.should == String
148
+
149
+ schema.value_schema.class.should == Membrane::Schema::Class
150
+ schema.value_schema.klass.should == Integer
151
+ end
152
+
153
+ it "should translate 'tuple' into Membrane::Schema::Tuple" do
154
+ schema = parser.parse { tuple(String, any, Integer) }
155
+
156
+ schema.class.should == Membrane::Schema::Tuple
157
+
158
+ schema.elem_schemas[0].class.should == Membrane::Schema::Class
159
+ schema.elem_schemas[0].klass.should == String
160
+
161
+ schema.elem_schemas[1].class == Membrane::Schema::Any
162
+
163
+ schema.elem_schemas[2].class.should == Membrane::Schema::Class
164
+ schema.elem_schemas[2].klass.should == Integer
165
+ end
166
+
167
+ it "should translate classes into Membrane::Schema::Class" do
168
+ schema = parser.parse { String }
169
+
170
+ schema.class.should == Membrane::Schema::Class
171
+
172
+ schema.klass.should == String
173
+ end
174
+
175
+ it "should translate regexps into Membrane::Schema::Regexp" do
176
+ regexp = /foo/
177
+
178
+ schema = parser.parse { regexp }
179
+
180
+ schema.class.should == Membrane::Schema::Regexp
181
+
182
+ schema.regexp.should == regexp
183
+ end
184
+
185
+ it "should fall back to Membrane::Schema::Value" do
186
+ schema = parser.parse { 5 }
187
+
188
+ schema.class.should == Membrane::Schema::Value
189
+ schema.value.should == 5
190
+ end
191
+
192
+ describe "when parsing a list" do
193
+ it "should raise an error when no element schema is supplied" do
194
+ expect do
195
+ parser.parse { [] }
196
+ end.to raise_error(ArgumentError, /must supply/)
197
+ end
198
+
199
+ it "should raise an error when supplied > 1 element schema" do
200
+ expect do
201
+ parser.parse { [String, String] }
202
+ end.to raise_error(ArgumentError, /single schema/)
203
+ end
204
+
205
+ it "should parse '[<expr>]' into Membrane::Schema::List" do
206
+ schema = parser.parse { [String] }
207
+
208
+ schema.class.should == Membrane::Schema::List
209
+
210
+ schema.elem_schema.class.should == Membrane::Schema::Class
211
+ schema.elem_schema.klass.should == String
212
+ end
213
+ end
214
+
215
+ describe "when parsing a record" do
216
+ it "should raise an error if the record is empty" do
217
+ expect do
218
+ parser.parse { {} }
219
+ end.to raise_error(ArgumentError, /must supply/)
220
+ end
221
+
222
+ it "should parse '{ <key> => <schema> }' into Membrane::Schema::Record" do
223
+ schema = parser.parse do
224
+ { "string" => String,
225
+ "ints" => [Integer],
226
+ }
227
+ end
228
+
229
+ schema.class.should == Membrane::Schema::Record
230
+
231
+ str_schema = schema.schemas["string"]
232
+ str_schema.class.should == Membrane::Schema::Class
233
+ str_schema.klass.should == String
234
+
235
+ ints_schema = schema.schemas["ints"]
236
+ ints_schema.class.should == Membrane::Schema::List
237
+ ints_schema.elem_schema.class.should == Membrane::Schema::Class
238
+ ints_schema.elem_schema.klass.should == Integer
239
+ end
240
+
241
+ it "should handle keys marked with 'optional()'" do
242
+ schema = parser.parse { { optional("test") => Integer } }
243
+
244
+ schema.class.should == Membrane::Schema::Record
245
+ schema.optional_keys.to_a.should == ["test"]
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,7 @@
1
+ require "membrane"
2
+
3
+ def expect_validation_failure(schema, object, regex)
4
+ expect do
5
+ schema.validate(object)
6
+ end.to raise_error(Membrane::SchemaValidationError, regex)
7
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe Membrane::Schema::Tuple do
4
+ let(:schema) do
5
+ Membrane::Schema::Tuple.new(Membrane::Schema::Class.new(String),
6
+ Membrane::Schema::ANY,
7
+ Membrane::Schema::Class.new(Integer))
8
+ end
9
+
10
+ describe "#validate" do
11
+ it "should raise an error if the validated object isn't an array" do
12
+ expect_validation_failure(schema, {}, /Array/)
13
+ end
14
+
15
+ it "should raise an error if the validated object has too many/few items" do
16
+ expect_validation_failure(schema, ["foo", 2], /element/)
17
+ expect_validation_failure(schema, ["foo", 2, "bar", 3], /element/)
18
+ end
19
+
20
+ it "should raise an error if any of the items do not validate" do
21
+ expect_validation_failure(schema, [5, 2, 0], /0 =>/)
22
+ expect_validation_failure(schema, ["foo", 2, "foo"], /2 =>/)
23
+ end
24
+
25
+ it "should return nil when validation succeeds" do
26
+ schema.validate(["foo", "bar", 5]).should be_nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Membrane::Schema::Value do
5
+ describe "#validate" do
6
+ let(:schema) { Membrane::Schema::Value.new("test") }
7
+
8
+ it "should return nil for values that are equal" do
9
+ schema.validate("test").should be_nil
10
+ end
11
+
12
+ it "should return an error for values that are not equal" do
13
+ expect_validation_failure(schema, "tast", /Expected test/)
14
+ end
15
+ end
16
+ end