acts_as_sdata 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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