Merged in develop

This commit is contained in:
Keith Holliday
2017-06-27 22:23:13 -06:00
897 changed files with 50434 additions and 41464 deletions
@@ -525,6 +525,7 @@ describe('Amazon Payments', () => {
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
expectAmazonStubs();
});
@@ -555,6 +556,7 @@ describe('Amazon Payments', () => {
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
amzLib.closeBillingAgreement.restore();
});
@@ -593,6 +595,7 @@ describe('Amazon Payments', () => {
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
expectAmazonStubs();
});
@@ -623,6 +626,7 @@ describe('Amazon Payments', () => {
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
amzLib.closeBillingAgreement.restore();
});
+7
View File
@@ -79,6 +79,13 @@ describe('cron', () => {
expect(user.purchased.plan.gemsBought).to.equal(0);
});
it('resets plan.gemsBought on a new month if user does not have purchased.plan.dateUpdated', () => {
user.purchased.plan.gemsBought = 10;
user.purchased.plan.dateUpdated = undefined;
cron({user, tasksByType, daysMissed, analytics});
expect(user.purchased.plan.gemsBought).to.equal(0);
});
it('does not reset plan.gemsBought within the month', () => {
let clock = sinon.useFakeTimers(moment().startOf('month').add(2, 'days').unix());
user.purchased.plan.dateUpdated = moment().startOf('month').toDate();
+40
View File
@@ -16,6 +16,7 @@ describe('payments/index', () => {
beforeEach(async () => {
user = new User();
user.profile.name = 'sender';
await user.save();
group = generateGroup({
name: 'test group',
@@ -504,6 +505,18 @@ describe('payments/index', () => {
expect(daysTillTermination).to.be.within(13, 15);
});
it('terminates at next billing date even if dateUpdated is prior to now', async () => {
data.nextBill = moment().add({ days: 15 });
data.user.purchased.plan.dateUpdated = moment().subtract({ days: 10 });
await api.cancelSubscription(data);
let now = new Date();
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
expect(daysTillTermination).to.be.within(13, 15);
});
it('resets plan.extraMonths', async () => {
user.purchased.plan.extraMonths = 5;
@@ -653,5 +666,32 @@ describe('payments/index', () => {
expect(updatedUser.items.pets['Jackalope-RoyalPurple']).to.eql(5);
});
it('saves previously unused Mystery Items and Hourglasses for an expired subscription', async () => {
let planExpirationDate = new Date();
planExpirationDate.setDate(planExpirationDate.getDate() - 2);
let mysteryItem = 'item';
let mysteryItems = [mysteryItem];
let consecutive = {
trinkets: 3,
};
// set expired plan with unused items
plan.mysteryItems = mysteryItems;
plan.consecutive = consecutive;
plan.dateCreated = planExpirationDate;
plan.dateTerminated = planExpirationDate;
plan.customerId = null;
user.purchased.plan = plan;
await user.save();
await api.addSubToGroupUser(user, group);
let updatedUser = await User.findById(user._id).exec();
expect(updatedUser.purchased.plan.mysteryItems[0]).to.eql(mysteryItem);
expect(updatedUser.purchased.plan.consecutive.trinkets).to.equal(consecutive.trinkets);
});
});
});
@@ -138,7 +138,7 @@ describe('Canceling a subscription for group', () => {
]);
});
it('prevents non group leader from manging subscription', async () => {
it('prevents non group leader from managing subscription', async () => {
let groupMember = new User();
data.user = groupMember;
data.groupId = group._id;
@@ -1,5 +1,6 @@
import moment from 'moment';
import stripeModule from 'stripe';
import nconf from 'nconf';
import * as sender from '../../../../../../../website/server/libs/email';
import * as api from '../../../../../../../website/server/libs/payments';
@@ -12,17 +13,24 @@ import {
generateGroup,
} from '../../../../../../helpers/api-unit.helper.js';
describe('Purchasing a subscription for group', () => {
describe('Purchasing a group plan for group', () => {
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE = 'Google_subscription';
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS = 'iOS_subscription';
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL = 'normal_subscription';
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE = 'no_subscription';
let plan, group, user, data;
let stripe = stripeModule('test');
let groupLeaderName = 'sender';
let groupName = 'test group';
beforeEach(async () => {
user = new User();
user.profile.name = 'sender';
user.profile.name = groupLeaderName;
await user.save();
group = generateGroup({
name: 'test group',
name: groupName,
type: 'guild',
privacy: 'public',
leader: user._id,
@@ -81,7 +89,7 @@ describe('Purchasing a subscription for group', () => {
sender.sendTxn.restore();
});
it('creates a subscription', async () => {
it('creates a group plan', async () => {
expect(group.purchased.plan.planId).to.not.exist;
data.groupId = group._id;
@@ -157,7 +165,7 @@ describe('Purchasing a subscription for group', () => {
expect(updatedLeader.items.mounts['Jackalope-RoyalPurple']).to.be.true;
});
it('sends an email to members of group', async () => {
it('sends an email to member of group who was not a subscriber', async () => {
let recipient = new User();
recipient.profile.name = 'recipient';
recipient.guilds.push(group._id);
@@ -169,11 +177,181 @@ describe('Purchasing a subscription for group', () => {
expect(sender.sendTxn).to.be.calledTwice;
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-joining');
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
expect(sender.sendTxn.firstCall.args[2]).to.eql([
{name: 'LEADER', content: user.profile.name},
{name: 'GROUP_NAME', content: group.name},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE},
]);
// confirm that the other email sent is appropriate:
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
expect(sender.sendTxn.secondCall.args[1]).to.equal('group-subscription-begins');
});
it('sends one email to subscribed member of group, stating subscription is cancelled (Stripe)', async () => {
let recipient = new User();
recipient.profile.name = 'recipient';
plan.key = 'basic_earned';
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
recipient.purchased.plan = plan;
recipient.guilds.push(group._id);
await recipient.save();
data.groupId = group._id;
await api.createSubscription(data);
expect(sender.sendTxn).to.be.calledTwice;
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
expect(sender.sendTxn.firstCall.args[2]).to.eql([
{name: 'LEADER', content: user.profile.name},
{name: 'GROUP_NAME', content: group.name},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
]);
// confirm that the other email sent is not a cancel-subscription email:
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
expect(sender.sendTxn.secondCall.args[1]).to.equal('group-subscription-begins');
});
it('sends one email to subscribed member of group, stating subscription is cancelled (Amazon)', async () => {
sinon.stub(amzLib, 'getBillingAgreementDetails')
.returnsPromise()
.resolves({
BillingAgreementDetails: {
BillingAgreementStatus: {State: 'Closed'},
},
});
let recipient = new User();
recipient.profile.name = 'recipient';
plan.planId = 'basic_earned';
plan.paymentMethod = amzLib.constants.PAYMENT_METHOD;
recipient.purchased.plan = plan;
recipient.guilds.push(group._id);
await recipient.save();
data.groupId = group._id;
await api.createSubscription(data);
expect(sender.sendTxn).to.be.calledTwice;
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
expect(sender.sendTxn.firstCall.args[2]).to.eql([
{name: 'LEADER', content: user.profile.name},
{name: 'GROUP_NAME', content: group.name},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
]);
// confirm that the other email sent is not a cancel-subscription email:
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
expect(sender.sendTxn.secondCall.args[1]).to.equal('group-subscription-begins');
amzLib.getBillingAgreementDetails.restore();
});
it('sends one email to subscribed member of group, stating subscription is cancelled (PayPal)', async () => {
sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').returnsPromise().resolves({});
sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
.returnsPromise().resolves({
agreement_details: { // eslint-disable-line camelcase
next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase
cycles_completed: 1, // eslint-disable-line camelcase
},
});
let recipient = new User();
recipient.profile.name = 'recipient';
plan.planId = 'basic_earned';
plan.paymentMethod = paypalPayments.constants.PAYMENT_METHOD;
recipient.purchased.plan = plan;
recipient.guilds.push(group._id);
await recipient.save();
data.groupId = group._id;
await api.createSubscription(data);
expect(sender.sendTxn).to.be.calledTwice;
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
expect(sender.sendTxn.firstCall.args[2]).to.eql([
{name: 'LEADER', content: user.profile.name},
{name: 'GROUP_NAME', content: group.name},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
]);
// confirm that the other email sent is not a cancel-subscription email:
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
expect(sender.sendTxn.secondCall.args[1]).to.equal('group-subscription-begins');
paypalPayments.paypalBillingAgreementGet.restore();
paypalPayments.paypalBillingAgreementCancel.restore();
});
it('sends appropriate emails when subscribed member of group must manually cancel recurring Android subscription', async () => {
const TECH_ASSISTANCE_EMAIL = nconf.get('TECH_ASSISTANCE_EMAIL');
plan.customerId = 'random';
plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD;
let recipient = new User();
recipient.profile.name = 'recipient';
recipient.purchased.plan = plan;
recipient.guilds.push(group._id);
await recipient.save();
user.guilds.push(group._id);
await user.save();
data.groupId = group._id;
await api.createSubscription(data);
expect(sender.sendTxn).to.be.calledFourTimes;
expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL);
expect(sender.sendTxn.args[0][1]).to.equal('admin-user-subscription-details');
expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id);
expect(sender.sendTxn.args[1][1]).to.equal('group-member-join');
expect(sender.sendTxn.args[1][2]).to.eql([
{name: 'LEADER', content: groupLeaderName},
{name: 'GROUP_NAME', content: groupName},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE},
]);
expect(sender.sendTxn.args[2][0]._id).to.equal(group.leader);
expect(sender.sendTxn.args[2][1]).to.equal('group-member-join');
expect(sender.sendTxn.args[3][0]._id).to.equal(group.leader);
expect(sender.sendTxn.args[3][1]).to.equal('group-subscription-begins');
});
it('sends appropriate emails when subscribed member of group must manually cancel recurring iOS subscription', async () => {
const TECH_ASSISTANCE_EMAIL = nconf.get('TECH_ASSISTANCE_EMAIL');
plan.customerId = 'random';
plan.paymentMethod = api.constants.IOS_PAYMENT_METHOD;
let recipient = new User();
recipient.profile.name = 'recipient';
recipient.purchased.plan = plan;
recipient.guilds.push(group._id);
await recipient.save();
user.guilds.push(group._id);
await user.save();
data.groupId = group._id;
await api.createSubscription(data);
expect(sender.sendTxn).to.be.calledFourTimes;
expect(sender.sendTxn.args[0][0]._id).to.equal(TECH_ASSISTANCE_EMAIL);
expect(sender.sendTxn.args[0][1]).to.equal('admin-user-subscription-details');
expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id);
expect(sender.sendTxn.args[1][1]).to.equal('group-member-join');
expect(sender.sendTxn.args[1][2]).to.eql([
{name: 'LEADER', content: groupLeaderName},
{name: 'GROUP_NAME', content: groupName},
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS},
]);
expect(sender.sendTxn.args[2][0]._id).to.equal(group.leader);
expect(sender.sendTxn.args[2][1]).to.equal('group-member-join');
expect(sender.sendTxn.args[3][0]._id).to.equal(group.leader);
expect(sender.sendTxn.args[3][1]).to.equal('group-subscription-begins');
});
it('adds months to members with existing gift subscription', async () => {
@@ -333,7 +511,7 @@ describe('Purchasing a subscription for group', () => {
});
it('adds months to members with existing recurring subscription (Android)');
it('adds months to members with existing recurring subscription (iOs)');
it('adds months to members with existing recurring subscription (iOS)');
it('adds months to members who already cancelled but not yet terminated recurring subscription', async () => {
let recipient = new User();
@@ -603,7 +781,7 @@ describe('Purchasing a subscription for group', () => {
expect(updatedUser.purchased.plan.dateCreated).to.exist;
});
it('does not modify a user with a Google subscription', async () => {
it('does not modify a user with an Android subscription', async () => {
plan.customerId = 'random';
plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD;
@@ -447,6 +447,7 @@ describe('Paypal Payments', () => {
groupId,
paymentMethod: 'Paypal',
nextBill: nextBillingDate,
cancellationReason: undefined,
});
});
@@ -464,6 +465,7 @@ describe('Paypal Payments', () => {
groupId: group._id,
paymentMethod: 'Paypal',
nextBill: nextBillingDate,
cancellationReason: undefined,
});
});
});
@@ -683,6 +683,7 @@ describe('Stripe Payments', () => {
groupId: undefined,
nextBill: currentPeriodEndTimeStamp * 1000, // timestamp in seconds
paymentMethod: 'Stripe',
cancellationReason: undefined,
});
});
@@ -702,6 +703,7 @@ describe('Stripe Payments', () => {
groupId,
nextBill: currentPeriodEndTimeStamp * 1000, // timestamp in seconds
paymentMethod: 'Stripe',
cancellationReason: undefined,
});
});
});
+1 -1
View File
@@ -228,7 +228,7 @@ describe('Group Model', () => {
});
it('applies damage only to participating members of party even under buggy conditions', async () => {
// stops unfair damage from mbugs like https://github.com/HabitRPG/habitrpg/issues/7653
// stops unfair damage from mbugs like https://github.com/HabitRPG/habitica/issues/7653
party.quest.members = {
[questLeader._id]: true,
[participatingMember._id]: true,