solidus_auction 0.0.4 → 0.0.5

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.
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::AuctionsController, type: :controller do
4
+ context '#index' do
5
+ it 'lists auctions' do
6
+ auctions = [
7
+ create(:auction),
8
+ create(:auction),
9
+ create(:auction)
10
+ ]
11
+ get :index
12
+ expect(assigns[:auctions]).to match_array auctions
13
+ end
14
+
15
+ it 'renders the index template' do
16
+ get :index
17
+ expect(response.status).to eq 200
18
+ expect(response).to render_template :index
19
+ end
20
+ end
21
+
22
+ context '#show' do
23
+ let!(:auction) { create(:auction) }
24
+
25
+ context 'for an auction that does not exist' do
26
+ it 'responds with a 404' do
27
+ expect {
28
+ get :show, params: { id: 'not_real' }
29
+ }.to raise_error ActiveRecord::RecordNotFound
30
+ end
31
+ end
32
+
33
+ it 'assigns an auction instance variable' do
34
+ get :show, params: { id: auction.id }
35
+ expect(response.status).to eq 200
36
+ expect(assigns[:auction]).to eq auction
37
+ end
38
+
39
+ it 'renders the show template' do
40
+ get :show, params: { id: auction.id }
41
+ expect(response.status).to eq 200
42
+ expect(response).to render_template :show
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::BidsController, type: :controller do
4
+ let(:auction) { create(:auction) }
5
+ let(:product) { create(:product) }
6
+ let(:bidder1) { create(:user) }
7
+ let(:bidder2) { create(:user) }
8
+ let(:bidder3) { create(:user) }
9
+
10
+ context '#index' do
11
+ it 'lists bids' do
12
+ bids = [
13
+ create(:bid, bidder: bidder1, amount: 26.00, auction: auction),
14
+ create(:bid, bidder: bidder2, amount: 27.00, auction: auction),
15
+ create(:bid, bidder: bidder3, amount: 28.00, auction: auction)
16
+ ]
17
+
18
+ bids.each do |b|
19
+ auction.receive_bid(b)
20
+ end
21
+ auction.reload
22
+
23
+ get :index, params: { auction_id: auction.id }
24
+ expect(assigns[:bids]).to match_array bids
25
+ expect(assigns[:auction]).to eq auction
26
+ end
27
+
28
+ it 'renders the index template' do
29
+ get :index, params: { auction_id: auction.id }
30
+ expect(response.status).to eq 200
31
+ expect(response).to render_template :index
32
+ end
33
+ end
34
+
35
+ context '#new' do
36
+ before do
37
+ controller.stub spree_current_user: bidder1
38
+ controller.stub spree_user_signed_in?: true
39
+ end
40
+
41
+ it 'assigns a bid instance variable' do
42
+ get :new, params: { auction_id: auction.id }
43
+ expect(assigns[:bid]).to be_a_kind_of Spree::Bid
44
+ end
45
+
46
+ it 'redirects to auction page if not signed in' do
47
+ controller.stub spree_current_user: nil
48
+ controller.stub spree_user_signed_in?: false
49
+ get :new, params: { auction_id: auction.id }
50
+ expect(response.status).to eq 302
51
+ expect(flash[:error]).to eq "You must be signed in to bid."
52
+ end
53
+ end
54
+
55
+ context '#create' do
56
+ before do
57
+ controller.stub spree_current_user: bidder1
58
+ controller.stub spree_user_signed_in?: true
59
+ end
60
+
61
+ it 'creates a normal bid' do
62
+ bid_params = {
63
+ auction_id: auction.id,
64
+ amount: 26.00
65
+ }
66
+
67
+ expect {
68
+ post :create, params: bid_params
69
+ }.to change(Spree::Bid, :count).by(1)
70
+ end
71
+
72
+ it 'creates an autobid' do
73
+ bid_params = {
74
+ auction_id: auction.id,
75
+ amount: 40.00
76
+ }
77
+
78
+ expect {
79
+ post :create, params: bid_params
80
+ }.to change(Spree::Bid, :count).by(2)
81
+ end
82
+
83
+ it 'fails if not signed in' do
84
+ bid_params = {
85
+ auction_id: auction.id,
86
+ amount: Random.rand(4000)
87
+ }
88
+ controller.stub spree_current_user: nil
89
+ controller.stub spree_user_signed_in?: false
90
+ post :create, params: bid_params
91
+ expect(flash[:error]).to eq "You must be signed in to bid."
92
+ end
93
+
94
+ it 'flashes a success' do
95
+ bid_params = {
96
+ auction_id: auction.id,
97
+ amount: 26.00
98
+ }
99
+
100
+ post :create, params: bid_params
101
+ expect(flash[:success]).to eq "You successfully bid $26.00."
102
+ end
103
+
104
+ it 'redirects to auction' do
105
+ bid_params = {
106
+ auction_id: auction.id,
107
+ amount: 26.00
108
+ }
109
+
110
+ post :create, params: bid_params
111
+ expect(response.status).to eq 302
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Auctions', js: true do
4
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ require "cancan/matchers"
4
+
5
+ describe Spree::AuctionAbility do
6
+ context 'permissions' do
7
+ let(:admin_user) { create(:admin_user) }
8
+ let(:admin_user2) { create(:admin_user) }
9
+ let(:user) { create(:user) }
10
+ let(:product) { create(:product) }
11
+ let!(:auction) {
12
+ create(:auction, {
13
+ product: product,
14
+ creator: admin_user
15
+ })}
16
+
17
+ it 'only allows admins to create an auction' do
18
+ expect(Spree::AuctionAbility.new(admin_user)).to be_able_to(:create, Spree::Auction.new)
19
+ expect(Spree::AuctionAbility.new(user)).to_not be_able_to(:create, Spree::Auction.new)
20
+ end
21
+
22
+ it 'only allows certain admins to edit an auction' do
23
+ expect(Spree::AuctionAbility.new(admin_user)).to be_able_to(:edit, auction)
24
+ expect(Spree::AuctionAbility.new(admin_user2)).to_not be_able_to(:edit, auction)
25
+ end
26
+
27
+ it 'only allows certain admins to update an auction' do
28
+ expect(Spree::AuctionAbility.new(admin_user)).to be_able_to(:update, auction)
29
+ expect(Spree::AuctionAbility.new(admin_user2)).to_not be_able_to(:update, auction)
30
+ end
31
+
32
+ it 'only allows certain admins to delete an auction' do
33
+ expect(Spree::AuctionAbility.new(admin_user)).to be_able_to(:destroy, auction)
34
+ expect(Spree::AuctionAbility.new(admin_user2)).to_not be_able_to(:destroy, auction)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,834 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Spree::Auction, type: :model do
4
+ context 'initializes' do
5
+ let(:bidder) { create(:user) }
6
+ let!(:auction) { create(:auction) }
7
+
8
+ it 'has a title' do
9
+ expect(auction.title).to eq "My First Auction"
10
+ end
11
+
12
+ it 'has a product' do
13
+ auction.reload
14
+ id = auction.product.id
15
+ expect(auction.product).to eq Spree::Product.find(id)
16
+ end
17
+
18
+ it 'has a creator' do
19
+ auction.reload
20
+ id = auction.creator.id
21
+ expect(auction.creator).to eq Spree::User.find(id)
22
+ end
23
+
24
+ it 'has a starting price' do
25
+ expect(auction.starting_price).to eq 25.00
26
+ end
27
+
28
+ it 'has a bid increment' do
29
+ expect(auction.bid_increment).to eq 1.00
30
+ end
31
+
32
+ it 'has a starting datetime' do
33
+ expect(auction.starting_datetime).to be_a_kind_of ActiveSupport::TimeWithZone
34
+ end
35
+
36
+ it 'has a planned end datetime' do
37
+ expect(auction.planned_end_datetime).to be_a_kind_of ActiveSupport::TimeWithZone
38
+ end
39
+
40
+ it 'has not completed' do
41
+ expect(auction.complete).to be false
42
+ end
43
+
44
+ it 'has inits the current_price as the starting_price' do
45
+ expect(auction.current_price).to eq 25.00
46
+ end
47
+
48
+ it 'has a default checkout deadline 1 day (1440 minutes) ahead' do
49
+ checkout_deadline = auction.checkout_deadline
50
+ expect(checkout_deadline).to eq(auction.current_end_datetime + 1440.minutes)
51
+ end
52
+ end
53
+
54
+ context 'processes bids' do
55
+ let!(:auction) {
56
+ create(:auction, {
57
+ starting_price: 25.00,
58
+ bid_increment: 1.00
59
+ })}
60
+ let(:bidder1) { create(:user) }
61
+ let(:bidder2) { create(:user) }
62
+ let(:bidder3) { create(:user) }
63
+ let(:bidder4) { create(:user) }
64
+
65
+ it 'updates current price when bid amount is higher than current price' do
66
+ bid = create(:bid, amount: 26.00, auction: auction)
67
+ auction.receive_bid(bid)
68
+ auction.reload
69
+ expect(auction.visible_bids.count).to eq 1
70
+ expect(auction.current_price).to eq 26.00
71
+ end
72
+
73
+ it 'does not update current price when bid amount is lower than current price' do
74
+ bid = create(:bid, amount: 15.00, auction: auction)
75
+ auction.receive_bid(bid)
76
+ auction.reload
77
+ expect(auction.visible_bids.count).to eq 0
78
+ expect(auction.current_price).to eq 25.00
79
+ end
80
+
81
+ it 'does not update current price when bid amount the same as current price' do
82
+ bid = create(:bid, amount: 25.00, auction: auction)
83
+ auction.receive_bid(bid)
84
+ auction.reload
85
+ expect(auction.visible_bids.count).to eq 0
86
+ expect(auction.current_price).to eq 25.00
87
+ end
88
+
89
+ it 'does not update current price when bid amount is lower than current price + increment' do
90
+ bid = create(:bid, amount: 25.50, auction: auction)
91
+ auction.receive_bid(bid)
92
+ auction.reload
93
+ expect(auction.visible_bids.count).to eq 0
94
+ expect(auction.current_price).to eq 25.00
95
+ end
96
+
97
+ it 'updates current price to last price plus bid increment' do
98
+ bid = create(:bid, amount: 27.00, auction: auction)
99
+ auction.receive_bid(bid)
100
+ auction.reload
101
+ expect(auction.visible_bids.count).to eq 1
102
+ expect(auction.current_price).to eq 26.00
103
+ end
104
+
105
+ it 'returns a bid for a successfully calculated bid' do
106
+ bid = create(:bid, amount: 26.00, auction: auction)
107
+ result, _ = auction.receive_bid(bid)
108
+ expect(result).to be bid
109
+ end
110
+
111
+ it 'returns nil for a low bid' do
112
+ bid = create(:bid, amount: 15.00, auction: auction)
113
+ result, _ = auction.receive_bid(bid)
114
+ expect(result).to be_falsey
115
+ end
116
+
117
+ it 'knows the highest bidder' do
118
+ bid1 = create(:bid, amount: 26.00, auction: auction, bidder: bidder1)
119
+ auction.receive_bid(bid1)
120
+ auction.reload
121
+ expect(auction.highest_bidder).to eq bidder1
122
+ end
123
+
124
+ it 'handles bids from the same bidder with reserve' do
125
+ auction = create(:auction, {
126
+ starting_price: 25.00,
127
+ reserve_price: 30.00,
128
+ bid_increment: 1.00
129
+ })
130
+ bid1 = create(:bid, amount: 26.00, auction: auction, bidder: bidder1)
131
+ auction.receive_bid(bid1)
132
+ auction.reload
133
+ expect(auction.highest_bidder).to eq bidder1
134
+ expect(auction.highest_bid).to eq bid1
135
+ expect(auction.current_price).to eq 26.00
136
+ expect(auction.bids.count).to eq 1
137
+ expect(auction.visible_bids.count).to eq 1
138
+
139
+ bid2 = create(:bid, amount: 35.00, auction: auction, bidder: bidder1)
140
+ auction.receive_bid(bid2)
141
+ auction.reload
142
+ expect(auction.highest_bidder).to eq bidder1
143
+ expect(auction.current_price).to eq 30.00
144
+ expect(auction.highest_bid).to eq bid2
145
+ expect(auction.bids.count).to eq 3
146
+ expect(auction.visible_bids.count).to eq 2
147
+ end
148
+
149
+ it 'handles bids from the same bidder without reserve' do
150
+ auction = create(:auction, {
151
+ starting_price: 25.00,
152
+ bid_increment: 1.00
153
+ })
154
+ bid1 = create(:bid, amount: 26.00, auction: auction, bidder: bidder1)
155
+ auction.receive_bid(bid1)
156
+ auction.reload
157
+ expect(auction.highest_bidder).to eq bidder1
158
+ expect(auction.highest_bid).to eq bid1
159
+ expect(auction.current_price).to eq 26.00
160
+ expect(auction.bids.count).to eq 1
161
+ expect(auction.visible_bids.count).to eq 1
162
+
163
+ bid2 = create(:bid, amount: 35.00, auction: auction, bidder: bidder1)
164
+ auction.receive_bid(bid2)
165
+ auction.reload
166
+ expect(auction.highest_bidder).to eq bidder1
167
+ expect(auction.current_price).to eq 26.00
168
+ expect(auction.highest_bid).to eq bid2
169
+ expect(auction.bids.count).to eq 2
170
+ expect(auction.visible_bids.count).to eq 1
171
+ end
172
+
173
+ it 'handles bids from the same bidder when undercutting reserve' do
174
+ auction = create(:auction, {
175
+ starting_price: 20.00,
176
+ reserve_price: 40.00,
177
+ bid_increment: 5.00
178
+ })
179
+ bid1 = create(:bid, amount: 25.00, auction: auction, bidder: bidder1)
180
+ auction.receive_bid(bid1)
181
+ auction.reload
182
+ expect(auction.highest_bidder).to eq bidder1
183
+ expect(auction.highest_bid).to eq bid1
184
+ expect(auction.current_price).to eq 25.00
185
+ expect(auction.visible_bids.count).to eq 1
186
+
187
+ bid2 = create(:bid, amount: 35.00, auction: auction, bidder: bidder1)
188
+ auction.receive_bid(bid2)
189
+ auction.reload
190
+ expect(auction.highest_bidder).to eq bidder1
191
+ expect(auction.current_price).to eq 25.00
192
+ expect(auction.highest_bid).to eq bid2
193
+ expect(auction.visible_bids.count).to eq 1
194
+ end
195
+
196
+ it 'knows if reserve has been met' do
197
+ auction = create(:auction, {
198
+ starting_price: 25.00,
199
+ reserve_price: 28.00,
200
+ bid_increment: 1.00
201
+ })
202
+ bid1 = create(:bid, amount: 27.00, auction: auction, bidder: bidder1)
203
+ auction.receive_bid(bid1)
204
+ auction.reload
205
+ expect(auction.current_price).to eq 26.00
206
+ expect(auction.reserve_met?).to be false
207
+
208
+ bid2 = create(:bid, amount: 28.00, auction: auction, bidder: bidder2)
209
+ auction.receive_bid(bid2)
210
+ auction.reload
211
+ expect(auction.current_price).to eq 28.00
212
+ expect(auction.reserve_met?).to be true
213
+ end
214
+
215
+ it 'autobids for a high bid' do
216
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
217
+ auction.receive_bid(bid1)
218
+ auction.reload
219
+ expect(auction.current_price).to eq 26.00
220
+ expect(auction.visible_bids.count).to eq 1
221
+
222
+ bid2 = create(:bid, amount: 28.00, auction: auction, bidder: bidder2)
223
+ auction.receive_bid(bid2)
224
+ auction.reload
225
+ expect(auction.current_price).to eq 29.00
226
+ expect(auction.highest_bidder).to eq bidder1
227
+ expect(auction.visible_bids.count).to eq 3
228
+
229
+ bid3 = create(:bid, amount: 30.00, auction: auction, bidder: bidder2)
230
+ auction.receive_bid(bid3)
231
+ auction.reload
232
+ expect(auction.current_price).to eq 31.00
233
+ expect(auction.highest_bidder).to eq bidder1
234
+ expect(auction.visible_bids.count).to eq 5
235
+
236
+ bid4 = create(:bid, amount: 35.00, auction: auction, bidder: bidder2)
237
+ auction.receive_bid(bid4)
238
+ auction.reload
239
+ expect(auction.current_price).to eq 36.00
240
+ expect(auction.visible_bids.count).to eq 7
241
+ expect(auction.highest_bidder).to eq bidder1
242
+ end
243
+
244
+ it 'shows the last autobid if outbidding it' do
245
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
246
+ auction.receive_bid(bid1)
247
+ auction.reload
248
+ expect(auction.current_price).to eq 26.00
249
+ expect(auction.visible_bids.count).to eq 1
250
+
251
+ bid2 = create(:bid, amount: 60.00, auction: auction, bidder: bidder2)
252
+ auction.receive_bid(bid2)
253
+ auction.reload
254
+ expect(auction.current_price).to eq 51.00
255
+ expect(auction.highest_bidder).to eq bidder2
256
+ expect(auction.visible_bids.count).to eq 3
257
+ expect(auction.visible_bids_in_id_order[0].amount).to eq 50.00
258
+
259
+ bid3 = create(:bid, amount: 80.00, auction: auction, bidder: bidder1)
260
+ auction.receive_bid(bid3)
261
+ auction.reload
262
+ expect(auction.current_price).to eq 61.00
263
+ expect(auction.highest_bidder).to eq bidder1
264
+ expect(auction.visible_bids.count).to eq 5
265
+ expect(auction.visible_bids_in_id_order[-3].amount).to eq 60.00
266
+ end
267
+
268
+ it 'correctly autobids after an autobid tie' do
269
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
270
+ auction.receive_bid(bid1)
271
+ auction.reload
272
+ expect(auction.current_price).to eq 26.00
273
+ expect(auction.visible_bids.count).to eq 1
274
+
275
+ bid2 = create(:bid, amount: 50.00, auction: auction, bidder: bidder2)
276
+ auction.receive_bid(bid2)
277
+ auction.reload
278
+ expect(auction.current_price).to eq 50.00
279
+ expect(auction.highest_bidder).to eq bidder1
280
+ expect(auction.visible_bids.count).to eq 3
281
+
282
+ bid3 = create(:bid, amount: 80.00, auction: auction, bidder: bidder2)
283
+ auction.receive_bid(bid3)
284
+ auction.reload
285
+ expect(auction.current_price).to eq 51.00
286
+ expect(auction.highest_bidder).to eq bidder2
287
+ expect(auction.visible_bids.count).to eq 4
288
+ end
289
+
290
+ it 'autobids for a high bid even when it changes' do
291
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
292
+ auction.receive_bid(bid1)
293
+ auction.reload
294
+ expect(auction.current_price).to eq 26.00
295
+ expect(auction.visible_bids.count).to eq 1
296
+
297
+ bid1 = create(:bid, amount: 80.00, auction: auction, bidder: bidder1)
298
+ auction.receive_bid(bid1)
299
+ auction.reload
300
+ expect(auction.current_price).to eq 26.00
301
+ expect(auction.visible_bids.count).to eq 1
302
+
303
+ bid2 = create(:bid, amount: 28.00, auction: auction, bidder: bidder2)
304
+ auction.receive_bid(bid2)
305
+ auction.reload
306
+ expect(auction.current_price).to eq 29.00
307
+ expect(auction.highest_bidder).to eq bidder1
308
+ expect(auction.visible_bids.count).to eq 3
309
+
310
+ bid3 = create(:bid, amount: 30.00, auction: auction, bidder: bidder2)
311
+ auction.receive_bid(bid3)
312
+ auction.reload
313
+ expect(auction.current_price).to eq 31.00
314
+ expect(auction.highest_bidder).to eq bidder1
315
+ expect(auction.visible_bids.count).to eq 5
316
+
317
+ bid4 = create(:bid, amount: 35.00, auction: auction, bidder: bidder2)
318
+ auction.receive_bid(bid4)
319
+ auction.reload
320
+ expect(auction.current_price).to eq 36.00
321
+ expect(auction.visible_bids.count).to eq 7
322
+ expect(auction.highest_bidder).to eq bidder1
323
+
324
+ bid4 = create(:bid, amount: 75.00, auction: auction, bidder: bidder2)
325
+ auction.receive_bid(bid4)
326
+ auction.reload
327
+ expect(auction.current_price).to eq 76.00
328
+ expect(auction.visible_bids.count).to eq 9
329
+ expect(auction.highest_bidder).to eq bidder1
330
+
331
+ bid5 = create(:bid, amount: 90.00, auction: auction, bidder: bidder2)
332
+ auction.receive_bid(bid5)
333
+ auction.reload
334
+ expect(auction.current_price).to eq 81.00
335
+ expect(auction.visible_bids.count).to eq 11
336
+ expect(auction.highest_bidder).to eq bidder2
337
+ end
338
+
339
+ it 'autobids to the highest autobid amount even if it does not meet bid increment' do
340
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
341
+ auction.receive_bid(bid1)
342
+ auction.reload
343
+ expect(auction.visible_bids.count).to eq 1
344
+ expect(auction.current_price).to eq 26.00
345
+
346
+ bid2 = create(:bid, amount: 49.50, auction: auction, bidder: bidder2)
347
+ auction.receive_bid(bid2)
348
+ auction.reload
349
+ expect(auction.current_price).to eq 50.00
350
+ expect(auction.visible_bids.count).to eq 3
351
+ expect(auction.highest_bidder).to eq bidder1
352
+ end
353
+
354
+ it 'autobids above the highest invisible bid' do
355
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
356
+ auction.receive_bid(bid1)
357
+ auction.reload
358
+ expect(auction.current_price).to eq 26.00
359
+
360
+ bid2 = create(:bid, amount: 55.00, auction: auction, bidder: bidder2)
361
+ auction.receive_bid(bid2)
362
+ auction.reload
363
+ expect(auction.current_price).to eq 51.00
364
+ expect(auction.visible_bids.count).to eq 3
365
+ expect(auction.highest_bidder).to eq bidder2
366
+ end
367
+
368
+ it 'autobids above the highest invisible bid but not exceeding bidders amount' do
369
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
370
+ auction.receive_bid(bid1)
371
+ auction.reload
372
+ expect(auction.visible_bids.count).to eq 1
373
+ expect(auction.current_price).to eq 26.00
374
+
375
+ bid2 = create(:bid, amount: 50.50, auction: auction, bidder: bidder2)
376
+ auction.receive_bid(bid2)
377
+ auction.reload
378
+ expect(auction.current_price).to eq 50.50
379
+ expect(auction.visible_bids.count).to eq 3
380
+ expect(auction.highest_bidder).to eq bidder2
381
+ end
382
+
383
+ it 'favors the initial bidder in the event of an autobid tie' do
384
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
385
+ auction.receive_bid(bid1)
386
+ auction.reload
387
+ expect(auction.current_price).to eq 26.00
388
+ expect(auction.highest_bidder).to eq bidder1
389
+
390
+ bid2 = create(:bid, amount: 50.00, auction: auction, bidder: bidder2)
391
+ auction.receive_bid(bid2)
392
+ auction.reload
393
+ expect(auction.current_price).to eq 50.00
394
+ expect(auction.visible_bids.count).to eq 3
395
+ expect(auction.highest_bidder).to eq bidder1
396
+ end
397
+
398
+ it 'sets highest autobid amount to reserve price if there is one' do
399
+ auction = create(:auction, {
400
+ starting_price: 25.00,
401
+ reserve_price: 30.00,
402
+ bid_increment: 1.00
403
+ })
404
+
405
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
406
+ auction.receive_bid(bid1)
407
+ auction.reload
408
+ expect(auction.current_price).to eq 30.00
409
+ expect(auction.visible_bids.count).to eq 1
410
+ expect(auction.reserve_met?).to be true
411
+ end
412
+
413
+ it 'jumps to reserve price if later autobids are higher' do
414
+ auction = create(:auction, {
415
+ starting_price: 25.00,
416
+ reserve_price: 35.00,
417
+ bid_increment: 1.00
418
+ })
419
+
420
+ bid1 = create(:bid, amount: 30.00, auction: auction, bidder: bidder1)
421
+ auction.receive_bid(bid1)
422
+ auction.reload
423
+ expect(auction.current_price).to eq 26.00
424
+ expect(auction.visible_bids.count).to eq 1
425
+ expect(auction.reserve_met?).to be false
426
+ expect(auction.highest_bidder).to eq bidder1
427
+
428
+ bid2 = create(:bid, amount: 31.00, auction: auction, bidder: bidder2)
429
+ auction.receive_bid(bid2)
430
+ auction.reload
431
+ expect(auction.current_price).to eq 31.00
432
+ expect(auction.visible_bids.count).to eq 3
433
+ expect(auction.reserve_met?).to be false
434
+ expect(auction.highest_bidder).to eq bidder2
435
+
436
+ bid3 = create(:bid, amount: 40.00, auction: auction, bidder: bidder1)
437
+ auction.receive_bid(bid3)
438
+ auction.reload
439
+ expect(auction.current_price).to eq 35.00
440
+ expect(auction.visible_bids.count).to eq 4
441
+ expect(auction.reserve_met?).to be true
442
+ expect(auction.highest_bidder).to eq bidder1
443
+ end
444
+
445
+ it 'creates new bids for each autobid' do
446
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
447
+ auction.receive_bid(bid1)
448
+ auction.reload
449
+ expect(auction.bids.count).to eq 2
450
+ expect(auction.visible_bids.count).to eq 1
451
+
452
+ bid2 = create(:bid, amount: 28.00, auction: auction, bidder: bidder2)
453
+ auction.receive_bid(bid2)
454
+ auction.reload
455
+ expect(auction.bids.count).to eq 4
456
+ expect(auction.visible_bids.count).to eq 3
457
+ end
458
+
459
+ it 'manages a series of simple bids in the correct manner' do
460
+ bid1 = create(:bid, amount: 26.00, auction: auction, bidder: bidder1)
461
+ auction.receive_bid(bid1)
462
+ auction.reload
463
+ expect(auction.current_price).to eq 26.00
464
+ expect(auction.highest_bidder).to eq bidder1
465
+ expect(auction.bids.count).to eq 1
466
+ expect(auction.visible_bids.count).to eq 1
467
+
468
+ bid2 = create(:bid, amount: 27.00, auction: auction, bidder: bidder2)
469
+ auction.receive_bid(bid2)
470
+ auction.reload
471
+ expect(auction.current_price).to eq 27.00
472
+ expect(auction.highest_bidder).to eq bidder2
473
+ expect(auction.bids.count).to eq 2
474
+ expect(auction.visible_bids.count).to eq 2
475
+
476
+ bid3 = create(:bid, amount: 28.00, auction: auction, bidder: bidder3)
477
+ auction.receive_bid(bid3)
478
+ auction.reload
479
+ expect(auction.current_price).to eq 28.00
480
+ expect(auction.highest_bidder).to eq bidder3
481
+ expect(auction.bids.count).to eq 3
482
+ expect(auction.visible_bids.count).to eq 3
483
+ end
484
+
485
+ it 'manages a series of bids in the correct manner' do
486
+ bid1 = create(:bid, amount: 27.00, auction: auction, bidder: bidder1)
487
+ auction.receive_bid(bid1)
488
+ auction.reload
489
+ expect(auction.current_price).to eq 26.00
490
+ expect(auction.highest_bidder).to eq bidder1
491
+ expect(auction.bids.count).to eq 2
492
+ expect(auction.visible_bids.count).to eq 1
493
+
494
+ bid2 = create(:bid, amount: 30.00, auction: auction, bidder: bidder2)
495
+ auction.receive_bid(bid2)
496
+ auction.reload
497
+ expect(auction.current_price).to eq 28.00
498
+ expect(auction.highest_bidder).to eq bidder2
499
+ expect(auction.bids.count).to eq 4
500
+ expect(auction.visible_bids.count).to eq 3
501
+
502
+ bid3 = create(:bid, amount: 35.00, auction: auction, bidder: bidder3)
503
+ auction.receive_bid(bid3)
504
+ auction.reload
505
+ expect(auction.current_price).to eq 31.00
506
+ expect(auction.highest_bidder).to eq bidder3
507
+ expect(auction.bids.count).to eq 6
508
+ expect(auction.visible_bids.count).to eq 5
509
+
510
+ bid4 = create(:bid, amount: 35.00, auction: auction, bidder: bidder4)
511
+ auction.receive_bid(bid4)
512
+ auction.reload
513
+ expect(auction.current_price).to eq 35.00
514
+ expect(auction.highest_bidder).to eq bidder3
515
+ expect(auction.bids.count).to eq 7
516
+ expect(auction.visible_bids.count).to eq 7
517
+
518
+ bid5 = create(:bid, amount: 36.00, auction: auction, bidder: bidder1)
519
+ auction.receive_bid(bid5)
520
+ auction.reload
521
+ expect(auction.current_price).to eq 36.00
522
+ expect(auction.highest_bidder).to eq bidder1
523
+ expect(auction.bids.count).to eq 8
524
+ expect(auction.visible_bids.count).to eq 8
525
+ end
526
+
527
+ it 'returns an ordered list of visible bids' do
528
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
529
+ auction.receive_bid(bid1)
530
+
531
+ bid2 = create(:bid, amount: 29.00, auction: auction, bidder: bidder2)
532
+ auction.receive_bid(bid2)
533
+
534
+ bid3 = create(:bid, amount: 31.00, auction: auction, bidder: bidder2)
535
+ auction.receive_bid(bid3)
536
+
537
+ expect(auction.visible_bids_in_chron_order.count).to eq 5
538
+ end
539
+
540
+ it 'does not autobid above a bidders own autobids' do
541
+ bid1 = create(:bid, amount: 50.00, auction: auction, bidder: bidder1)
542
+ auction.receive_bid(bid1)
543
+ auction.reload
544
+ expect(auction.current_price).to eq 26.00
545
+ expect(auction.highest_bid).to eq bid1
546
+ expect(auction.visible_bids.count).to eq 1
547
+
548
+ bid2 = create(:bid, amount: 28.00, auction: auction, bidder: bidder1)
549
+ auction.receive_bid(bid2)
550
+ auction.reload
551
+ expect(auction.current_price).to eq 26.00
552
+ expect(auction.highest_bidder).to eq bidder1
553
+ expect(auction.highest_bid).to eq bid1
554
+ expect(auction.visible_bids.count).to eq 1
555
+
556
+ bid3 = create(:bid, amount: 30.00, auction: auction, bidder: bidder1)
557
+ auction.receive_bid(bid3)
558
+ auction.reload
559
+ expect(auction.current_price).to eq 26.00
560
+ expect(auction.highest_bidder).to eq bidder1
561
+ expect(auction.highest_bid).to eq bid1
562
+ expect(auction.visible_bids.count).to eq 1
563
+
564
+ bid4 = create(:bid, amount: 55.00, auction: auction, bidder: bidder1)
565
+ auction.receive_bid(bid4)
566
+ auction.reload
567
+ expect(auction.current_price).to eq 26.00
568
+ expect(auction.highest_bidder).to eq bidder1
569
+ expect(auction.highest_bid).to eq bid4
570
+ expect(auction.visible_bids.count).to eq 1
571
+ end
572
+ end
573
+
574
+ context 'handles time' do
575
+ let(:starting_datetime) { Time.now }
576
+ let(:planned_end_datetime) { Time.now + 10.days + 5.minutes }
577
+ let(:bidder) { create(:user) }
578
+ let(:bidder1) { create(:user) }
579
+ let(:bidder2) { create(:user) }
580
+ let(:bidder3) { create(:user) }
581
+ let!(:auction) {
582
+ create(:auction, {
583
+ starting_datetime: starting_datetime,
584
+ planned_end_datetime: planned_end_datetime,
585
+ time_increment: 60,
586
+ highest_bidder: bidder,
587
+ reserve_price: 30,
588
+ countdown: 300
589
+ })}
590
+
591
+ it 'has the same planned and current end datetime in the default case' do
592
+ starting_datetime = Time.now
593
+ planned_end_datetime = Time.now + 1.minute
594
+
595
+ auction = create(:auction, {
596
+ starting_datetime: starting_datetime,
597
+ planned_end_datetime: planned_end_datetime
598
+ })
599
+
600
+ expect(auction.planned_end_datetime).to eq auction.current_end_datetime
601
+ end
602
+
603
+ it 'knows if it has started' do
604
+ starting_datetime = Time.now
605
+
606
+ auction = create(:auction, {
607
+ starting_datetime: starting_datetime
608
+ })
609
+
610
+ expect(auction.started?).to be true
611
+ end
612
+
613
+ it 'knows it is ending soon' do
614
+ starting_datetime = Time.now
615
+ planned_end_datetime = Time.now + 59.minutes
616
+
617
+ auction = create(:auction, {
618
+ starting_datetime: starting_datetime,
619
+ planned_end_datetime: planned_end_datetime
620
+ })
621
+
622
+ expect(auction.ending_soon?).to be true
623
+ end
624
+
625
+ it 'knows it is not ending soon' do
626
+ starting_datetime = Time.now
627
+ planned_end_datetime = Time.now + 1.hour + 5.minutes
628
+
629
+ auction = create(:auction, {
630
+ starting_datetime: starting_datetime,
631
+ planned_end_datetime: planned_end_datetime
632
+ })
633
+
634
+ expect(auction.ending_soon?).to be false
635
+ end
636
+
637
+ it 'does not have a countdown by default' do
638
+ starting_datetime = Time.now
639
+ planned_end_datetime = Time.now + 59.seconds
640
+ auction = create(:auction, {
641
+ starting_datetime: starting_datetime,
642
+ planned_end_datetime: planned_end_datetime
643
+ })
644
+
645
+ expect(auction.in_countdown?).to be false
646
+ end
647
+
648
+ it 'knows it is in countdown mode' do
649
+ starting_datetime = Time.now
650
+ planned_end_datetime = Time.now + 59.seconds
651
+ one_minute_countdown = 60
652
+ auction = create(:auction, {
653
+ starting_datetime: starting_datetime,
654
+ planned_end_datetime: planned_end_datetime,
655
+ countdown: one_minute_countdown
656
+ })
657
+
658
+ expect(auction.in_countdown?).to be true
659
+ end
660
+
661
+ it 'knows when it is complete' do
662
+ starting_datetime = Time.now
663
+ planned_end_datetime = Time.now + 1.seconds
664
+ auction = create(:auction, {
665
+ starting_datetime: starting_datetime,
666
+ planned_end_datetime: planned_end_datetime,
667
+ highest_bidder: bidder
668
+ })
669
+ sleep 2
670
+ auction.reload
671
+ expect(auction.complete).to be true
672
+ end
673
+
674
+ it 'adds the item to the winning bidders cart' do
675
+ bid = create(:bid, amount: 30.00, auction: auction, bidder: bidder)
676
+ auction.receive_bid(bid)
677
+ auction.update(current_end_datetime: Time.now - 1.second)
678
+ auction.reload
679
+ expect(bidder.last_incomplete_spree_order.line_items.count).to eq 1
680
+ expect(bidder.last_incomplete_spree_order.line_items.last.price).to eq 30.00
681
+ expect(bidder.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
682
+ end
683
+
684
+ it 'does not add the item to the winning bidders cart if reserve not met' do
685
+ bid = create(:bid, amount: 26.00, auction: auction, bidder: bidder)
686
+ auction.receive_bid(bid)
687
+ auction.update(current_end_datetime: Time.now - 1.second)
688
+ auction.reload
689
+ expect(auction.reserve_met?).to be false
690
+ expect(bidder.last_incomplete_spree_order).to be nil
691
+ end
692
+
693
+ it 'awards the auction to the next highest bidder if not paid within checkout deadline' do
694
+ starting_datetime = Time.now
695
+ planned_end_datetime = Time.now + 5.minutes
696
+ auction = create(:auction, {
697
+ starting_datetime: starting_datetime,
698
+ planned_end_datetime: planned_end_datetime
699
+ })
700
+ bid1 = create(:bid, amount: 27.00, auction: auction, bidder: bidder1)
701
+ auction.receive_bid(bid1)
702
+
703
+ bid2 = create(:bid, amount: 30.00, auction: auction, bidder: bidder2)
704
+ auction.receive_bid(bid2)
705
+
706
+ bid3 = create(:bid, amount: 35.00, auction: auction, bidder: bidder3)
707
+ auction.receive_bid(bid3)
708
+
709
+ current_end_datetime = Time.now
710
+ auction.update(current_end_datetime: current_end_datetime)
711
+ auction.reload
712
+
713
+ expect(bidder3.last_incomplete_spree_order.line_items.count).to eq 1
714
+ expect(bidder3.last_incomplete_spree_order.line_items.last.price).to eq 31.00
715
+ expect(bidder3.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
716
+
717
+ current_end_datetime = Time.now - (1.day + 5.minutes)
718
+ auction.update(current_end_datetime: current_end_datetime)
719
+ auction.reload
720
+
721
+ expect(bidder3.last_incomplete_spree_order.line_items.empty?).to be true
722
+ expect(bidder2.last_incomplete_spree_order.line_items.last.price).to eq 30.00
723
+ expect(bidder2.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
724
+
725
+ current_end_datetime = Time.now - (1.day + 5.minutes)
726
+ auction.update(current_end_datetime: current_end_datetime)
727
+ auction.reload
728
+
729
+ expect(bidder2.last_incomplete_spree_order.line_items.empty?).to be true
730
+ expect(bidder1.last_incomplete_spree_order.line_items.last.price).to eq 27.00
731
+ expect(bidder1.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
732
+ end
733
+
734
+ it 'awards the auction to nobody if not paid within checkout deadline' do
735
+ starting_datetime = Time.now
736
+ planned_end_datetime = Time.now + 5.minutes
737
+ auction = create(:auction, {
738
+ starting_datetime: starting_datetime,
739
+ planned_end_datetime: planned_end_datetime
740
+ })
741
+ bid1 = create(:bid, amount: 27.00, auction: auction, bidder: bidder1)
742
+ auction.receive_bid(bid1)
743
+
744
+ current_end_datetime = Time.now
745
+ auction.update(current_end_datetime: current_end_datetime)
746
+ auction.reload
747
+
748
+ expect(bidder1.last_incomplete_spree_order.line_items.count).to eq 1
749
+ expect(bidder1.last_incomplete_spree_order.line_items.last.price).to eq 26.00
750
+ expect(bidder1.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
751
+
752
+ current_end_datetime = Time.now - (1.day + 5.minutes)
753
+ auction.update(current_end_datetime: current_end_datetime)
754
+ auction.reload
755
+
756
+ expect(bidder1.last_incomplete_spree_order.line_items.empty?).to be true
757
+ expect(auction.won?).to be_falsey
758
+ end
759
+
760
+ it 'awards the auction to the highest bidder paid within checkout deadline' do
761
+ starting_datetime = Time.now
762
+ planned_end_datetime = Time.now + 5.minutes
763
+ store = create(:store)
764
+ auction = create(:auction, {
765
+ starting_datetime: starting_datetime,
766
+ planned_end_datetime: planned_end_datetime
767
+ })
768
+ bid1 = create(:bid, amount: 27.00, auction: auction, bidder: bidder1)
769
+ auction.receive_bid(bid1)
770
+
771
+ bid2 = create(:bid, amount: 30.00, auction: auction, bidder: bidder2)
772
+ auction.receive_bid(bid2)
773
+
774
+ bid3 = create(:bid, amount: 35.00, auction: auction, bidder: bidder3)
775
+ auction.receive_bid(bid3)
776
+
777
+ current_end_datetime = Time.now
778
+ auction.update(current_end_datetime: current_end_datetime)
779
+ auction.reload
780
+
781
+ expect(bidder3.last_incomplete_spree_order.line_items.count).to eq 1
782
+ expect(bidder3.last_incomplete_spree_order.line_items.last.price).to eq 31.00
783
+ expect(bidder3.last_incomplete_spree_order.line_items.last.variant).to eq auction.variant
784
+
785
+ order = bidder3.last_incomplete_spree_order
786
+ order.update(store: store, state: "complete")
787
+ expect(auction.order_complete?).to be true
788
+
789
+ current_end_datetime = Time.now - (1.day + 5.minutes)
790
+ auction.update(current_end_datetime: current_end_datetime)
791
+
792
+ expect(auction.highest_bidder).to eq bidder3
793
+ expect(auction.current_price).to eq 31.00
794
+ expect(auction.won?).to be true
795
+ end
796
+
797
+ it 'does not allow any more bidding once complete' do
798
+ starting_datetime = Time.now
799
+ planned_end_datetime = Time.now + 1.seconds
800
+ auction = create(:auction, {
801
+ starting_datetime: starting_datetime,
802
+ planned_end_datetime: planned_end_datetime,
803
+ highest_bidder: bidder
804
+ })
805
+ sleep 2
806
+ auction.reload
807
+ bid = create(:bid, amount: 26.00, auction: auction)
808
+ bid_result, _ = auction.receive_bid(bid)
809
+
810
+ expect(bid_result).to be_falsey
811
+ expect(bid.accepted).to be false
812
+ end
813
+
814
+ it 'increases current endtime with more bids inside a countdown' do
815
+ starting_datetime = Time.now
816
+ planned_end_datetime = Time.now + 59.seconds
817
+ time_increment = 60
818
+ one_minute_countdown = 60
819
+ auction = create(:auction, {
820
+ starting_price: 25.00,
821
+ starting_datetime: starting_datetime,
822
+ planned_end_datetime: planned_end_datetime,
823
+ time_increment: time_increment,
824
+ countdown: one_minute_countdown
825
+ })
826
+
827
+ bid1 = create(:bid, amount: 26.00, auction: auction)
828
+ auction.receive_bid(bid1)
829
+ auction.reload
830
+ auction_end_difference = (auction.current_end_datetime - auction.planned_end_datetime).seconds
831
+ expect(auction_end_difference).to be >= 59.seconds
832
+ end
833
+ end
834
+ end