schemata-router 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ def set_current_version(msg_type, version)
2
+ msg_type.stub(:current_version).and_return(version)
3
+ @curr_class = msg_type.current_class
4
+
5
+ @future_versions = {}
6
+ msg_type::constants.each do |v|
7
+ next if !is_message_version?(v) || !greater_version?(v, version)
8
+ @future_versions[v] = msg_type::const_get(v)
9
+ msg_type.send(:remove_const, v)
10
+ end
11
+ end
12
+
13
+ def reset_version(msg_type)
14
+ @future_versions.each do |v, klass|
15
+ msg_type::const_set(v, klass)
16
+ end
17
+ @future_versions
18
+ end
19
+
20
+ def is_message_version?(constant)
21
+ !!(constant =~ /^V[0-9]+$/)
22
+ end
23
+
24
+ def greater_version?(version, benchmark)
25
+ version = version[1..-1].to_i
26
+ version > benchmark
27
+ end
28
+
29
+ def decamelize(str)
30
+ words = []
31
+ curr_word = ""
32
+ 0.upto(str.length - 1) do |i|
33
+ ch = str[i]
34
+ if ch =~ /[A-Z]/
35
+ words.push(curr_word)
36
+ curr_word = ""
37
+ end
38
+ curr_word += ch
39
+ end
40
+ words.push(curr_word)
41
+ words.map! { |x| x.downcase }
42
+
43
+ # If the first letter is capitalized, then the first word here is empty
44
+ words.shift if words[0] == ""
45
+
46
+ words.join('_')
47
+ end
48
+
49
+ def num_mandatory_fields(msg_obj)
50
+ optional = msg_obj.class.schema.optional_keys
51
+ mandatory = Set.new(msg_obj.class.schema.schemas.keys)
52
+ diff = mandatory - optional
53
+ return diff.size
54
+ end
@@ -0,0 +1,138 @@
1
+ shared_examples "a message" do
2
+
3
+ version = described_class.version
4
+
5
+ let(:component_name) { described_class.name.split("::")[1] }
6
+ let(:message_type_name) { described_class.name.split("::")[2] }
7
+ let(:message_name) { described_class.name.split("::")[3] }
8
+
9
+ let(:component) { Schemata::const_get(component_name) }
10
+ let(:message_type) { component::const_get(message_type_name) }
11
+ let(:message) { described_class }
12
+
13
+ let(:mock_method) { "mock_#{decamelize(message_type_name)}" }
14
+
15
+ before :each do
16
+ set_current_version(message_type, version)
17
+ end
18
+
19
+ after :each do
20
+ reset_version(message_type)
21
+ end
22
+
23
+ describe "#new" do
24
+ it "should create a #{described_class} object with an incomplete hash" do
25
+ mock_hash = component.send(mock_method, 1).contents
26
+ first_key = mock_hash.keys[0]
27
+ first_value = mock_hash[first_key]
28
+
29
+ msg_obj = message.new({first_key => first_value})
30
+ msg_obj.class.should == message
31
+ msg_obj.send(first_key).should == first_value
32
+ end
33
+
34
+ it "should raise an error if the hash contains incorrect types" do
35
+ mock_hash = component.send(mock_method, 1).contents
36
+ first_key = mock_hash.keys[0]
37
+ first_value = mock_hash[first_key]
38
+ if first_value.kind_of? Integer
39
+ bad_value = "foo"
40
+ else
41
+ bad_value = 1
42
+ end
43
+
44
+ expect {
45
+ msg_obj = message.new({first_key => bad_value})
46
+ }.to raise_error(Schemata::UpdateAttributeError)
47
+ end
48
+ end
49
+
50
+ describe "#encode" do
51
+ if version == 1
52
+ it "should return a Schemata-encoded json string, with a V1 hash also in the raw payload" do
53
+ msg_obj = component.send(mock_method, 1)
54
+ json = msg_obj.encode
55
+ json_hash = Yajl::Parser.parse(json)
56
+
57
+ json_hash.should have_key "V1"
58
+ json_hash.should have_key "min_version"
59
+
60
+ data = Schemata::HashCopyHelpers.deep_copy(json_hash["V1"])
61
+
62
+ json_hash.delete("V1")
63
+ json_hash.delete("min_version")
64
+
65
+ json_hash.should == data
66
+ end
67
+ else
68
+ it "should return a Schemata-encoded json string, with no raw payload" do
69
+ msg_obj = component.send(mock_method, version)
70
+ json = msg_obj.encode
71
+ json_hash = Yajl::Parser.parse(json)
72
+
73
+ 1.upto(version) do |i|
74
+ json_hash.should have_key "V#{i}"
75
+ end
76
+ json_hash.should have_key "min_version"
77
+
78
+ data = Schemata::HashCopyHelpers.deep_copy(json_hash["V#{version}"])
79
+ 1.upto(version) do |i|
80
+ json_hash.delete("V#{i}")
81
+ end
82
+ json_hash.delete("min_version")
83
+
84
+ json_hash.should be_empty
85
+ end
86
+ end
87
+ end
88
+
89
+ described_class.schema.schemas.keys.each do |attr|
90
+ describe "##{attr}" do
91
+ it "should the attribute if it was specified at instantiation" do
92
+ mock_value = component.send(mock_method, version).contents[attr]
93
+ msg_obj = message.new({ attr => mock_value })
94
+ msg_obj.send(attr).should == mock_value
95
+ end
96
+
97
+ it "should return the attribute if it was set with an attr writer" do
98
+ mock_value = component.send(mock_method, version).contents[attr]
99
+ msg_obj = message.new
100
+ msg_obj.send("#{attr}=", mock_value)
101
+ msg_obj.send(attr).should == mock_value
102
+ end
103
+
104
+ it "should return nil if the attribute was never set" do
105
+ msg_obj = message.new
106
+ msg_obj.send(attr).should be_nil
107
+ end
108
+ end
109
+
110
+ describe "##{attr}=" do
111
+ it "should change the attribute and return the new value" do
112
+ mock_value = component.send(mock_method, version).contents[attr]
113
+ msg_obj = message.new({ attr => mock_value })
114
+ ret = (msg_obj.send("#{attr}=", mock_value))
115
+ msg_obj.send(attr).should == mock_value
116
+ ret.should == mock_value
117
+ end
118
+
119
+ unless described_class.schema.schemas[attr].kind_of? Membrane::Schema::Any
120
+ it "should raise an error if the wrong type is written" do
121
+ mock_value = component.send(mock_method, version).contents[attr]
122
+
123
+ if mock_value.kind_of? Integer
124
+ bad_value = "foo"
125
+ else
126
+ bad_value = 1
127
+ end
128
+
129
+ msg_obj = message.new
130
+
131
+ expect {
132
+ msg_obj.send("#{attr}=", bad_value)
133
+ }.to raise_error(Schemata::UpdateAttributeError)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,171 @@
1
+ shared_examples "a message type" do
2
+
3
+ versions = described_class.constants.select {|x| x =~ /V[0-9]+/ }
4
+ .map {|x| x.to_s[1..-1].to_i}
5
+
6
+ let(:message_type_name) { described_class.name.split("::")[2] }
7
+ let(:message_type) { described_class }
8
+ let(:component_name) { described_class.name.split("::")[1] }
9
+ let(:component) { Schemata::const_get(component_name) }
10
+
11
+ let(:mock_method) { "mock_#{decamelize(message_type_name)}" }
12
+
13
+ describe ".decode" do
14
+ context "when the current version is 1" do
15
+ before :each do
16
+ set_current_version(message_type, 1)
17
+ end
18
+
19
+ after :each do
20
+ reset_version(message_type)
21
+ end
22
+
23
+ it "should return a V1 object given a flat hash" do
24
+ data = component.send(mock_method, 1).contents
25
+ json = Yajl::Encoder.encode(data)
26
+ msg_obj = message_type.decode(json)
27
+ msg_obj.class.should == message_type::V1
28
+
29
+ message_type::V1.schema.schemas.keys do |key|
30
+ msg_obj.send(key).should == data[key]
31
+ end
32
+ end
33
+
34
+ it "should return a V1 object given a V1-encoded json" do
35
+ data = component.send(mock_method, 1).contents
36
+
37
+ msg_hash = {"V1" => data, "min_version" => 1}
38
+ json = Yajl::Encoder.encode(msg_hash)
39
+
40
+ msg_obj = message_type.decode(json)
41
+ msg_obj.class.should == message_type::V1
42
+
43
+ message_type::V1.schema.schemas.keys do |key|
44
+ msg_obj.send(key).should == data[key]
45
+ end
46
+ end
47
+
48
+ it "should return a V1 object given a mixed (V1-encoded + flat hash) json" do
49
+ data = component.send(mock_method, 1).contents
50
+ msg_hash = {
51
+ "V1" => data,
52
+ "min_version" => 1
53
+ }.merge(Schemata::HashCopyHelpers.deep_copy(data))
54
+ json = Yajl::Encoder.encode(msg_hash)
55
+
56
+ msg_obj = message_type.decode(json)
57
+ msg_obj.class.should == message_type::V1
58
+
59
+ message_type::V1.schema.schemas.keys do |key|
60
+ msg_obj.send(key).should == data[key]
61
+ end
62
+ end
63
+
64
+ it "should return a V1 object when given a V2-encoded json" do
65
+ v2_hash = component.send(mock_method, 1).contents
66
+ msg_hash = {
67
+ "V2" => v2_hash,
68
+ "V1" => {},
69
+ "min_version" => 1
70
+ }
71
+ json = Yajl::Encoder.encode(msg_hash)
72
+
73
+ msg_obj = message_type.decode(json)
74
+ msg_obj.class.should == message_type::V1
75
+
76
+ message_type::V1.schema.schemas.keys do |key|
77
+ msg_obj.send(key).should == data[key]
78
+ end
79
+ end
80
+
81
+ it "should raise an error if the input does not have the valid Schemata \
82
+ structure or a complete flash hash" do
83
+ mock_obj = component.send(mock_method, 1)
84
+ mock_hash = mock_obj.contents
85
+
86
+ if num_mandatory_fields(mock_obj) > 1
87
+ first_key = mock_hash.keys[0]
88
+ first_value = mock_hash[first_key]
89
+
90
+ json = Yajl::Encoder.encode({ first_key => first_value})
91
+ else
92
+ json = Yajl::Encoder.encode({})
93
+ end
94
+ expect {
95
+ msg_obj = message_type.decode(json)
96
+ }.to raise_error(Schemata::DecodeError)
97
+ end
98
+ end
99
+ end
100
+
101
+ if versions.include?(2)
102
+ context "when current version is 2" do
103
+ before :each do
104
+ set_current_version(message_type, 2)
105
+ end
106
+
107
+ after :each do
108
+ reset_version(message_type)
109
+ end
110
+
111
+ it "should raise an error given a flat hash" do
112
+ data = component.send(mock_method, 2).contents
113
+ json = Yajl::Encoder.encode(data)
114
+ expect {
115
+ msg_obj = message_type.decode(json)
116
+ }.to raise_error(Schemata::DecodeError)
117
+ end
118
+
119
+ it "should return a V2 object a given a mixed (flat hash + V1-encoded) json" do
120
+ msg_hash = component.send(mock_method, 2).contents
121
+ v1_hash = Schemata::HashCopyHelpers.deep_copy(msg_hash)
122
+ msg_hash["V1"] = v1_hash
123
+ msg_hash["min_version"] = 1
124
+
125
+ json = Yajl::Encoder.encode(msg_hash)
126
+
127
+ msg_obj = message_type.decode(json)
128
+ msg_obj.class.should == message_type::V2
129
+
130
+ message_type::V2.schema.schemas.keys.each do |key|
131
+ msg_obj.send(key).should == v1_hash[key]
132
+ end
133
+ end
134
+
135
+ it "should return a V2 object given a V1-encoded json" do
136
+ v1_hash = component.send(mock_method, 1).contents
137
+ msg_hash = {"V1" => v1_hash, 'min_version' => 1}
138
+
139
+ json = Yajl::Encoder.encode(msg_hash)
140
+
141
+ msg_obj = message_type.decode(json)
142
+ msg_obj.class.should == message_type::V2
143
+
144
+ message_type::V2.schema.schemas.keys.each do |key|
145
+ msg_obj.send(key).should == v1_hash[key]
146
+ end
147
+ end
148
+
149
+ it "should return a V2 object given a V2-encoded object" do
150
+ data = component.send(mock_method, 2).contents
151
+ v2_hash = Schemata::HashCopyHelpers.deep_copy(data)
152
+ msg_hash = {"V2" => v2_hash, "V1" => {}, "min_version" => 1}
153
+
154
+ json = Yajl::Encoder.encode(msg_hash)
155
+
156
+ msg_obj = message_type.decode(json)
157
+ msg_obj.class.should == message_type::V2
158
+
159
+ message_type::V2.schema.schemas.keys.each do |key|
160
+ msg_obj.send(key).should == data[key]
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # The first two versions have exceptional behavior, starting from
167
+ # version 3, all versions should have the same unit tests
168
+ versions.select { |x| x > 2 }.each do |version|
169
+
170
+ end
171
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schemata-router
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - dsabeti
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: membrane
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: yajl-ruby
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ci_reporter
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: ! " Specify schema for Router messages and validate messages\n against
95
+ defined schema\n"
96
+ email:
97
+ - support@cloudfoundry.org
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - lib/schemata/router/register_request.rb
103
+ - lib/schemata/router/register_request/register_request_v1.rb
104
+ - lib/schemata/router/start_message.rb
105
+ - lib/schemata/router/version.rb
106
+ - lib/schemata/router/start_message/start_message_v1.rb
107
+ - lib/schemata/router.rb
108
+ - lib/schemata/common/msgtypebase.rb
109
+ - lib/schemata/common/parsed_msg.rb
110
+ - lib/schemata/common/msgbase.rb
111
+ - lib/schemata/common/error.rb
112
+ - lib/schemata/helpers/stringify.rb
113
+ - lib/schemata/helpers/hash_copy.rb
114
+ - spec/common/helpers_spec.rb
115
+ - spec/common/parsed_msg_spec.rb
116
+ - spec/router/register_request_spec.rb
117
+ - spec/router/router_spec.rb
118
+ - spec/router/start_message_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/component_helpers.rb
121
+ - spec/support/helpers.rb
122
+ - spec/support/message_helpers.rb
123
+ - spec/support/message_type_helpers.rb
124
+ homepage: http://www.cloudfoundry.org
125
+ licenses: []
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>'
140
+ - !ruby/object:Gem::Version
141
+ version: 1.3.1
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 1.8.24
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: validation for cloundfoundry Router messages
148
+ test_files:
149
+ - spec/common/helpers_spec.rb
150
+ - spec/common/parsed_msg_spec.rb
151
+ - spec/router/register_request_spec.rb
152
+ - spec/router/router_spec.rb
153
+ - spec/router/start_message_spec.rb
154
+ - spec/spec_helper.rb
155
+ - spec/support/component_helpers.rb
156
+ - spec/support/helpers.rb
157
+ - spec/support/message_helpers.rb
158
+ - spec/support/message_type_helpers.rb