acts_as_sdata 1.0.0

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 (79) hide show
  1. data/.gitignore +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +200 -0
  4. data/Rakefile +20 -0
  5. data/VERSION +1 -0
  6. data/config/sdata.yml +13 -0
  7. data/config/sdata.yml.example +20 -0
  8. data/config/sdata.yml.tmpl.staging +14 -0
  9. data/generators/acts_as_sdata/acts_as_sdata_generator.rb +9 -0
  10. data/generators/acts_as_sdata/templates/migration.rb +69 -0
  11. data/init.rb +36 -0
  12. data/lib/s_data/active_record_extensions/base.rb +7 -0
  13. data/lib/s_data/active_record_extensions/mixin.rb +157 -0
  14. data/lib/s_data/active_record_extensions/sdata_uuid_mixin.rb +133 -0
  15. data/lib/s_data/atom_extensions/content_mixin.rb +14 -0
  16. data/lib/s_data/atom_extensions/entry_mixin.rb +41 -0
  17. data/lib/s_data/atom_extensions/nodes/digest.rb +48 -0
  18. data/lib/s_data/atom_extensions/nodes/payload.rb +34 -0
  19. data/lib/s_data/atom_extensions/nodes/sync_state.rb +14 -0
  20. data/lib/s_data/conditions_builder.rb +59 -0
  21. data/lib/s_data/controller_mixin.rb +11 -0
  22. data/lib/s_data/controller_mixin/actions.rb +87 -0
  23. data/lib/s_data/controller_mixin/collection_scope.rb +57 -0
  24. data/lib/s_data/controller_mixin/s_data_feed.rb +87 -0
  25. data/lib/s_data/controller_mixin/s_data_instance.rb +35 -0
  26. data/lib/s_data/diagnosis/application_controller_mixin.rb +16 -0
  27. data/lib/s_data/diagnosis/diagnosis.rb +130 -0
  28. data/lib/s_data/diagnosis/diagnosis_mapper.rb +39 -0
  29. data/lib/s_data/exceptions.rb +10 -0
  30. data/lib/s_data/formatting.rb +13 -0
  31. data/lib/s_data/namespace_definitions.rb +19 -0
  32. data/lib/s_data/payload.rb +158 -0
  33. data/lib/s_data/payload_map.rb +0 -0
  34. data/lib/s_data/payload_map/payload_map.rb +136 -0
  35. data/lib/s_data/payload_map/payload_map_hash.rb +39 -0
  36. data/lib/s_data/predicate.rb +31 -0
  37. data/lib/s_data/route_mapper.rb +143 -0
  38. data/lib/s_data/router_mixin.rb +10 -0
  39. data/lib/s_data/sync/controller_mixin.rb +122 -0
  40. data/lib/s_data/sync/sdata_syncing_mixin.rb +17 -0
  41. data/lib/s_data/virtual_base.rb +114 -0
  42. data/test/functional/Rakefile +0 -0
  43. data/test/unit/active_record_mixin/active_record_mixin_spec.rb +20 -0
  44. data/test/unit/active_record_mixin/acts_as_sdata_spec.rb +41 -0
  45. data/test/unit/active_record_mixin/find_by_sdata_instance_id_spec.rb +34 -0
  46. data/test/unit/active_record_mixin/payload_spec.rb +622 -0
  47. data/test/unit/active_record_mixin/to_atom_spec.rb +85 -0
  48. data/test/unit/atom_entry_mixin/atom_entry_mixin_spec.rb +11 -0
  49. data/test/unit/atom_entry_mixin/to_attributes_spec.rb +30 -0
  50. data/test/unit/class_stubs/address.rb +19 -0
  51. data/test/unit/class_stubs/contact.rb +25 -0
  52. data/test/unit/class_stubs/customer.rb +70 -0
  53. data/test/unit/class_stubs/model_base.rb +17 -0
  54. data/test/unit/class_stubs/payload.rb +15 -0
  55. data/test/unit/class_stubs/sd_uuid.rb +28 -0
  56. data/test/unit/class_stubs/user.rb +40 -0
  57. data/test/unit/conditions_builder_spec.rb +54 -0
  58. data/test/unit/controller_mixin/acts_as_sdata_spec.rb +29 -0
  59. data/test/unit/controller_mixin/build_sdata_feed_spec.rb +50 -0
  60. data/test/unit/controller_mixin/controller_mixin_spec.rb +22 -0
  61. data/test/unit/controller_mixin/diagnosis_spec.rb +232 -0
  62. data/test/unit/controller_mixin/sdata_collection_spec.rb +78 -0
  63. data/test/unit/controller_mixin/sdata_create_instance_spec.rb +173 -0
  64. data/test/unit/controller_mixin/sdata_opensearch_and_links_spec.rb +382 -0
  65. data/test/unit/controller_mixin/sdata_scope/linked_model_spec.rb +58 -0
  66. data/test/unit/controller_mixin/sdata_scope/non_linked_model_spec.rb +66 -0
  67. data/test/unit/controller_mixin/sdata_scope/scoping_in_config_spec.rb +64 -0
  68. data/test/unit/controller_mixin/sdata_show_instance_spec.rb +98 -0
  69. data/test/unit/controller_mixin/sdata_update_instance_spec.rb +65 -0
  70. data/test/unit/payload_map/payload_map_hash_spec.rb +84 -0
  71. data/test/unit/payload_map/payload_map_spec.rb +144 -0
  72. data/test/unit/predicate_spec.rb +59 -0
  73. data/test/unit/router_mixin/routes_spec.rb +138 -0
  74. data/test/unit/spec.opts +4 -0
  75. data/test/unit/spec_helper.rb +47 -0
  76. data/test/unit/spec_helpers/nokogiri_extensions.rb +16 -0
  77. data/test/unit/sync_controller_mixin/controller_mixin_spec.rb +22 -0
  78. data/test/unit/sync_controller_mixin/sdata_collection_sync_feed_spec.rb +69 -0
  79. metadata +175 -0
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin do
6
+ describe "given a class which behaves like ActionController::Base" do
7
+ before :all do
8
+ Base = Class.new(ActionController::Base)
9
+ end
10
+
11
+ describe "when SData::ControllerMixin is included" do
12
+ before :each do
13
+ Base.extend ControllerMixin
14
+ end
15
+
16
+ it "class should respond to .acts_as_sdata" do
17
+ Base.should respond_to(:acts_as_sdata)
18
+ Base.new.should_not respond_to(:acts_as_sdata)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,232 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin, "#sdata_collection" do
6
+ describe "given a model which acts as sdata" do
7
+ before :all do
8
+ Customer.extend SData::ActiveRecordExtensions::Mixin
9
+ Customer.class_eval { acts_as_sdata }
10
+ class EntryDiagnosisCustomer < Customer
11
+ def resource_header_attributes(*params)
12
+ raise 'Exception while trying to construct payload map'
13
+ end
14
+ end
15
+ class FeedDiagnosisCustomer < Customer
16
+ def id
17
+ raise 'Exception while trying to get customer id'
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "given a controller which acts as sdata" do
23
+ before :all do
24
+ class Base < ActionController::Base
25
+ extend ControllerMixin
26
+
27
+ Base.acts_as_sdata :model => Customer,
28
+ :feed => { :id => 'some-unique-id',
29
+ :author => 'Test Author',
30
+ :path => '/test_resource',
31
+ :title => 'List of Test Items',
32
+ :default_items_per_page => 10,
33
+ :maximum_items_per_page => 100}
34
+ end
35
+ end
36
+
37
+ before :each do
38
+ @controller = Base.new
39
+ @controller.stub! :request => OpenStruct.new(
40
+ :protocol => 'http://',
41
+ :host_with_port => 'example.com',
42
+ :request_uri => Base.sdata_options[:feed][:path],
43
+ :path => SData.store_path + '/-/testResource',
44
+ :query_parameters => {}),
45
+ :params => {}
46
+ end
47
+
48
+ #TODO: spec trying to populate xpaths for errored entries
49
+
50
+ context "when one of entries is erroneous" do
51
+ before :each do
52
+ @controller.sdata_options[:model].stub! :all => [EntryDiagnosisCustomer.new, Customer.new]
53
+ end
54
+
55
+ it "should respond with both entries" do
56
+ @controller.should_receive(:render) do |hash|
57
+ hash[:xml].entries.size.should == 2
58
+
59
+ failed_entries = hash[:xml].entries.reject{ |e| e.diagnosis.nil? }
60
+ successful_entries = hash[:xml].entries.select { |e| e.diagnosis.nil? }
61
+
62
+ failed_entries.size.should == 1
63
+ successful_entries.size.should == 1
64
+ end
65
+
66
+ @controller.sdata_collection
67
+ end
68
+
69
+ it "should compose diagnosis entry correctly" do
70
+ @controller.should_receive(:render) do |hash|
71
+ failed_entry = hash[:xml].entries.reject{ |e| e.diagnosis.nil? }.first
72
+
73
+ failed_entry.id.should_not be_nil
74
+ failed_entry.content.should_not be_nil
75
+ failed_entry.sdata_payload.should be_nil
76
+ end
77
+ @controller.sdata_collection
78
+ end
79
+
80
+ it "should compose sdata:diagnosis properties correctly" do
81
+ @controller.should_receive(:render) do |hash|
82
+ failed_entry = hash[:xml].entries.reject{ |e| e.diagnosis.nil? }.first
83
+ entry = Nokogiri::XML(failed_entry.to_xml, nil, nil, Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::NOBLANKS)
84
+
85
+ diagnosis = entry.xpath('/xmlns:entry/sdata:diagnosis').first
86
+ diagnosis.children.size.should == 4
87
+
88
+ diagnosis.xpath('sdata:severity/text()').to_s.should == 'error'
89
+ diagnosis.xpath('sdata:sdataCode/text()').to_s.should == 'ApplicationDiagnosis'
90
+ diagnosis.xpath('sdata:message/text()').to_s.should == "Exception while trying to construct payload map"
91
+ diagnosis.xpath('sdata:stackTrace/text()').to_s.include?('/diagnosis_spec.rb').should be_true
92
+ end
93
+ @controller.sdata_collection
94
+ end
95
+
96
+ it "should correctly compose regular entry as well" do
97
+ @controller.should_receive(:render) do |hash|
98
+ successful_entry = hash[:xml].entries.select{ |e| e.diagnosis.nil? }.first
99
+ successful_entry.id.should_not be_nil
100
+ successful_entry.content.should_not be_nil
101
+ successful_entry.sdata_payload.should_not be_nil
102
+ successful_entry.content.should_not be_nil
103
+ end
104
+ @controller.sdata_collection
105
+ end
106
+ end
107
+
108
+ it "should construct multiple diagnosis elements within the same entry to describe multiple caught exceptions" do
109
+ pending #not implemented, difficulties with rAtom
110
+ end
111
+
112
+ it "should construct diagnosis for caught Feed intercepting uncaught Entry exception" do
113
+ #TODO: spec trying to populate xpaths for errored entries
114
+ @controller.sdata_options[:model].stub! :all => [FeedDiagnosisCustomer.new, Customer.new]
115
+ @controller.should_receive(:render) do |hash|
116
+ hash[:xml].entries.size.should == 1
117
+ hash[:xml].entries.each do |entry|
118
+ entry.id.should_not be_nil
119
+ entry.sdata_payload.should_not be_nil
120
+ entry.content.should_not be_nil
121
+ entry.diagnosis.should be_nil
122
+ end
123
+ hash[:xml].simple_extensions['{http://schemas.sage.com/sdata/2008/1,diagnosis}'].size.should == 1
124
+ feed_diagnosis = hash[:xml].simple_extensions['{http://schemas.sage.com/sdata/2008/1,diagnosis}'][0]
125
+ feed_diagnosis.children.detect{|x|x.name=='sdata:severity'}.children[0].to_s.should == 'error'
126
+ feed_diagnosis.children.detect{|x|x.name=='sdata:sdataCode'}.children[0].to_s.should == 'ApplicationDiagnosis'
127
+ feed_diagnosis.children.detect{|x|x.name=='sdata:message'}.children[0].to_s.should == "Exception while trying to get customer id"
128
+ feed_diagnosis.children.detect{|x|x.name=='sdata:stackTrace'}.children[0].to_s.include?('/diagnosis_spec.rb').should == true
129
+ end
130
+ @controller.sdata_collection
131
+ end
132
+
133
+ it "should be able to combine feed and entry exceptions" do
134
+ @controller.sdata_options[:model].stub! :all => [FeedDiagnosisCustomer.new, FeedDiagnosisCustomer.new, EntryDiagnosisCustomer.new, Customer.new, Customer.new]
135
+ @controller.should_receive(:render) do |hash|
136
+ hash[:xml].entries.size.should == 3
137
+ hash[:xml].entries.collect{|e|e if e.diagnosis.nil?}.compact.size.should == 2
138
+ hash[:xml].entries.collect{|e|e if !e.diagnosis.nil?}.compact.size.should == 1
139
+ hash[:xml].entries.each do |entry|
140
+ entry.id.should_not be_nil
141
+ entry.content.should_not be_nil
142
+ if entry.diagnosis.nil?
143
+ entry.sdata_payload.should_not be_nil
144
+ else
145
+ entry.sdata_payload.should be_nil
146
+ entry.diagnosis[0].children.detect{|x|x.name=='sdata:severity'}.children[0].to_s.should == 'error'
147
+ entry.diagnosis[0].children.detect{|x|x.name=='sdata:sdataCode'}.children[0].to_s.should == 'ApplicationDiagnosis'
148
+ entry.diagnosis[0].children.detect{|x|x.name=='sdata:message'}.children[0].to_s.should == "Exception while trying to construct payload map"
149
+ end
150
+ end
151
+ hash[:xml].simple_extensions['{http://schemas.sage.com/sdata/2008/1,diagnosis}'].size.should == 2
152
+ feed_diagnoses = hash[:xml].simple_extensions['{http://schemas.sage.com/sdata/2008/1,diagnosis}']
153
+ feed_diagnoses.each do |failed_entry|
154
+ failed_entry.children.detect{|x|x.name=='sdata:severity'}.children[0].to_s.should == 'error'
155
+ failed_entry.children.detect{|x|x.name=='sdata:sdataCode'}.children[0].to_s.should == 'ApplicationDiagnosis'
156
+ failed_entry.children.detect{|x|x.name=='sdata:message'}.children[0].to_s.should == "Exception while trying to get customer id"
157
+ end
158
+ end
159
+ @controller.sdata_collection
160
+ end
161
+
162
+ it "should construct standalone exception with full xml header" do
163
+ @controller.stub!(:sdata_scope).and_raise(Exception.new('exception rendering collection'))
164
+ @controller.should_receive(:render) do |hash|
165
+ hash[:xml].class.should == LibXML::XML::Document
166
+ hash[:xml].root.name.should == "sdata:diagnoses"
167
+ hash[:xml].root.children.size.should == 1
168
+ hash[:xml].root.children[0].name.should == "sdata:diagnosis"
169
+ hash[:xml].root.children[0].children.size.should == 4
170
+ attributes = hash[:xml].root.children[0].children.collect{|c|c.name}.sort
171
+ attributes.should == ["sdata:message", "sdata:sdataCode", "sdata:severity", "sdata:stackTrace"]
172
+ hash[:xml].root.children[0].children.each do |child|
173
+ child.children.size.should == 1
174
+ case child.name
175
+ when "sdata:message"
176
+ child.children[0].to_s.should == "exception rendering collection"
177
+ when "sdata:sdataCode"
178
+ child.children[0].to_s.should == "ApplicationDiagnosis"
179
+ when "sdata:severity"
180
+ child.children[0].to_s.should == "error"
181
+ end
182
+ end
183
+ end
184
+ @controller.sdata_collection
185
+ end
186
+
187
+
188
+ end
189
+
190
+ describe "given a controller which acts as sdata" do
191
+ before :all do
192
+
193
+ class NewBase < ActionController::Base
194
+ sdata_rescue_support
195
+ rescue_from Exception, :with => :global_rescue
196
+ def global_rescue
197
+ if request.env['REQUEST_URI'].match(/^\/sdata/)
198
+ #this case must happen in ALL rails environments (dev, test, prod, etc.)
199
+ sdata_global_rescue(exception, request.env['REQUEST_URI'])
200
+ end
201
+ end
202
+ end
203
+ NewBase.extend ControllerMixin
204
+ NewBase.extend SData::ApplicationControllerMixin
205
+ NewBase.acts_as_sdata :model => Customer,
206
+ :feed => { :id => 'some-unique-id',
207
+ :author => 'Test Author',
208
+ :path => '/test_resource',
209
+ :title => 'List of Test Items',
210
+ :default_items_per_page => 10,
211
+ :maximum_items_per_page => 100}
212
+
213
+ end
214
+
215
+ before :each do
216
+ @controller = NewBase.new
217
+ @controller.stub! :request => OpenStruct.new(
218
+ :protocol => 'http://',
219
+ :host_with_port => 'example.com',
220
+ :request_uri => NewBase.sdata_options[:feed][:path],
221
+ :path => SData.store_path + '/-/testResource',
222
+ :query_parameters => {}),
223
+ :params => {}
224
+ end
225
+
226
+ it "should catch unhandled feed exception in a handled method exception" do
227
+ pending #can't figure out how to do it here, perhaps only easily possible in cucumber
228
+ end
229
+ end
230
+
231
+ end
232
+ end
@@ -0,0 +1,78 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin, "#sdata_collection" do
6
+ describe "given a model which acts as sdata" do
7
+ before :all do
8
+ @time = Time.now - 1.day
9
+
10
+ class ModelBob
11
+ extend SData::ActiveRecordExtensions::Mixin
12
+ acts_as_sdata
13
+ def self.name
14
+ "SData::Contracts::CrmErp::ModelBob"
15
+ end
16
+ def id
17
+ 1
18
+ end
19
+ def attributes; {} end
20
+ def updated_at
21
+ @time
22
+ end
23
+ def created_by
24
+ OpenStruct.new(:id => 1, :sage_username => 'sage_user')
25
+ end
26
+ def name
27
+ "John Smith"
28
+ end
29
+ def sdata_content
30
+ "ModelBob ##{self.id}: #{self.name}"
31
+ end
32
+ def payload_map
33
+ {}
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "given a controller which acts as sdata" do
39
+ before :all do
40
+ Base = Class.new(ActionController::Base)
41
+ Base.extend ControllerMixin
42
+
43
+
44
+ Base.acts_as_sdata :model => ModelBob,
45
+ :feed => { :id => 'some-unique-id',
46
+ :author => 'Test Author',
47
+ :path => '/test_resource',
48
+ :title => 'List of Test Items',
49
+ :default_items_per_page => 10,
50
+ :maximum_items_per_page => 100}
51
+
52
+ end
53
+
54
+ before :each do
55
+ @controller = Base.new
56
+ @controller.stub! :request => OpenStruct.new(
57
+ :protocol => 'http',
58
+ :host_with_port => 'http://example.com',
59
+ :request_uri => Base.sdata_options[:feed][:path],
60
+ :path => SData.store_path + '/-/testResource',
61
+ :query_parameters => {}),
62
+ :params => {}
63
+ @controller.sdata_options[:model].stub! :all => [ModelBob.new, ModelBob.new]
64
+ end
65
+
66
+ it "should render Atom feed" do
67
+
68
+ @controller.should_receive(:render) do |hash|
69
+ hash[:content_type].should == "application/atom+xml; type=feed"
70
+ hash[:xml].should be_kind_of(Atom::Feed)
71
+ hash[:xml].entries.should == ModelBob.all.map{|entry| entry.to_atom({})}
72
+ end
73
+ @controller.sdata_collection
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,173 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin, "#sdata_create_instance" do
6
+
7
+ describe "given a controller which acts as sdata with a virtual model as a base" do
8
+
9
+ class VirtualModel < SData::VirtualBase
10
+ attr_accessor :baze
11
+ self.baze_class = Class
12
+ end
13
+
14
+ before :all do
15
+ Base = Class.new(ActionController::Base)
16
+ Base.extend ControllerMixin
17
+ Model = Class.new
18
+ Base.acts_as_sdata :model => VirtualModel
19
+ end
20
+
21
+ before :each do
22
+ pending
23
+ @controller = Base.new
24
+ end
25
+
26
+ describe "given params contain Atom::Entry" do
27
+ before :each do
28
+ @entry = Atom::Entry.new
29
+ @controller.stub! :params => { :entry => @entry }
30
+
31
+ @model = VirtualModel.new(Model.new)
32
+ VirtualModel.should_receive(:new).and_return @model
33
+ end
34
+
35
+ describe "when save is successful" do
36
+ before :each do
37
+ @model.baze.stub! :save => true
38
+ @model.stub! :to_atom => stub(:to_xml => '<entry></entry>')
39
+ end
40
+
41
+ it "should respond with 201 (created)" do
42
+ @controller.should_receive(:render) do |args|
43
+ args[:status].should == :created
44
+ end
45
+ @controller.sdata_create_instance
46
+ end
47
+
48
+ it "should return updated model as a body" do
49
+ @controller.should_receive(:render) do |args|
50
+ args[:content_type].should == "application/atom+xml; type=entry"
51
+ args[:xml].should == @model.to_atom.to_xml
52
+ end
53
+
54
+ @controller.sdata_create_instance
55
+ end
56
+ end
57
+
58
+
59
+ describe "when save fails" do
60
+ before :each do
61
+ @model.baze.stub! :save => false
62
+ @model.stub! :errors => stub(:to_xml => '<errors></errors>')
63
+ end
64
+
65
+ it "should respond with 400 (Bad Request)" do
66
+ @controller.should_receive(:render) do |args|
67
+ args[:status].should == :bad_request
68
+ end
69
+
70
+ @controller.sdata_create_instance
71
+ end
72
+
73
+ it "should return validation errors as a body" do
74
+ @controller.should_receive(:render) do |args|
75
+ args[:xml].should == @model.errors.to_xml
76
+ end
77
+
78
+ @controller.sdata_create_instance
79
+ end
80
+ end
81
+
82
+
83
+ end
84
+
85
+ end
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+ describe "given a controller which acts as sdata" do
103
+ before :all do
104
+ Base = Class.new(ActionController::Base)
105
+ Base.extend ControllerMixin
106
+
107
+ Model = Class.new
108
+ Base.acts_as_sdata :model => Model
109
+ end
110
+
111
+ before :each do
112
+ pending
113
+ @controller = Base.new
114
+ end
115
+
116
+ describe "given params contain Atom::Entry" do
117
+ before :each do
118
+ @entry = Atom::Entry.new
119
+ @controller.stub! :params => { :entry => @entry }
120
+
121
+ @model = Model.new
122
+ Model.should_receive(:new).and_return @model
123
+ end
124
+
125
+ describe "when save is successful" do
126
+ before :each do
127
+ @model.stub! :save => true
128
+ @model.stub! :to_atom => stub(:to_xml => '<entry></entry>')
129
+ end
130
+
131
+ it "should respond with 201 (created)" do
132
+ @controller.should_receive(:render) do |args|
133
+ args[:status].should == :created
134
+ end
135
+
136
+ @controller.sdata_create_instance
137
+ end
138
+
139
+ it "should return updated model as a body" do
140
+ @controller.should_receive(:render) do |args|
141
+ args[:content_type].should == "application/atom+xml; type=entry"
142
+ args[:xml].should == @model.to_atom.to_xml
143
+ end
144
+
145
+ @controller.sdata_create_instance
146
+ end
147
+ end
148
+
149
+ describe "when save fails" do
150
+ before :each do
151
+ @model.stub! :save => false
152
+ @model.stub! :errors => stub(:to_xml => '<errors></errors>')
153
+ end
154
+
155
+ it "should respond with 400 (Bad Request)" do
156
+ @controller.should_receive(:render) do |args|
157
+ args[:status].should == :bad_request
158
+ end
159
+
160
+ @controller.sdata_create_instance
161
+ end
162
+
163
+ it "should return validation errors as a body" do
164
+ @controller.should_receive(:render) do |args|
165
+ args[:xml].should == @model.errors.to_xml
166
+ end
167
+
168
+ @controller.sdata_create_instance
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end