mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 08:50:58 -05:00
Added test for parallel subscription redeems, more informative redeem error messages, more robust prepaid update when redeeming
This commit is contained in:
parent
9c7345fed0
commit
d09711be37
2 changed files with 90 additions and 49 deletions
|
@ -196,7 +196,7 @@ class SubscriptionHandler extends Handler
|
|||
@sendSuccess(res, user)
|
||||
|
||||
subscribeWithPrepaidCode: (req, res) ->
|
||||
return @sendForbiddenError(res) unless req.user?
|
||||
return @sendUnauthorizedError(res) unless req.user?
|
||||
return @sendBadInputError(res,"You must provide a valid prepaid code") unless req.body?.ppc
|
||||
|
||||
# Check if code exists and has room for more redeemers
|
||||
|
@ -206,14 +206,30 @@ class SubscriptionHandler extends Handler
|
|||
return @sendDatabaseError(res, err)
|
||||
unless prepaid
|
||||
@logSubscriptionError(req.user, "Could not find prepaid code #{req.body.ppc}")
|
||||
return @sendForbiddenError(res)
|
||||
return @sendNotFoundError(res, "Prepaid not found")
|
||||
|
||||
oldRedeemers = prepaid.get('redeemers') ? []
|
||||
return @sendForbiddenError(res) if oldRedeemers.length >= prepaid.get('maxRedeemers')
|
||||
return @sendError(res, 403, "Too many redeemers") if oldRedeemers.length >= prepaid.get('maxRedeemers')
|
||||
months = parseInt(prepaid.get('properties')?.months)
|
||||
return @sendForbiddenError(res) if isNaN(months) or months < 1
|
||||
return @sendBadInputError(res, "Bad months") if isNaN(months) or months < 1
|
||||
for redeemer in oldRedeemers
|
||||
return @sendForbiddenError(res) if redeemer.userID.equals(req.user._id)
|
||||
return @sendError(res, 403, "User already redeemed") if redeemer.userID.equals(req.user._id)
|
||||
|
||||
@redeemPrepaidCode(req, res, months)
|
||||
|
||||
redeemPrepaidCode: (req, res, months) =>
|
||||
return @sendUnauthorizedError(res) unless req.user?
|
||||
return @sendForbiddenError(res) unless req.body?.ppc
|
||||
return @sendForbiddenError(res) if isNaN(months) or months < 1
|
||||
|
||||
newRedeemerPush = { $push: { redeemers : { date: new Date(), userID: req.user._id } }}
|
||||
|
||||
Prepaid.update { 'code': req.body.ppc, 'redeemers.userID': { $ne: req.user._id }, '$where': 'this.redeemers.length < this.maxRedeemers'}, newRedeemerPush, (err, num, info) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Subscribe with Prepaid Code update: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
|
||||
return @sendError(res, 403, "Can't add user to prepaid redeemers") if num isnt 1
|
||||
|
||||
customerID = req.user.get('stripe')?.customerID
|
||||
subscriptionID = req.user.get('stripe')?.subscriptionID
|
||||
|
@ -224,51 +240,32 @@ class SubscriptionHandler extends Handler
|
|||
if err
|
||||
@logSubscriptionError(user, "Redeem Prepaid Code Stripe cancel subscription error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@redeemPrepaidCode(req, res, oldRedeemers, months, stripeSubscriptionPeriodEndDate)
|
||||
|
||||
redeemPrepaidCode: (req, res, oldRedeemers, months, startDate=null) =>
|
||||
return @sendForbiddenError(res) unless req.user?
|
||||
return @sendForbiddenError(res) unless req.body?.ppc
|
||||
return @sendForbiddenError(res) unless oldRedeemers
|
||||
return @sendForbiddenError(res) if isNaN(months) or months < 1
|
||||
# Add terminal subscription to User, extending existing subscriptions
|
||||
# TODO: refactor this into some form useable by both this and purchaseYearSale
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new moment()
|
||||
if stripeSubscriptionPeriodEndDate
|
||||
endDate = new moment(stripeSubscriptionPeriodEndDate)
|
||||
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
|
||||
endDate = new moment(stripeInfo.free)
|
||||
|
||||
newRedeemerPush = { $push: { redeemers : { date: new Date().toISOString(), userID: req.user._id } }}
|
||||
endDate = endDate.add(months, 'months')
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Only update the prepaid document if the length of the redeemers array hasn't changed in the db.
|
||||
# This will probably fail if redeemers isn't defined. new terminal_subscriptions created should be sure to set the redeemers array
|
||||
# TODO: find a better way?
|
||||
Prepaid.update { 'code': req.body.ppc, 'redeemers': { $size: oldRedeemers.length }}, newRedeemerPush, (err, num, info) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "Subscribe with Prepaid Code update: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
# Add gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += subscriptions.basic.gems * months
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
return @sendNotFoundError(res, "Error while updating prepaid redeemer") if num isnt 1
|
||||
|
||||
# Add terminal subscription to User, extending existing subscriptions
|
||||
# TODO: refactor this into some form useable by both this and purchaseYearSale
|
||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||
endDate = new moment()
|
||||
if startDate
|
||||
endDate = new moment(startDate)
|
||||
else if _.isString(stripeInfo.free) and new moment().isBefore(new moment(stripeInfo.free))
|
||||
endDate = new moment(stripeInfo.free)
|
||||
|
||||
endDate = endDate.add(months, 'months')
|
||||
stripeInfo.free = endDate.toISOString().substring(0, 10)
|
||||
req.user.set('stripe', stripeInfo)
|
||||
|
||||
# Add gems to User
|
||||
purchased = _.clone(req.user.get('purchased'))
|
||||
purchased ?= {}
|
||||
purchased.gems ?= 0
|
||||
purchased.gems += subscriptions.basic.gems * months
|
||||
req.user.set('purchased', purchased)
|
||||
|
||||
req.user.save (err, user) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendSuccess(res, user)
|
||||
req.user.save (err, user) =>
|
||||
if err
|
||||
@logSubscriptionError(req.user, "User save error: #{JSON.stringify(err)}")
|
||||
return @sendDatabaseError(res, err)
|
||||
@sendSuccess(res, user)
|
||||
|
||||
subscribeUser: (req, user, done) ->
|
||||
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
||||
|
|
|
@ -2,6 +2,7 @@ require '../common'
|
|||
config = require '../../../server_config'
|
||||
moment = require 'moment'
|
||||
{findStripeSubscription} = require '../../../server/lib/utils'
|
||||
async = require 'async'
|
||||
|
||||
describe '/db/prepaid', ->
|
||||
prepaidURL = getURL('/db/prepaid')
|
||||
|
@ -320,7 +321,6 @@ describe '/db/prepaid', ->
|
|||
expect(payments[0].get('amount')).toEqual(8991)
|
||||
done()
|
||||
|
||||
|
||||
it 'Anonymous cant redeem a prepaid code', (done) ->
|
||||
logoutUser () ->
|
||||
subscribeWithPrepaid joeCode, (err, res) ->
|
||||
|
@ -332,7 +332,7 @@ describe '/db/prepaid', ->
|
|||
loginJoe (joe) ->
|
||||
subscribeWithPrepaid 'abc123', (err, res) ->
|
||||
expect(err).toBeNull()
|
||||
expect(res.statusCode).toEqual(403)
|
||||
expect(res.statusCode).toEqual(404)
|
||||
done()
|
||||
|
||||
it 'User cant redeem empty code', (done) ->
|
||||
|
@ -463,4 +463,48 @@ describe '/db/prepaid', ->
|
|||
expect(err).toBeNull()
|
||||
expect(res.statusCode).not.toEqual(200)
|
||||
done()
|
||||
# TODO: add a bunch of parallel tests trying to redeem a code with a high maxRedeemers (50?) to see what happens
|
||||
|
||||
it 'Test a bunch of people trying to redeem at once', (done) ->
|
||||
doRedeem = (userX, code, testnum, retry, fnDone) =>
|
||||
loginUser userX, () =>
|
||||
endDate = new moment().add(3, 'months').toISOString().substring(0, 10)
|
||||
subscribeWithPrepaid code, (err, res, result) ->
|
||||
if err
|
||||
return fnDone(err)
|
||||
|
||||
expect(err).toBeNull()
|
||||
expect(result).toBeDefined()
|
||||
if result.stripe
|
||||
expect(result.stripe).toBeDefined()
|
||||
expect(result.stripe.free).toEqual(endDate)
|
||||
expect(result?.purchased?.gems).toEqual(10500)
|
||||
return fnDone(null, {status: "ok", msg: "Redeemed " + retry})
|
||||
else
|
||||
return fnDone(null, {status: 'error', msg: "Redeem attempt Error #{result} (#{userX.id})" + retry })
|
||||
|
||||
redeemPrepaidFn = (code, testnum) =>
|
||||
(fnDone) =>
|
||||
loginNewUser (user1) =>
|
||||
doRedeem(user1, code, testnum, 0, fnDone)
|
||||
|
||||
stripe.tokens.create {
|
||||
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||
}, (err, token) ->
|
||||
loginNewUser (user) =>
|
||||
codeRedeemers = 50
|
||||
codeMonths = 3
|
||||
redeemers = 51
|
||||
purchasePrepaid 'terminal_subscription', months: codeMonths, codeRedeemers, token.id, (err, res, prepaid) ->
|
||||
expect(err).toBeNull()
|
||||
expect(prepaid).toBeDefined()
|
||||
expect(prepaid.code).toBeDefined()
|
||||
tasks = (redeemPrepaidFn(prepaid.code, i) for i in [0...redeemers])
|
||||
async.parallel tasks, (err, results) =>
|
||||
redeemed = 0
|
||||
error = 0
|
||||
for result in results
|
||||
redeemed += 1 if result.status is 'ok'
|
||||
error += 1 if result.status is 'error'
|
||||
expect(redeemed).toEqual(codeRedeemers)
|
||||
expect(error).toEqual(redeemers - codeRedeemers)
|
||||
done()
|
||||
|
|
Loading…
Reference in a new issue