opium 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,274 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opium::File do
4
+ it { expect( described_class ).to be <= Opium::Model::Connectable }
5
+ let!(:gif_file) do
6
+ Tempfile.new(['test', '.gif']).tap do |f|
7
+ f.binmode
8
+ f.write Base64.decode64('R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7')
9
+ end
10
+ end
11
+
12
+ let(:location) { 'http://files.example.com/1234-file.gif' }
13
+
14
+ it { expect( described_class ).to respond_to(:upload, :to_ruby, :to_parse).with(1).argument }
15
+ it { is_expected.to respond_to( :delete, :name, :url, :mime_type, :to_parse ) }
16
+
17
+ describe '.upload' do
18
+ subject { described_class }
19
+ let(:upload_options) { {} }
20
+ let(:result) { subject.upload( gif_file, upload_options ) }
21
+
22
+ before do
23
+ stub_request( :post, %r{https://api\.parse\.com/1/files/.+} ).
24
+ with(
25
+ body: "9a\x15\x00\x04\x00\x80\x00\x00#-0\xFF\xFF\xFF!\xF9\x04\x01\x00\x00\x01\x00,\x00\x00\x00\x00\x15\x00\x04\x00\x00\x02\r\x8C\x8F\x01\xC9\xAD\xB0\x9C\x83T2\x8AY\x01\x00;".force_encoding('ASCII-8BIT'),
26
+ headers: {content_type: 'image/gif', x_parse_application_id: 'PARSE_APP_ID', x_parse_rest_api_key: 'PARSE_API_KEY'}
27
+ ).to_return(status: 201, body: ->(request) { { url: location, name: "1234-#{ ::File.basename(request.uri) }" }.to_json }, headers: { content_type: 'application/json', location: location })
28
+ end
29
+
30
+ context 'when the upload completes' do
31
+ it { expect { result }.to_not raise_exception }
32
+ it { expect( result ).to be_a Opium::File }
33
+ it { expect( result.url ).to_not be_nil }
34
+ it { expect( result.name ).to_not be_nil }
35
+ end
36
+
37
+ context 'with a :original_filename option' do
38
+ let(:upload_options) { { original_filename: 'chunky_bacon.jpg' } }
39
+
40
+ it { expect { result }.to_not raise_exception }
41
+ it 'preferentially uses the :original_filename' do
42
+ expect( result.name ).to end_with 'chunky_bacon.jpg'
43
+ end
44
+ end
45
+
46
+ context 'with a :content_type option' do
47
+ let(:upload_options) { { content_type: 'image/png', sent_headers: true } }
48
+
49
+ it { expect { result }.to_not raise_exception }
50
+ it { expect( result ).to have_key( 'Content-Type') }
51
+ it 'sends the given :content_type value' do
52
+ expect( result['Content-Type'] ).to eq 'image/png'
53
+ end
54
+ it { expect( result.keys ).to include( 'X-Parse-Rest-Api-Key' )}
55
+ it { expect( result.keys ).to_not include( 'X-Parse-Master-Key' )}
56
+ end
57
+
58
+ context 'without a :content_type option' do
59
+ let(:upload_options) { { sent_headers: true } }
60
+
61
+ it { expect { result }.to_not raise_exception }
62
+ it { expect( result ).to have_key( 'Content-Type' ) }
63
+ it 'sends the proper content_type' do
64
+ expect( result['Content-Type'] ).to eq 'image/gif'
65
+ end
66
+ it { expect( result.keys ).to include( 'X-Parse-Rest-Api-Key' )}
67
+ it { expect( result.keys ).to_not include( 'X-Parse-Master-Key' )}
68
+ end
69
+ end
70
+
71
+ describe '.to_ruby' do
72
+ subject { described_class }
73
+
74
+ let(:result) { subject.to_ruby( object ) }
75
+
76
+ context 'when given a hash with __type: "File"' do
77
+ let(:object) { { __type: 'File', url: location, name: 'chunky_bacon.jpg' } }
78
+
79
+ it { expect { result }.to_not raise_exception }
80
+ it { expect( result ).to be_a Opium::File }
81
+ end
82
+
83
+ context 'when given a hash with just a :url and :name' do
84
+ let(:object) { { url: location, name: 'chunky_bacon.jpg' } }
85
+
86
+ it { expect { result }.to_not raise_exception }
87
+ it { expect( result ).to be_a Opium::File }
88
+ end
89
+
90
+ context 'when given a hash with __type != "File"' do
91
+ let(:object) { { __type: 'Pointer' } }
92
+
93
+ it { expect { result }.to raise_exception }
94
+ end
95
+
96
+ context 'when given an Opium::File' do
97
+ let(:object) { Opium::File.new( url: location, name: 'chunky_bacon.jpg' ) }
98
+
99
+ it { expect { result }.to_not raise_exception }
100
+ it { expect( result ).to be_a Opium::File }
101
+ it { expect( result ).to eq object }
102
+ end
103
+
104
+ context 'when given nil' do
105
+ let(:object) { nil }
106
+
107
+ it { expect { result }.to_not raise_exception }
108
+ it { expect( result ).to be_nil }
109
+ end
110
+
111
+ context 'when not given a hash' do
112
+ let(:object) { 42 }
113
+
114
+ it { expect { result }.to raise_exception }
115
+ end
116
+ end
117
+
118
+ describe '.to_parse' do
119
+ subject { described_class }
120
+
121
+ let(:result) { described_class.to_parse( object ) }
122
+
123
+ context 'when given an Opium::File' do
124
+ let(:object) { subject.new( url: location, name: 'chunky_bacon.jpg' ) }
125
+
126
+ it { expect { result }.to_not raise_exception }
127
+ it { expect( result ).to be_a Hash }
128
+ it { expect( result ).to have_key '__type' }
129
+ it { expect( result ).to have_key 'name' }
130
+ it 'has the proper values for :__type and :name' do
131
+ expect( result ).to eq( '__type' => 'File', 'name' => 'chunky_bacon.jpg' )
132
+ end
133
+ end
134
+
135
+ context 'when given nil' do
136
+ let(:object) { nil }
137
+
138
+ it { expect { result }.to_not raise_exception }
139
+ it { expect( result ).to be_nil }
140
+ end
141
+
142
+ context 'when given anything else' do
143
+ let(:object) { 42 }
144
+
145
+ it { expect { result }.to raise_exception }
146
+ end
147
+ end
148
+
149
+ context '#initialize' do
150
+ let(:filename) { 'file.png' }
151
+ let(:location) { 'http://files.example.com/1234-file.png' }
152
+ subject { described_class.new( __type: 'File', url: location, name: filename ) }
153
+
154
+ it 'sets the url' do
155
+ expect( subject.url ).to eq location
156
+ end
157
+
158
+ it 'sets the name' do
159
+ expect( subject.name ).to eq filename
160
+ end
161
+
162
+ it 'infers the mime_type' do
163
+ expect( subject.mime_type ).to eq 'image/png'
164
+ end
165
+ end
166
+
167
+ describe '#delete' do
168
+ subject { described_class.new( url: location, name: 'chunky_bacon.jpg' ) }
169
+
170
+ before do
171
+ stub_request(:delete, "https://api.parse.com/1/files/chunky_bacon.jpg").
172
+ with(:headers => {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Master-Key'=>'PARSE_MASTER_KEY'}).
173
+ to_return(:status => 200, :body => "{}", :headers => { content_type: 'application/json' })
174
+ end
175
+
176
+ let(:delete_options) { {} }
177
+ let(:result) { subject.delete( delete_options ) }
178
+
179
+ context 'with a name' do
180
+ it { expect { result }.to_not raise_exception }
181
+ it 'freezes the Opium::File' do
182
+ result
183
+ expect( subject ).to be_frozen
184
+ end
185
+ end
186
+
187
+ context 'without a name' do
188
+ subject { described_class.new }
189
+
190
+ it { expect { result }.to raise_exception }
191
+ end
192
+
193
+ context 'when executed' do
194
+ let(:delete_options) { { sent_headers: true } }
195
+
196
+ it { expect { result }.to_not raise_exception }
197
+ it { expect( result.keys ).to include( 'X-Parse-Master-Key' ) }
198
+ it { expect( result.keys ).to_not include( 'X-Parse-Rest-Api-Key' ) }
199
+ end
200
+ end
201
+
202
+ describe '#to_parse' do
203
+ let(:result) { subject.to_parse }
204
+
205
+ context 'when #name has a value' do
206
+ subject { described_class.new( url: location, name: 'chunky_bacon.jpg' ) }
207
+
208
+ it { expect { result }.to_not raise_exception }
209
+ it { expect( result ).to be_a Hash }
210
+ it { expect( result ).to have_key '__type' }
211
+ it { expect( result ).to have_key 'name' }
212
+ it 'has the proper values for :__type and :name' do
213
+ expect( result ).to eq( '__type' => 'File', 'name' => 'chunky_bacon.jpg' )
214
+ end
215
+ end
216
+
217
+ context 'when #name is empty' do
218
+ subject { described_class.new }
219
+
220
+ it { expect { result }.to raise_exception }
221
+ end
222
+ end
223
+
224
+ describe '#inspect' do
225
+ context 'with data' do
226
+ let(:filename) { 'file.gif' }
227
+ let(:mime) { 'image/gif' }
228
+
229
+ subject { described_class.new( url: location, name: filename ) }
230
+
231
+ it 'has the values for all components' do
232
+ expect( subject.inspect ).to eq %{#<Opium::File name="#{ filename }" url="#{ location }" mime_type="#{ mime }">}
233
+ end
234
+ end
235
+
236
+ context 'without data' do
237
+ subject { described_class.new }
238
+
239
+ it 'has nil values for all components' do
240
+ expect( subject.inspect ).to eq '#<Opium::File name=nil url=nil mime_type=nil>'
241
+ end
242
+ end
243
+ end
244
+
245
+ context 'when used as a field type' do
246
+ before do
247
+ stub_const( 'Game', Class.new do
248
+ include Opium::Model
249
+ field :cover_image, type: Opium::File
250
+ end )
251
+
252
+ stub_request(:get, "https://api.parse.com/1/classes/Game/abcd1234").
253
+ with(:headers => {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
254
+ to_return(:status => 200, :body => { objectId: 'abcd1234', coverImage: { __type: 'File', name: 'chunky_bacon.jpg', url: location }, createdAt: '2015-01-01T12:00:00Z' }.to_json, :headers => { content_type: 'application/json' })
255
+
256
+ stub_request(:post, "https://api.parse.com/1/classes/Game").
257
+ with(
258
+ :body => "{\"coverImage\":{\"__type\":\"File\",\"name\":\"chunky_bacon.jpg\"}}",
259
+ :headers => {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
260
+ to_return(:status => 200, :body => { objectId: 'abcd1234', createdAt: '2015-01-01T12:00:00Z' }.to_json, :headers => { content_type: 'application/json' })
261
+
262
+ end
263
+
264
+ it 'is retrievable as an Opium::File' do
265
+ game = Game.find('abcd1234')
266
+ expect( game.cover_image ).to be_a Opium::File
267
+ end
268
+
269
+ it 'is persistable as a Parse File object hash' do
270
+ game = Game.new cover_image: Opium::File.new( url: location, name: 'chunky_bacon.jpg' )
271
+ expect { game.save! }.to_not raise_exception
272
+ end
273
+ end
274
+ end
@@ -3,42 +3,56 @@ require 'spec_helper'
3
3
  describe Opium::Model::Attributable do
4
4
  let( :model ) { Class.new { include Opium::Model::Attributable } }
5
5
 
6
- describe 'instance' do
6
+ context 'within an instance' do
7
7
  subject { model.new }
8
8
 
9
- it { should respond_to( :attributes, :attributes= ) }
10
- it { should respond_to( :attributes_to_parse ) }
9
+ it { is_expected.to respond_to( :attributes, :attributes= ) }
10
+ it { is_expected.to respond_to( :attributes_to_parse ) }
11
11
  end
12
12
 
13
- describe 'when included in a model' do
13
+ context 'when included in a model' do
14
14
  before do
15
15
  stub_const( 'Book', Class.new do
16
16
  include Opium::Model
17
17
  field :title, type: String
18
+ field :genre, type: Symbol
19
+ attr_accessor :not_a_field
18
20
  end )
19
21
  end
20
22
 
21
- subject { Book.new( title: 'Little Brother', id: 'abc123', created_at: Time.now ) }
23
+ subject { Book.new( title: 'Little Brother', id: 'abc123', created_at: Time.now, genre: :sci_fi ) }
22
24
 
23
- describe 'attributes_to_parse' do
24
- it 'when called with no parameters, should have all fields present, with names and values converted to parse' do
25
- subject.attributes_to_parse.should =~ { 'objectId' => 'abc123', 'title' => 'Little Brother', 'createdAt' => subject.created_at.to_parse, 'updatedAt' => nil }
25
+ describe '#attributes_to_parse' do
26
+ context 'when called with no parameters' do
27
+ it 'has all fields present, with names and values converted to parse' do
28
+ expect( subject.attributes_to_parse ).to eq( 'objectId' => 'abc123', 'title' => 'Little Brother', 'genre' => 'sci_fi', 'createdAt' => subject.created_at.to_parse, 'updatedAt' => nil )
29
+ end
26
30
  end
27
31
 
28
- it 'when called with except, should exclude the excepted fields' do
29
- subject.attributes_to_parse( except: [:id, :updated_at] ).should =~ { 'title' => 'Little Brother', 'createdAt' => subject.created_at.to_parse }
32
+ context 'when called with except' do
33
+ it 'excludes the excepted fields' do
34
+ expect( subject.attributes_to_parse( except: [:id, :updated_at] ) ).to eq( 'title' => 'Little Brother', 'genre' => 'sci_fi', 'createdAt' => subject.created_at.to_parse )
35
+ end
30
36
  end
31
37
 
32
- it 'when called with not_readonly, should exclude all readonly fields' do
33
- subject.attributes_to_parse( not_readonly: true ).should =~ { 'title' => 'Little Brother' }
38
+ context 'when called with not_readonly' do
39
+ it 'excludes all readonly fields' do
40
+ expect( subject.attributes_to_parse( not_readonly: true ) ).to eq( 'title' => 'Little Brother', 'genre' => 'sci_fi' )
41
+ end
34
42
  end
35
43
  end
36
44
 
37
- describe ':attributes=' do
38
- it 'should still store unrecognized fields in the attributes hash' do
45
+ describe '#attributes=' do
46
+ it 'stores unrecognized fields in the attributes hash' do
39
47
  expect { subject.attributes = { unknownField: 42 } }.to_not raise_exception
40
- subject.attributes.should have_key('unknownField')
41
- subject.attributes['unknownField'].should == 42
48
+ expect( subject.attributes ).to have_key('unknownField')
49
+ expect( subject.attributes['unknownField'] ).to eq 42
50
+ end
51
+
52
+ it 'calls relevant setters rather for non fields if they exist' do
53
+ expect { subject.attributes = { not_a_field: 42 } }.to_not raise_exception
54
+ expect( subject.not_a_field ).to eq 42
55
+ expect( subject.attributes ).to_not have_key('not_a_field')
42
56
  end
43
57
  end
44
58
  end
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opium::Model::Batchable::Batch do
4
+ let(:update_path) { '/1/classes/Game/abcd1234' }
5
+ let(:body) { { title: 'Skyrim' } }
6
+
7
+ describe '#owner' do
8
+ it 'is a property' do
9
+ is_expected.to respond_to(:owner)
10
+ end
11
+ end
12
+
13
+ describe '#depth' do
14
+ let(:depth) { subject.depth }
15
+
16
+ context 'within a new batch' do
17
+ subject { described_class.new }
18
+
19
+ it { expect(depth).to eq 0 }
20
+ end
21
+ end
22
+
23
+ describe '#dive' do
24
+ after { subject.depth = 0 }
25
+
26
+ it { expect { subject.dive }.to change( subject, :depth ).by(1) }
27
+ end
28
+
29
+ describe '#ascend' do
30
+ after { subject.depth = 0 }
31
+
32
+ it { expect { subject.ascend }.to change( subject, :depth ).by(-1) }
33
+ end
34
+
35
+ describe '#queue' do
36
+ let(:queue) { subject.queue }
37
+
38
+ context 'within a new batch' do
39
+ subject { described_class.new }
40
+
41
+ it { expect(queue).to eq [] }
42
+ end
43
+ end
44
+
45
+ describe '#enqueue' do
46
+ let(:perform) { subject.enqueue( operation ) }
47
+
48
+ after { subject.queue.clear }
49
+
50
+ context 'with a valid operation hash' do
51
+ let(:operation) { { method: :put, path: update_path, body: body } }
52
+
53
+ it { expect { perform }.to_not raise_exception }
54
+ it { expect( perform ).to be_a Opium::Model::Batchable::Operation }
55
+ end
56
+
57
+ context 'with an Operation object' do
58
+ let(:operation) { Opium::Model::Batchable::Operation.new( method: :put, path: update_path, body: body ) }
59
+
60
+ it { expect { perform }.to_not raise_exception }
61
+ it { expect( perform ).to be_a Opium::Model::Batchable::Operation }
62
+ end
63
+
64
+ context 'with an invalid operation hash' do
65
+ let(:operation) { { path: update_path, body: body } }
66
+
67
+ it { expect { perform }.to raise_exception }
68
+ end
69
+ end
70
+
71
+ describe '#execute' do
72
+ let(:execute) { subject.execute }
73
+ let(:operation) { Opium::Model::Batchable::Operation.new( method: :put, path: update_path, body: body ) }
74
+
75
+ before do
76
+ subject.owner = double()
77
+ allow( subject.owner ).to receive(:http_post).and_return( [ { success: { objectId: 'abcd1234', createdAt: Time.now } } ] )
78
+ subject.enqueue( operation )
79
+ end
80
+
81
+ after do
82
+ subject.owner = nil
83
+ subject.queue.clear
84
+ end
85
+
86
+ context 'when #depth is 0' do
87
+ it { expect( subject.depth ).to eq 0 }
88
+ it { expect { execute }.to_not change( subject, :depth ) }
89
+ it 'performs a POST of the batch' do
90
+ expect( subject.owner ).to receive(:http_post).with( subject.to_parse.first )
91
+ execute
92
+ end
93
+ end
94
+
95
+ context 'when #depth is greater than 0' do
96
+ before { subject.dive }
97
+ after { subject.ascend }
98
+
99
+ it { expect( subject.depth ).to eq 1 }
100
+ it { expect { execute }.to change( subject, :depth ).by(-1) }
101
+ it 'does not POST the batch' do
102
+ expect( subject.owner ).to_not receive(:http_post)
103
+ execute
104
+ end
105
+ end
106
+
107
+ context 'when #queue is empty' do
108
+ before { subject.queue.clear }
109
+
110
+ it { expect { subject.execute }.to raise_exception }
111
+ end
112
+
113
+ context 'when #queue has more than MAX_BATCH_SIZE operations' do
114
+ before { subject.queue = [operation] * 51 }
115
+ after { subject.queue.clear }
116
+
117
+ it 'performs multiple POSTs' do
118
+ expect( subject.owner ).to receive(:http_post).twice
119
+ execute
120
+ end
121
+ end
122
+ end
123
+
124
+ describe '#to_parse' do
125
+ let(:result) { subject.to_parse }
126
+
127
+ context 'with Operations in the queue' do
128
+ before { subject.enqueue( method: :put, path: update_path, body: body ) }
129
+ after { subject.queue.clear }
130
+
131
+ it { expect { result }.to_not raise_exception }
132
+ it { expect( result ).to be_a Array }
133
+
134
+ it { expect( result.first ).to be_a Hash }
135
+ it { expect( result.first ).to have_key( :requests ) }
136
+ it { expect( result.first[:requests] ).to be_a Array }
137
+ it { expect( result.first[:requests] ).to_not be_empty }
138
+ end
139
+
140
+ context 'with an empty queue' do
141
+ it { expect { result }.to_not raise_exception }
142
+ it { expect( result ).to eq [] }
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opium::Model::Batchable::Operation do
4
+ let(:update_path) { '/1/classes/Game/abcd1234' }
5
+ let(:body) { { title: 'Skyrim' } }
6
+
7
+ describe '#initialize' do
8
+ let(:result) { described_class.new( properties ) }
9
+
10
+ context 'with all properties' do
11
+ let(:properties) { { method: :put, path: update_path, body: body } }
12
+
13
+ it { expect { result }.to_not raise_exception }
14
+ it 'sets the method' do
15
+ expect( result.method ).to eq :put
16
+ end
17
+
18
+ it 'sets the path' do
19
+ expect( result.path ).to eq update_path
20
+ end
21
+
22
+ it 'sets the body' do
23
+ expect( result.body ).to eq body
24
+ end
25
+ end
26
+
27
+ context 'when :method is missing' do
28
+ let(:properties) { { path: update_path, body: body } }
29
+
30
+ it { expect { result }.to raise_exception }
31
+ end
32
+
33
+ context 'when :path is missing' do
34
+ let(:properties) { { method: :put, body: body } }
35
+
36
+ it { expect { result }.to raise_exception }
37
+ end
38
+
39
+ context 'when :body is missing' do
40
+ let(:properties) { { method: :put, path: update_path } }
41
+
42
+ it { expect { result }.to_not raise_exception }
43
+ it { expect( result.body ).to be_nil }
44
+ end
45
+ end
46
+
47
+ describe '#to_parse' do
48
+ let(:result) { subject.to_parse }
49
+
50
+ context 'when a body is present' do
51
+ subject { described_class.new( method: :put, path: update_path, body: body ) }
52
+
53
+ it { expect { result }.to_not raise_exception }
54
+ it { expect( result ).to be_a Hash }
55
+ it { expect( result.keys ).to include( :method, :path, :body ) }
56
+ it 'stringifies and upcases the method' do
57
+ expect( result[:method] ).to eq 'PUT'
58
+ end
59
+ end
60
+
61
+ context 'without a body' do
62
+ subject { described_class.new( method: :put, path: update_path ) }
63
+
64
+ it { expect { result }.to_not raise_exception }
65
+ it { expect( result ).to be_a Hash }
66
+ it { expect( result.keys ).to include( :method, :path ) }
67
+ it { expect( result ).to_not have_key( :body ) }
68
+ it 'stringifies and upcases the method' do
69
+ expect( result[:method] ).to eq 'PUT'
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Opium::Model::Batchable do
4
+ it { expect( described_class.constants ).to include( :Batch, :Operation ) }
5
+ end
@@ -215,4 +215,31 @@ describe Opium::Model::Connectable do
215
215
  subject.requires_heightened_privileges?.should == true
216
216
  end
217
217
  end
218
+
219
+ describe '.with_heightened_privileges' do
220
+ it { expect {|b| subject.with_heightened_privileges &b}.to yield_control }
221
+
222
+ it 'has heightened privileges within the block' do
223
+ subject.with_heightened_privileges do
224
+ expect( subject ).to have_heightened_privileges
225
+ end
226
+ end
227
+
228
+ it 'does not have heightened privileges outside the block' do
229
+ subject.with_heightened_privileges {}
230
+ expect( subject ).to_not have_heightened_privileges
231
+ end
232
+
233
+ it 'turns off heightened privileges on exceptions' do
234
+ expect { subject.with_heightened_privileges { raise 'expected' } }.to raise_exception
235
+ expect( subject ).to_not have_heightened_privileges
236
+ end
237
+
238
+ it 'keeps the previous value of requires_heightened_privileges? after the block' do
239
+ subject.requires_heightened_privileges!
240
+ subject.with_heightened_privileges {}
241
+ expect( subject ).to have_heightened_privileges
242
+ subject.instance_variable_set :@requires_heightened_privileges, nil
243
+ end
244
+ end
218
245
  end
@@ -130,4 +130,39 @@ describe Opium::Model::Fieldable do
130
130
  end
131
131
  end
132
132
  end
133
+
134
+ context 'when a model has fields' do
135
+ before do
136
+ stub_const( 'Model', Class.new do
137
+ include Opium::Model
138
+ field :symbolic_with_string_default, type: Symbol, default: 'value'
139
+ field :symbolic_with_symbol_default, type: Symbol, default: :value
140
+ end )
141
+ end
142
+
143
+ subject { Model }
144
+ let(:instance) { subject.new }
145
+
146
+ it 'converts default values correctly' do
147
+ expect( Model.default_attributes ).to include( 'symbolic_with_string_default' => :value, 'symbolic_with_symbol_default' => :value )
148
+ end
149
+
150
+ describe '.field' do
151
+ subject { Model.new }
152
+
153
+ it 'converts default values' do
154
+ expect( subject.symbolic_with_string_default ).to be_a Symbol
155
+ expect( subject.symbolic_with_symbol_default ).to be_a Symbol
156
+ end
157
+ end
158
+
159
+ describe '.field=' do
160
+ subject { Model.new }
161
+
162
+ it 'converts strings appropriately' do
163
+ expect { subject.symbolic_with_symbol_default = 'updated_value' }.to change( subject, :symbolic_with_symbol_default ).from(:value).to(:updated_value)
164
+ expect( subject.symbolic_with_symbol_default ).to be_a Symbol
165
+ end
166
+ end
167
+ end
133
168
  end