diff --git a/habitica-images b/habitica-images index 5e51d6b8011..be2d077eb64 160000 --- a/habitica-images +++ b/habitica-images @@ -1 +1 @@ -Subproject commit 5e51d6b8011a9500557e184bc67bac3bb671eaa5 +Subproject commit be2d077eb64f9d19d73455ddd11d33c008a54bcf diff --git a/test/api/unit/libs/bug-report.test.js b/test/api/unit/libs/bug-report.test.js index 58eefca3e65..5cb33d39b49 100644 --- a/test/api/unit/libs/bug-report.test.js +++ b/test/api/unit/libs/bug-report.test.js @@ -44,7 +44,6 @@ describe('bug-report', () => { USER_HOURGLASSES: 0, USER_ID: userId, USER_LEVEL: 1, - USER_OFFSET_MONTHS: 0, USER_PAYMENT_PLATFORM: undefined, USER_SUBSCRIPTION: undefined, USER_TIMEZONE_OFFSET: 0, diff --git a/test/api/unit/libs/cron.test.js b/test/api/unit/libs/cron.test.js index 6757228033e..2863269ff2e 100644 --- a/test/api/unit/libs/cron.test.js +++ b/test/api/unit/libs/cron.test.js @@ -154,6 +154,14 @@ describe('cron', async () => { expect(user.purchased.plan.consecutive.count).to.equal(1); }); + it('increments plan.cumulativeCount', async () => { + user.purchased.plan.cumulativeCount = 0; + await cron({ + user, tasksByType, daysMissed, analytics, + }); + expect(user.purchased.plan.cumulativeCount).to.equal(1); + }); + it('increments plan.consecutive.count by more than 1 if user skipped months between logins', async () => { user.purchased.plan.dateUpdated = moment().subtract(2, 'months').toDate(); user.purchased.plan.consecutive.count = 0; @@ -163,12 +171,13 @@ describe('cron', async () => { expect(user.purchased.plan.consecutive.count).to.equal(2); }); - it('decrements plan.consecutive.offset when offset is greater than 0', async () => { - user.purchased.plan.consecutive.offset = 2; + it('increments plan.cumulativeCount by more than 1 if user skipped months between logins', async () => { + user.purchased.plan.dateUpdated = moment().subtract(3, 'months').toDate(); + user.purchased.plan.cumulativeCount = 0; await cron({ user, tasksByType, daysMissed, analytics, }); - expect(user.purchased.plan.consecutive.offset).to.equal(1); + expect(user.purchased.plan.cumulativeCount).to.equal(3); }); it('does not award unearned plan.consecutive.trinkets if subscription ended during an absence', async () => { @@ -185,12 +194,12 @@ describe('cron', async () => { }); it('does not increment plan.consecutive.gemCapExtra when user has reached the gemCap limit', async () => { - user.purchased.plan.consecutive.gemCapExtra = 25; + user.purchased.plan.consecutive.gemCapExtra = 26; user.purchased.plan.consecutive.count = 5; await cron({ user, tasksByType, daysMissed, analytics, }); - expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26); }); it('does not reset plan stats if we are before the last day of the cancelled month', async () => { @@ -205,16 +214,14 @@ describe('cron', async () => { user.purchased.plan.dateTerminated = moment(new Date()).subtract({ days: 1 }); user.purchased.plan.consecutive.gemCapExtra = 20; user.purchased.plan.consecutive.count = 5; - user.purchased.plan.consecutive.offset = 1; await cron({ user, tasksByType, daysMissed, analytics, }); expect(user.purchased.plan.customerId).to.not.exist; - expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(0); + expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(20); expect(user.purchased.plan.consecutive.count).to.equal(0); - expect(user.purchased.plan.consecutive.offset).to.equal(0); }); describe('for a 1-month recurring subscription', async () => { @@ -236,13 +243,11 @@ describe('cron', async () => { user1.purchased.plan.dateUpdated = moment().toDate(); user1.purchased.plan.planId = 'basic'; user1.purchased.plan.consecutive.count = 0; - user1.purchased.plan.perkMonthCount = 0; - user1.purchased.plan.consecutive.offset = 0; - user1.purchased.plan.consecutive.trinkets = 0; + user1.purchased.plan.consecutive.trinkets = 1; user1.purchased.plan.consecutive.gemCapExtra = 0; }); - it('does not increment consecutive benefits after the first month', async () => { + it('increments consecutive benefits', async () => { clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') .add(2, 'days') .toDate()); @@ -253,75 +258,8 @@ describe('cron', async () => { user: user1, tasksByType, daysMissed, analytics, }); expect(user1.purchased.plan.consecutive.count).to.equal(1); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(0); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0); - }); - - it('does not increment consecutive benefits after the second month', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - // Add 1 month to simulate what happens a month after the subscription was created. - // Add 2 days so that we're sure we're not affected by any start-of-month effects - // e.g., from time zone oddness. - await cron({ - user: user1, tasksByType, daysMissed, analytics, - }); - expect(user1.purchased.plan.consecutive.count).to.equal(2); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(0); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0); - }); - - it('increments consecutive benefits after the second month if they also received a 1 month gift subscription', async () => { - user1.purchased.plan.perkMonthCount = 1; - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - // Add 1 month to simulate what happens a month after the subscription was created. - // Add 2 days so that we're sure we're not affected by any start-of-month effects - // e.g., from time zone oddness. - await cron({ - user: user1, tasksByType, daysMissed, analytics, - }); - expect(user1.purchased.plan.perkMonthCount).to.equal(0); - expect(user1.purchased.plan.consecutive.count).to.equal(2); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('increments consecutive benefits after the third month', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months') - .add(2, 'days') - .toDate()); - // Add 1 month to simulate what happens a month after the subscription was created. - // Add 2 days so that we're sure we're not affected by any start-of-month effects - // e.g., from time zone oddness. - await cron({ - user: user1, tasksByType, daysMissed, analytics, - }); - expect(user1.purchased.plan.consecutive.count).to.equal(3); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('does not increment consecutive benefits after the fourth month', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months') - .add(2, 'days') - .toDate()); - // Add 1 month to simulate what happens a month after the subscription was created. - // Add 2 days so that we're sure we're not affected by any start-of-month effects - // e.g., from time zone oddness. - await cron({ - user: user1, tasksByType, daysMissed, analytics, - }); - expect(user1.purchased.plan.consecutive.count).to.equal(4); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5); + expect(user1.purchased.plan.consecutive.trinkets).to.equal(2); + expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(2); }); it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => { @@ -332,33 +270,8 @@ describe('cron', async () => { user: user1, tasksByType, daysMissed, analytics, }); expect(user1.purchased.plan.consecutive.count).to.equal(10); - expect(user1.purchased.plan.consecutive.offset).to.equal(0); - expect(user1.purchased.plan.consecutive.trinkets).to.equal(3); - expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(15); - }); - - it('initializes plan.perkMonthCount if necessary', async () => { - user.purchased.plan.perkMonthCount = undefined; - clock = sinon.useFakeTimers(moment(user.purchased.plan.dateUpdated) - .utcOffset(0) - .startOf('month') - .add(1, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user, tasksByType, daysMissed, analytics, - }); - expect(user.purchased.plan.perkMonthCount).to.equal(1); - user.purchased.plan.perkMonthCount = undefined; - user.purchased.plan.consecutive.count = 8; - clock.restore(); - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user, tasksByType, daysMissed, analytics, - }); - expect(user.purchased.plan.perkMonthCount).to.equal(2); + expect(user1.purchased.plan.consecutive.trinkets).to.equal(11); + expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(20); }); }); @@ -379,14 +292,12 @@ describe('cron', async () => { user3.purchased.plan.customerId = 'subscribedId'; user3.purchased.plan.dateUpdated = moment().toDate(); user3.purchased.plan.planId = 'basic_3mo'; - user3.purchased.plan.perkMonthCount = 0; user3.purchased.plan.consecutive.count = 0; - user3.purchased.plan.consecutive.offset = 3; user3.purchased.plan.consecutive.trinkets = 1; - user3.purchased.plan.consecutive.gemCapExtra = 5; + user3.purchased.plan.consecutive.gemCapExtra = 0; }); - it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => { + it('increments consecutive benefits', async () => { clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') .add(2, 'days') .toDate()); @@ -394,102 +305,8 @@ describe('cron', async () => { user: user3, tasksByType, daysMissed, analytics, }); expect(user3.purchased.plan.consecutive.count).to.equal(1); - expect(user3.purchased.plan.consecutive.offset).to.equal(2); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('does not increment consecutive benefits in the middle of the period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(2); - expect(user3.purchased.plan.consecutive.offset).to.equal(1); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(3); - expect(user3.purchased.plan.consecutive.offset).to.equal(0); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('increments consecutive benefits the month after the second paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(4); - expect(user3.purchased.plan.consecutive.offset).to.equal(2); expect(user3.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => { - user3.purchased.plan.perkMonthCount = 2; - user3.purchased.plan.consecutive.trinkets = 1; - user3.purchased.plan.consecutive.gemCapExtra = 5; - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.perkMonthCount).to.equal(2); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('does not increment consecutive benefits in the second month of the second period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(5, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(5); - expect(user3.purchased.plan.consecutive.offset).to.equal(1); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('does not increment consecutive benefits in the final month of the second period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(6, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(6); - expect(user3.purchased.plan.consecutive.offset).to.equal(0); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('increments consecutive benefits the month after the third paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3, tasksByType, daysMissed, analytics, - }); - expect(user3.purchased.plan.consecutive.count).to.equal(7); - expect(user3.purchased.plan.consecutive.offset).to.equal(2); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(3); - expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(15); + expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(2); }); it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => { @@ -500,8 +317,7 @@ describe('cron', async () => { user: user3, tasksByType, daysMissed, analytics, }); expect(user3.purchased.plan.consecutive.count).to.equal(10); - expect(user3.purchased.plan.consecutive.offset).to.equal(2); - expect(user3.purchased.plan.consecutive.trinkets).to.equal(4); + expect(user3.purchased.plan.consecutive.trinkets).to.equal(11); expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(20); }); }); @@ -523,14 +339,12 @@ describe('cron', async () => { user6.purchased.plan.customerId = 'subscribedId'; user6.purchased.plan.dateUpdated = moment().toDate(); user6.purchased.plan.planId = 'google_6mo'; - user6.purchased.plan.perkMonthCount = 0; user6.purchased.plan.consecutive.count = 0; - user6.purchased.plan.consecutive.offset = 6; - user6.purchased.plan.consecutive.trinkets = 2; - user6.purchased.plan.consecutive.gemCapExtra = 10; + user6.purchased.plan.consecutive.trinkets = 1; + user6.purchased.plan.consecutive.gemCapExtra = 0; }); - it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => { + it('increments benefits', async () => { clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') .add(2, 'days') .toDate()); @@ -538,74 +352,8 @@ describe('cron', async () => { user: user6, tasksByType, daysMissed, analytics, }); expect(user6.purchased.plan.consecutive.count).to.equal(1); - expect(user6.purchased.plan.consecutive.offset).to.equal(5); - expect(user6.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(6, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6, tasksByType, daysMissed, analytics, - }); - expect(user6.purchased.plan.consecutive.count).to.equal(6); - expect(user6.purchased.plan.consecutive.offset).to.equal(0); expect(user6.purchased.plan.consecutive.trinkets).to.equal(2); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(10); - }); - - it('increments consecutive benefits the month after the second paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6, tasksByType, daysMissed, analytics, - }); - expect(user6.purchased.plan.consecutive.count).to.equal(7); - expect(user6.purchased.plan.consecutive.offset).to.equal(5); - expect(user6.purchased.plan.consecutive.trinkets).to.equal(4); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20); - }); - - it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => { - user6.purchased.plan.perkMonthCount = 2; - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6, tasksByType, daysMissed, analytics, - }); - expect(user6.purchased.plan.perkMonthCount).to.equal(2); - expect(user6.purchased.plan.consecutive.trinkets).to.equal(4); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20); - }); - - it('increments consecutive benefits the month after the third paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6, tasksByType, daysMissed, analytics, - }); - expect(user6.purchased.plan.consecutive.count).to.equal(13); - expect(user6.purchased.plan.consecutive.offset).to.equal(5); - expect(user6.purchased.plan.consecutive.trinkets).to.equal(6); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25); - }); - - it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(19, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6, tasksByType, daysMissed, analytics, - }); - expect(user6.purchased.plan.consecutive.count).to.equal(19); - expect(user6.purchased.plan.consecutive.offset).to.equal(5); - expect(user6.purchased.plan.consecutive.trinkets).to.equal(8); - expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(2); }); }); @@ -626,11 +374,10 @@ describe('cron', async () => { user12.purchased.plan.dateUpdated = moment().toDate(); user12.purchased.plan.planId = 'basic_12mo'; user12.purchased.plan.consecutive.count = 0; - user12.purchased.plan.consecutive.offset = 12; - user12.purchased.plan.consecutive.trinkets = 4; - user12.purchased.plan.consecutive.gemCapExtra = 20; + user12.purchased.plan.consecutive.trinkets = 1; + user12.purchased.plan.consecutive.gemCapExtra = 26; - it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => { + it('increments consecutive benefits the month after the second paid period has started', async () => { clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') .add(2, 'days') .toDate()); @@ -638,61 +385,20 @@ describe('cron', async () => { user: user12, tasksByType, daysMissed, analytics, }); expect(user12.purchased.plan.consecutive.count).to.equal(1); - expect(user12.purchased.plan.consecutive.offset).to.equal(11); - expect(user12.purchased.plan.consecutive.trinkets).to.equal(4); - expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20); - }); - - it('does not increment consecutive benefits in the final month of the period that they already have benefits for', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(12, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user12, tasksByType, daysMissed, analytics, - }); - expect(user12.purchased.plan.consecutive.count).to.equal(12); - expect(user12.purchased.plan.consecutive.offset).to.equal(0); - expect(user12.purchased.plan.consecutive.trinkets).to.equal(4); - expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(20); - }); - - it('increments consecutive benefits the month after the second paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user12, tasksByType, daysMissed, analytics, - }); - expect(user12.purchased.plan.consecutive.count).to.equal(13); - expect(user12.purchased.plan.consecutive.offset).to.equal(11); - expect(user12.purchased.plan.consecutive.trinkets).to.equal(8); - expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25); - }); - - it('increments consecutive benefits the month after the third paid period has started', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(25, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user12, tasksByType, daysMissed, analytics, - }); - expect(user12.purchased.plan.consecutive.count).to.equal(25); - expect(user12.purchased.plan.consecutive.offset).to.equal(11); - expect(user12.purchased.plan.consecutive.trinkets).to.equal(12); - expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user12.purchased.plan.consecutive.trinkets).to.equal(2); + expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(26); }); it('increments consecutive benefits correctly if user has been absent with continuous subscription', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(37, 'months') + clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(10, 'months') .add(2, 'days') .toDate()); await cron({ user: user12, tasksByType, daysMissed, analytics, }); - expect(user12.purchased.plan.consecutive.count).to.equal(37); - expect(user12.purchased.plan.consecutive.offset).to.equal(11); - expect(user12.purchased.plan.consecutive.trinkets).to.equal(16); - expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user12.purchased.plan.consecutive.count).to.equal(10); + expect(user12.purchased.plan.consecutive.trinkets).to.equal(11); + expect(user12.purchased.plan.consecutive.gemCapExtra).to.equal(26); }); }); @@ -715,11 +421,11 @@ describe('cron', async () => { .toDate(); user3g.purchased.plan.planId = null; user3g.purchased.plan.consecutive.count = 0; - user3g.purchased.plan.consecutive.offset = 3; + user3g.purchased.plan.cumulativeCount = 0; user3g.purchased.plan.consecutive.trinkets = 1; - user3g.purchased.plan.consecutive.gemCapExtra = 5; + user3g.purchased.plan.consecutive.gemCapExtra = 0; - it('does not increment consecutive benefits in the first month of the gift subscription', async () => { + it('increments benefits', async () => { clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') .add(2, 'days') .toDate()); @@ -727,35 +433,9 @@ describe('cron', async () => { user: user3g, tasksByType, daysMissed, analytics, }); expect(user3g.purchased.plan.consecutive.count).to.equal(1); - expect(user3g.purchased.plan.consecutive.offset).to.equal(2); - expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('does not increment consecutive benefits in the second month of the gift subscription', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3g, tasksByType, daysMissed, analytics, - }); - expect(user3g.purchased.plan.consecutive.count).to.equal(2); - expect(user3g.purchased.plan.consecutive.offset).to.equal(1); - expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5); - }); - - it('does not increment consecutive benefits in the third month of the gift subscription', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user3g, tasksByType, daysMissed, analytics, - }); - expect(user3g.purchased.plan.consecutive.count).to.equal(3); - expect(user3g.purchased.plan.consecutive.offset).to.equal(0); - expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(5); + expect(user3g.purchased.plan.cumulativeCount).to.equal(1); + expect(user3g.purchased.plan.consecutive.trinkets).to.equal(2); + expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(2); }); it('does not increment consecutive benefits in the month after the gift subscription has ended', async () => { @@ -767,84 +447,9 @@ describe('cron', async () => { }); // subscription has been erased by now expect(user3g.purchased.plan.consecutive.count).to.equal(0); - expect(user3g.purchased.plan.consecutive.offset).to.equal(0); - expect(user3g.purchased.plan.consecutive.trinkets).to.equal(1); - expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(0); // erased - }); - }); - - describe('for a 6-month recurring subscription where the user has incorrect consecutive month data from prior bugs', async () => { - const user6x = new User({ - auth: { - local: { - username: 'username6x', - lowerCaseUsername: 'username6x', - email: 'email6x@example.com', - salt: 'salt', - hashed_password: 'hashed_password', // eslint-disable-line camelcase - }, - }, - }); - // user6x has a 6-month recurring subscription starting 8 months in the past - // before issue #4819 was fixed - user6x.purchased.plan.customerId = 'subscribedId'; - user6x.purchased.plan.dateUpdated = moment().toDate(); - user6x.purchased.plan.planId = 'basic_6mo'; - user6x.purchased.plan.consecutive.count = 8; - user6x.purchased.plan.consecutive.offset = 0; - user6x.purchased.plan.consecutive.trinkets = 3; - user6x.purchased.plan.consecutive.gemCapExtra = 15; - - it('increments consecutive benefits in the first month since the fix for #4819 goes live', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6x, tasksByType, daysMissed, analytics, - }); - expect(user6x.purchased.plan.consecutive.count).to.equal(9); - expect(user6x.purchased.plan.consecutive.offset).to.equal(5); - expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5); - expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25); - }); - - it('does not increment consecutive benefits in the second month after the fix goes live', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6x, tasksByType, daysMissed, analytics, - }); - expect(user6x.purchased.plan.consecutive.count).to.equal(10); - expect(user6x.purchased.plan.consecutive.offset).to.equal(4); - expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5); - expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25); - }); - - it('does not increment consecutive benefits in the third month after the fix goes live', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6x, tasksByType, daysMissed, analytics, - }); - expect(user6x.purchased.plan.consecutive.count).to.equal(11); - expect(user6x.purchased.plan.consecutive.offset).to.equal(3); - expect(user6x.purchased.plan.consecutive.trinkets).to.equal(5); - expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25); - }); - - it('increments consecutive benefits in the seventh month after the fix goes live', async () => { - clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months') - .add(2, 'days') - .toDate()); - await cron({ - user: user6x, tasksByType, daysMissed, analytics, - }); - expect(user6x.purchased.plan.consecutive.count).to.equal(15); - expect(user6x.purchased.plan.consecutive.offset).to.equal(5); - expect(user6x.purchased.plan.consecutive.trinkets).to.equal(7); - expect(user6x.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user3g.purchased.plan.consecutive.trinkets).to.equal(2); + expect(user3g.purchased.plan.consecutive.gemCapExtra).to.equal(2); + expect(user3g.purchased.plan.cumulativeCount).to.equal(1); }); }); }); @@ -888,12 +493,12 @@ describe('cron', async () => { expect(user.purchased.plan.consecutive.count).to.equal(0); }); - it('does not decrement plan.consecutive.offset when offset is greater than 0', async () => { - user.purchased.plan.consecutive.offset = 1; + it('does not increment plan.cumulativeCount', async () => { + user.purchased.plan.cumulativeCount = 0; await cron({ user, tasksByType, daysMissed, analytics, }); - expect(user.purchased.plan.consecutive.offset).to.equal(1); + expect(user.purchased.plan.cumulativeCount).to.equal(0); }); it('does not increment plan.consecutive.trinkets when user has reached a month that is a multiple of 3', async () => { @@ -913,12 +518,12 @@ describe('cron', async () => { }); it('does not increment plan.consecutive.gemCapExtra when user has reached the gemCap limit', async () => { - user.purchased.plan.consecutive.gemCapExtra = 25; + user.purchased.plan.consecutive.gemCapExtra = 26; user.purchased.plan.consecutive.count = 5; await cron({ user, tasksByType, daysMissed, analytics, }); - expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(25); + expect(user.purchased.plan.consecutive.gemCapExtra).to.equal(26); }); it('does nothing to plan stats if we are before the last day of the cancelled month', async () => { @@ -928,22 +533,6 @@ describe('cron', async () => { }); expect(user.purchased.plan.customerId).to.not.exist; }); - - xit('does nothing to plan stats when we are after the last day of the cancelled month', async () => { - user.purchased.plan.dateTerminated = moment(new Date()).subtract({ days: 1 }); - user.purchased.plan.consecutive.gemCapExtra = 20; - user.purchased.plan.consecutive.count = 5; - user.purchased.plan.consecutive.offset = 1; - - await cron({ - user, tasksByType, daysMissed, analytics, - }); - - expect(user.purchased.plan.customerId).to.exist; - expect(user.purchased.plan.consecutive.gemCapExtra).to.exist; - expect(user.purchased.plan.consecutive.count).to.exist; - expect(user.purchased.plan.consecutive.offset).to.exist; - }); }); describe('todos', async () => { diff --git a/test/api/unit/libs/payments/group-plans/group-payments-create.test.js b/test/api/unit/libs/payments/group-plans/group-payments-create.test.js index 383d7dfe6f3..17ff154a0a0 100644 --- a/test/api/unit/libs/payments/group-plans/group-payments-create.test.js +++ b/test/api/unit/libs/payments/group-plans/group-payments-create.test.js @@ -715,7 +715,7 @@ describe('Purchasing a group plan for group', () => { const mysteryItem = { title: 'item' }; const mysteryItems = [mysteryItem]; const consecutive = { - trinkets: 3, + trinkets: 4, gemCapExtra: 20, offset: 1, count: 13, diff --git a/test/api/unit/libs/payments/payments.test.js b/test/api/unit/libs/payments/payments.test.js index 5b2d2f98990..00732af3cea 100644 --- a/test/api/unit/libs/payments/payments.test.js +++ b/test/api/unit/libs/payments/payments.test.js @@ -12,6 +12,7 @@ import { } from '../../../../helpers/api-unit.helper'; import * as worldState from '../../../../../website/server/libs/worldState'; import { TransactionModel } from '../../../../../website/server/models/transaction'; +import { REPEATING_EVENTS } from '../../../../../website/common/script/content/constants/events'; describe('payments/index', () => { let user; @@ -65,7 +66,6 @@ describe('payments/index', () => { mysteryItems: [], consecutive: { trinkets: 0, - offset: 0, gemCapExtra: 0, }, }; @@ -108,14 +108,8 @@ describe('payments/index', () => { }); it('add a transaction entry to the recipient', async () => { - recipient.purchased.plan = plan; - - expect(recipient.purchased.plan.extraMonths).to.eql(0); - await api.createSubscription(data); - expect(recipient.purchased.plan.extraMonths).to.eql(3); - const transactions = await TransactionModel .find({ userId: recipient._id }) .sort({ createdAt: -1 }) @@ -177,6 +171,45 @@ describe('payments/index', () => { expect(recipient.purchased.plan.dateUpdated).to.exist; }); + it('does not reset gemCapExtra if they already had one', async () => { + recipient.purchased.plan.consecutive.gemCapExtra = 10; + + await api.createSubscription(data); + + expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(10); + }); + + it('sets gemCapExtra to 0 if they receive a 3 month sub', async () => { + data.gift.subscription.key = 'basic_3mo'; + data.gift.subscription.months = 3; + + await api.createSubscription(data); + + expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); + }); + + it('sets gemCapExtra to max if they receive a 12 month sub', async () => { + recipient.purchased.plan.consecutive.gemCapExtra = 10; + + data.gift.subscription.key = 'basic_12mo'; + data.gift.subscription.months = 12; + + await api.createSubscription(data); + + expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(26); + }); + + it('gives user 1 hourglass if they have no active subscription', async () => { + await api.createSubscription(data); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); + }); + + it('does not give any hourglasses if they have an active subscription', async () => { + recipient.purchased.plan = plan; + await api.createSubscription(data); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(plan.consecutive.trinkets); + }); + it('sets plan.dateUpdated if it did exist but the user has cancelled', async () => { recipient.purchased.plan.dateUpdated = moment().subtract(1, 'days').toDate(); recipient.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate(); @@ -235,116 +268,6 @@ describe('payments/index', () => { expect(recipient.purchased.plan.customerId).to.eql('customer-id'); }); - it('sets plan.perkMonthCount to 1 if user is not subscribed', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = 1; - recipient.purchased.plan.customerId = undefined; - data.sub.key = 'basic_earned'; - data.gift.subscription.key = 'basic_earned'; - data.gift.subscription.months = 1; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(1); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(1); - }); - - it('sets plan.perkMonthCount to 1 if field is not initialized', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = -1; - recipient.purchased.plan.customerId = undefined; - data.sub.key = 'basic_earned'; - data.gift.subscription.key = 'basic_earned'; - data.gift.subscription.months = 1; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(-1); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(1); - }); - - it('sets plan.perkMonthCount to 1 if user had previous count but lapsed subscription', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = 2; - recipient.purchased.plan.customerId = undefined; - data.sub.key = 'basic_earned'; - data.gift.subscription.key = 'basic_earned'; - data.gift.subscription.months = 1; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(2); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(1); - }); - - it('adds to plan.perkMonthCount if user is already subscribed', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = 1; - data.sub.key = 'basic_earned'; - data.gift.subscription.key = 'basic_earned'; - data.gift.subscription.months = 1; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(1); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(2); - }); - - it('awards perks if plan.perkMonthCount reaches 3 with existing subscription', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = 2; - data.sub.key = 'basic_earned'; - data.gift.subscription.key = 'basic_earned'; - data.gift.subscription.months = 1; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(2); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(0); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5); - }); - - it('awards perks if plan.perkMonthCount reaches 3 without existing subscription', async () => { - recipient.purchased.plan.perkMonthCount = 0; - expect(recipient.purchased.plan.perkMonthCount).to.eql(0); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(0); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5); - }); - - it('awards perks if plan.perkMonthCount reaches 3 without initialized field', async () => { - expect(recipient.purchased.plan.perkMonthCount).to.eql(-1); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(0); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5); - }); - - it('awards perks if plan.perkMonthCount goes over 3', async () => { - recipient.purchased.plan = plan; - recipient.purchased.plan.perkMonthCount = 2; - data.sub.key = 'basic_earned'; - - expect(recipient.purchased.plan.perkMonthCount).to.eql(2); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); - await api.createSubscription(data); - - expect(recipient.purchased.plan.perkMonthCount).to.eql(2); - expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); - expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5); - }); - it('sets plan.customerId to "Gift" if it does not already exist', async () => { expect(recipient.purchased.plan.customerId).to.not.exist; @@ -421,8 +344,8 @@ describe('payments/index', () => { context('Active Promotion', () => { beforeEach(() => { sinon.stub(worldState, 'getCurrentEventList').returns([{ - ...common.content.events.winter2021Promo, - event: 'winter2021', + ...REPEATING_EVENTS.giftOneGetOne, + event: 'g1g1', }]); }); @@ -438,22 +361,30 @@ describe('payments/index', () => { expect(user.purchased.plan.dateTerminated).to.exist; expect(user.purchased.plan.dateUpdated).to.exist; expect(user.purchased.plan.dateCreated).to.exist; + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5); expect(recipient.purchased.plan.customerId).to.eql('Gift'); expect(recipient.purchased.plan.dateTerminated).to.exist; expect(recipient.purchased.plan.dateUpdated).to.exist; expect(recipient.purchased.plan.dateCreated).to.exist; + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1); + expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0); }); it('adds extraMonths to existing subscription for purchaser and creates a gift subscription for recipient without sub', async () => { user.purchased.plan = plan; expect(user.purchased.plan.extraMonths).to.eql(0); + expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); await api.createSubscription(data); expect(user.purchased.plan.extraMonths).to.eql(3); + expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); expect(recipient.items.pets['Jackalope-RoyalPurple']).to.eql(5); expect(recipient.purchased.plan.customerId).to.eql('Gift'); @@ -466,10 +397,12 @@ describe('payments/index', () => { recipient.purchased.plan = plan; expect(recipient.purchased.plan.extraMonths).to.eql(0); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); await api.createSubscription(data); expect(recipient.purchased.plan.extraMonths).to.eql(3); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); expect(user.items.pets['Jackalope-RoyalPurple']).to.eql(5); expect(user.purchased.plan.customerId).to.eql('Gift'); @@ -484,11 +417,15 @@ describe('payments/index', () => { expect(user.purchased.plan.extraMonths).to.eql(0); expect(recipient.purchased.plan.extraMonths).to.eql(0); + expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); await api.createSubscription(data); expect(user.purchased.plan.extraMonths).to.eql(3); expect(recipient.purchased.plan.extraMonths).to.eql(3); + expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0); }); it('sends a private message about the promotion', async () => { @@ -511,7 +448,6 @@ describe('payments/index', () => { expect(user.purchased.plan.customerId).to.eql('customer-id'); expect(user.purchased.plan.dateUpdated).to.exist; expect(user.purchased.plan.gemsBought).to.eql(0); - expect(user.purchased.plan.perkMonthCount).to.eql(0); expect(user.purchased.plan.paymentMethod).to.eql('Payment Method'); expect(user.purchased.plan.extraMonths).to.eql(0); expect(user.purchased.plan.dateTerminated).to.eql(null); @@ -549,33 +485,6 @@ describe('payments/index', () => { expect(user.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate); }); - it('keeps plan.perkMonthCount when changing subscription type', async () => { - await api.createSubscription(data); - user.purchased.plan.perkMonthCount = 2; - await api.createSubscription(data); - expect(user.purchased.plan.perkMonthCount).to.eql(2); - }); - - it('sets plan.perkMonthCount to zero when creating new monthly subscription', async () => { - user.purchased.plan.perkMonthCount = 2; - await api.createSubscription(data); - expect(user.purchased.plan.perkMonthCount).to.eql(0); - }); - - it('sets plan.perkMonthCount to zero when creating new 3 month subscription', async () => { - user.purchased.plan.perkMonthCount = 2; - await api.createSubscription(data); - expect(user.purchased.plan.perkMonthCount).to.eql(0); - }); - - it('updates plan.consecutive.offset when changing subscription type', async () => { - await api.createSubscription(data); - expect(user.purchased.plan.consecutive.offset).to.eql(3); - data.sub.key = 'basic_6mo'; - await api.createSubscription(data); - expect(user.purchased.plan.consecutive.offset).to.eql(6); - }); - it('awards the Royal Purple Jackalope pet', async () => { await api.createSubscription(data); @@ -694,6 +603,7 @@ describe('payments/index', () => { expect(user.purchased.plan.dateCreated).to.eql(created); expect(user.purchased.plan.dateUpdated).to.not.eql(updated); expect(user.purchased.plan.customerId).to.eql('customer-id'); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); }); @@ -741,77 +651,51 @@ describe('payments/index', () => { }); context('Block subscription perks', () => { - it('adds block months to plan.consecutive.offset', async () => { + it('adds 26 to plan.consecutive.gemCapExtra for 12 month block', async () => { + data.sub.key = 'basic_12mo'; await api.createSubscription(data); - expect(user.purchased.plan.consecutive.offset).to.eql(3); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('does not add to plans.consecutive.offset if 1 month subscription', async () => { - data.sub.key = 'basic_earned'; - await api.createSubscription(data); - - expect(user.purchased.plan.consecutive.offset).to.eql(0); - }); + it('does not raise plan.consecutive.gemCapExtra higher than 26', async () => { + data.sub.key = 'basic_12mo'; - it('resets plans.consecutive.offset if 1 month subscription', async () => { - user.purchased.plan.consecutive.offset = 1; - await user.save(); - data.sub.key = 'basic_earned'; + await api.createSubscription(data); await api.createSubscription(data); - expect(user.purchased.plan.consecutive.offset).to.eql(0); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('adds 5 to plan.consecutive.gemCapExtra for 3 month block', async () => { + it('adds a plan.consecutive.trinkets for 3 month block', async () => { await api.createSubscription(data); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); }); - it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => { + it('adds 1 plan.consecutive.trinkets for 6 month block', async () => { data.sub.key = 'basic_6mo'; - await api.createSubscription(data); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => { - data.sub.key = 'basic_12mo'; await api.createSubscription(data); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); }); - it('does not raise plan.consecutive.gemCapExtra higher than 25', async () => { + it('adds 1 plan.consecutive.trinkets for 12 month block if they had promo', async () => { + user.purchased.plan.hourglassPromoReceived = new Date(); data.sub.key = 'basic_12mo'; - await api.createSubscription(data); - await api.createSubscription(data); - - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25); - }); - - it('adds a plan.consecutive.trinkets for 3 month block', async () => { await api.createSubscription(data); expect(user.purchased.plan.consecutive.trinkets).to.eql(1); }); - it('adds 2 plan.consecutive.trinkets for 6 month block', async () => { - data.sub.key = 'basic_6mo'; - - await api.createSubscription(data); - - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - }); - - it('adds 4 plan.consecutive.trinkets for 12 month block', async () => { + it('adds 12 plan.consecutive.trinkets for 12 month block', async () => { data.sub.key = 'basic_12mo'; await api.createSubscription(data); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); context('Upgrades subscription', () => { @@ -819,70 +703,38 @@ describe('payments/index', () => { beforeEach(async () => { data.updatedFrom = { logic: 'payDifference' }; }); - it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_3mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - }); - - it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -894,7 +746,7 @@ describe('payments/index', () => { data.updatedFrom.key = 'basic_3mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); }); @@ -902,70 +754,39 @@ describe('payments/index', () => { beforeEach(async () => { data.updatedFrom = { logic: 'payFull' }; }); - it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_3mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25); - }); - - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(6); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -977,7 +798,7 @@ describe('payments/index', () => { data.updatedFrom.key = 'basic_3mo'; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(5); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); }); @@ -988,30 +809,13 @@ describe('payments/index', () => { data.updatedFrom = { logic: 'refundAndRepay' }; }); context('Upgrades within first half of subscription', () => { - it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - clock.restore(); - clock = sinon.useFakeTimers(new Date('2022-01-10')); - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_3mo'; @@ -1019,28 +823,10 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-02-05')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); - }); - - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - clock.restore(); - clock = sinon.useFakeTimers(new Date('2022-01-08')); - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -1054,17 +840,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-01-31')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; @@ -1072,35 +858,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-01-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom.key = 'basic_earned'; - clock.restore(); - clock = sinon.useFakeTimers(new Date('2024-01-08')); - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - }); - - it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => { + it('2 plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; @@ -1108,10 +876,10 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-08-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -1125,11 +893,11 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-07-31')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); }); context('Upgrades within second half of subscription', () => { - it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { + it('Adds 0 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { data.sub.key = 'basic_earned'; expect(user.purchased.plan.planId).to.not.exist; @@ -1144,16 +912,16 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-01-20')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); }); - it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 26 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_3mo'; @@ -1161,17 +929,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-02-24')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { + it('Adds 0 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { data.sub.key = 'basic_earned'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_6mo'; data.updatedFrom.key = 'basic_earned'; @@ -1179,17 +947,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-01-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; @@ -1197,10 +965,10 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-05-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(6); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -1214,17 +982,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-03-03')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(5); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => { + it('Adds 0 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => { data.sub.key = 'basic_earned'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_6mo'; data.updatedFrom.key = 'basic_earned'; @@ -1232,17 +1000,17 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2022-05-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => { data.sub.key = 'basic_6mo'; expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.trinkets).to.eql(1); data.sub.key = 'basic_12mo'; data.updatedFrom.key = 'basic_6mo'; @@ -1250,10 +1018,10 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2023-05-28')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(6); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); - it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => { + it('Adds 12 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => { expect(user.purchased.plan.planId).to.not.exist; await api.createSubscription(data); @@ -1267,7 +1035,7 @@ describe('payments/index', () => { clock = sinon.useFakeTimers(new Date('2023-09-03')); await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(5); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); }); afterEach(async () => { @@ -1277,22 +1045,6 @@ describe('payments/index', () => { }); context('Downgrades subscription', () => { - it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => { - data.sub.key = 'basic_6mo'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - - data.sub.key = 'basic_earned'; - data.updatedFrom = { key: 'basic_6mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => { expect(user.purchased.plan.planId).to.not.exist; @@ -1300,28 +1052,12 @@ describe('payments/index', () => { await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); data.sub.key = 'basic_3mo'; data.updatedFrom = { key: 'basic_12mo' }; await api.createSubscription(data); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); - }); - - it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => { - data.sub.key = 'basic_6mo'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - - data.sub.key = 'basic_earned'; - data.updatedFrom = { key: 'basic_6mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(26); }); it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => { @@ -1331,12 +1067,12 @@ describe('payments/index', () => { await api.createSubscription(data); expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); data.sub.key = 'basic_3mo'; data.updatedFrom = { key: 'basic_12mo' }; await api.createSubscription(data); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); + expect(user.purchased.plan.consecutive.trinkets).to.eql(13); }); }); }); @@ -1453,6 +1189,32 @@ describe('payments/index', () => { expect(user.purchased.plan.extraMonths).to.eql(0); }); + it('does not reset gemCapExtra', async () => { + user.purchased.plan.consecutive.gemCapExtra = 12; + + await api.cancelSubscription(data); + + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(12); + }); + + it('initializes gemCapExtra', async () => { + await api.cancelSubscription(data); + expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); + }); + + it('initializes hourglasses', async () => { + await api.cancelSubscription(data); + expect(user.purchased.plan.consecutive.trinkets).to.eql(0); + }); + + it('does not reset owned hourglasses', async () => { + user.purchased.plan.consecutive.trinkets = 12; + + await api.cancelSubscription(data); + + expect(user.purchased.plan.consecutive.trinkets).to.eql(12); + }); + it('sends an email', async () => { await api.cancelSubscription(data); diff --git a/test/api/unit/libs/payments/stripe/oneTimePayments.test.js b/test/api/unit/libs/payments/stripe/oneTimePayments.test.js index c52cd162b4a..7ccb85b8eee 100644 --- a/test/api/unit/libs/payments/stripe/oneTimePayments.test.js +++ b/test/api/unit/libs/payments/stripe/oneTimePayments.test.js @@ -308,6 +308,7 @@ describe('Stripe - One Time Payments', () => { customerId, paymentMethod: 'Gift', gift, + autoRenews: false, gemsBlock: undefined, }); }); diff --git a/test/api/unit/libs/payments/stripe/subscriptions.test.js b/test/api/unit/libs/payments/stripe/subscriptions.test.js index c135f94b276..3955037c020 100644 --- a/test/api/unit/libs/payments/stripe/subscriptions.test.js +++ b/test/api/unit/libs/payments/stripe/subscriptions.test.js @@ -173,6 +173,7 @@ describe('Stripe - Subscriptions', () => { paymentMethod: 'Stripe', sub: sinon.match({ ...sub }), groupId: null, + autoRenews: true, }); }); @@ -197,6 +198,7 @@ describe('Stripe - Subscriptions', () => { paymentMethod: 'Stripe', sub: sinon.match({ ...sub }), groupId, + autoRenews: true, }); }); @@ -231,6 +233,7 @@ describe('Stripe - Subscriptions', () => { paymentMethod: 'Stripe', sub: sinon.match({ ...sub }), groupId, + autoRenews: true, }); }); }); diff --git a/test/api/v3/integration/user/buy/POST-user_buy_mystery_set.test.js b/test/api/v3/integration/user/buy/POST-user_buy_mystery_set.test.js index d4df2c93b3f..cd0576d13a6 100644 --- a/test/api/v3/integration/user/buy/POST-user_buy_mystery_set.test.js +++ b/test/api/v3/integration/user/buy/POST-user_buy_mystery_set.test.js @@ -31,7 +31,7 @@ describe('POST /user/buy-mystery-set/:key', () => { expect(res.data).to.eql({ items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared - purchasedPlanConsecutive: user.purchased.plan.consecutive, + purchasedPlanConsecutive: JSON.parse(JSON.stringify(user.purchased.plan.consecutive)), }); expect(res.message).to.equal(t('hourglassPurchaseSet')); }); diff --git a/test/common/libs/cron.test.js b/test/common/libs/cron.test.js index e8be92fceb5..c2e8f7b32bb 100644 --- a/test/common/libs/cron.test.js +++ b/test/common/libs/cron.test.js @@ -183,8 +183,6 @@ describe('cron utility functions', () => { }); describe('getPlanContext', () => { - const now = new Date(2022, 5, 1); - function baseUserData (count, offset, planId) { return { purchased: { @@ -192,7 +190,7 @@ describe('cron utility functions', () => { consecutive: { count, offset, - gemCapExtra: 25, + gemCapExtra: 26, trinkets: 19, }, quantity: 1, @@ -213,52 +211,19 @@ describe('cron utility functions', () => { }; } - it('monthly plan, next date in 3 months', () => { + it('elapsedMonths is 0 if its the same month', () => { const user = baseUserData(60, 0, 'group_plan_auto'); - user.purchased.plan.perkMonthCount = 0; - - const planContext = getPlanContext(user, now); - - expect(planContext.nextHourglassDate) - .to.be.sameMoment('2022-08-10T02:00:00.144Z'); - }); - it('monthly plan, next date in 1 month', () => { - const user = baseUserData(62, 0, 'group_plan_auto'); - user.purchased.plan.perkMonthCount = 2; - - const planContext = getPlanContext(user, now); - - expect(planContext.nextHourglassDate) - .to.be.sameMoment('2022-06-10T02:00:00.144Z'); + const planContext = getPlanContext(user, new Date(2022, 4, 20)); + expect(planContext.elapsedMonths).to.equal(0); }); - it('multi-month plan, no offset', () => { - const user = baseUserData(60, 0, 'basic_3mo'); - - const planContext = getPlanContext(user, now); - - expect(planContext.nextHourglassDate) - .to.be.sameMoment('2022-06-10T02:00:00.144Z'); - }); - - it('multi-month plan with offset', () => { - const user = baseUserData(60, 1, 'basic_3mo'); - - const planContext = getPlanContext(user, now); - - expect(planContext.nextHourglassDate) - .to.be.sameMoment('2022-07-10T02:00:00.144Z'); - }); - - it('multi-month plan with perk count', () => { - const user = baseUserData(60, 1, 'basic_3mo'); - user.purchased.plan.perkMonthCount = 2; + it('elapsedMonths is 1 after one month', () => { + const user = baseUserData(60, 0, 'group_plan_auto'); - const planContext = getPlanContext(user, now); + const planContext = getPlanContext(user, new Date(2022, 5, 11)); - expect(planContext.nextHourglassDate) - .to.be.sameMoment('2022-07-10T02:00:00.144Z'); + expect(planContext.elapsedMonths).to.equal(1); }); }); }); diff --git a/website/client/src/assets/css/sprites/spritesmith-main.css b/website/client/src/assets/css/sprites/spritesmith-main.css index 3c87e3e11c7..b2fb062682a 100644 --- a/website/client/src/assets/css/sprites/spritesmith-main.css +++ b/website/client/src/assets/css/sprites/spritesmith-main.css @@ -40127,6 +40127,11 @@ width: 28px; height: 28px; } +.notif_subscriber_reward { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/notif_subscriber_reward.png'); + width: 28px; + height: 28px; +} .npc_bailey { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/npc_bailey.png'); width: 60px; diff --git a/website/client/src/assets/images/confetti.png b/website/client/src/assets/images/confetti.png new file mode 100644 index 00000000000..f2bcbc52e35 Binary files /dev/null and b/website/client/src/assets/images/confetti.png differ diff --git a/website/client/src/assets/images/subscriber-food.png b/website/client/src/assets/images/subscriber-food.png deleted file mode 100644 index 88fc52f7ccb..00000000000 Binary files a/website/client/src/assets/images/subscriber-food.png and /dev/null differ diff --git a/website/client/src/assets/scss/colors.scss b/website/client/src/assets/scss/colors.scss index 9ee2894a5af..4e1536efa24 100644 --- a/website/client/src/assets/scss/colors.scss +++ b/website/client/src/assets/scss/colors.scss @@ -78,15 +78,3 @@ $gold-color: #FFA624; $hourglass-color: #2995CD; $purple-task: #925cf3; - -.gray-200 { - color: $gray-200 !important; -} - -.purple-300 { - color: $purple-300 !important; -} - -.white { - color: $white !important; -} diff --git a/website/client/src/assets/scss/typography.scss b/website/client/src/assets/scss/typography.scss index 970fd417370..59b206aafd2 100644 --- a/website/client/src/assets/scss/typography.scss +++ b/website/client/src/assets/scss/typography.scss @@ -86,3 +86,91 @@ h4 { .opacity-75 { opacity: 0.75; } + +.bg-gray-100 { + background-color: $gray-100 !important; +} + +.bg-gray-300 { + background-color: $gray-300 !important; +} + +.bg-gray-600 { + background-color: $gray-600 !important; +} + +.bg-gray-700 { + background-color: $gray-700 !important; +} + +.bg-green-10 { + background-color: $green-10 !important; +} + +.bg-green-100 { + background-color: $green-100 !important; +} + +.bg-purple-100 { + background-color: $purple-100 !important; +} + +.bg-purple-300 { + background-color: $purple-300 !important; +} + +.bg-white { + background-color: $white !important; +} + +.gray-10 { + color: $gray-10 !important; +} + +.gray-50 { + color: $gray-50 !important; +} + +.gray-200 { + color: $gray-200 !important; +} + +.gray-300 { + color: $gray-300 !important; +} + +.green-10 { + color: $green-10 !important; +} + +.maroon-50 { + color: $maroon-50 !important; +} + +.purple-200 { + color: $purple-200 !important; +} + +.purple-300 { + color: $purple-300 !important; +} + +.purple-600 { + color: $purple-600 !important; +} + +.teal-1 { + color: $teal-1 !important; +} + +.teal-10 { + color: $teal-10 !important; +} + +.yellow-10 { + color: $yellow-10 !important; +} + +.white { + color: $white !important; +} diff --git a/website/client/src/assets/svg/divider-stars.svg b/website/client/src/assets/svg/divider-stars.svg new file mode 100644 index 00000000000..648001dab4d --- /dev/null +++ b/website/client/src/assets/svg/divider-stars.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/habitica-logo.svg b/website/client/src/assets/svg/habitica-logo.svg index 54b2acc5a19..01d80fb183d 100644 --- a/website/client/src/assets/svg/habitica-logo.svg +++ b/website/client/src/assets/svg/habitica-logo.svg @@ -1,7 +1,5 @@ - - - - - - + + + + diff --git a/website/client/src/assets/svg/hourglass-sparkle-left.svg b/website/client/src/assets/svg/hourglass-sparkle-left.svg new file mode 100644 index 00000000000..c4d221fbb8d --- /dev/null +++ b/website/client/src/assets/svg/hourglass-sparkle-left.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/hourglass-sparkle-right.svg b/website/client/src/assets/svg/hourglass-sparkle-right.svg new file mode 100644 index 00000000000..0388d0d7364 --- /dev/null +++ b/website/client/src/assets/svg/hourglass-sparkle-right.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/jackalope.svg b/website/client/src/assets/svg/jackalope.svg new file mode 100644 index 00000000000..5e351c4955f --- /dev/null +++ b/website/client/src/assets/svg/jackalope.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/website/client/src/assets/svg/stars-purple.svg b/website/client/src/assets/svg/stars-purple.svg new file mode 100644 index 00000000000..a6c799848dd --- /dev/null +++ b/website/client/src/assets/svg/stars-purple.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/subscriber-food.svg b/website/client/src/assets/svg/subscriber-food.svg new file mode 100644 index 00000000000..7991bab49fe --- /dev/null +++ b/website/client/src/assets/svg/subscriber-food.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/subscriber-gems.svg b/website/client/src/assets/svg/subscriber-gems.svg index e57077f7d86..844f13598ec 100644 --- a/website/client/src/assets/svg/subscriber-gems.svg +++ b/website/client/src/assets/svg/subscriber-gems.svg @@ -1,26 +1,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/assets/svg/subscriber-hourglasses.svg b/website/client/src/assets/svg/subscriber-hourglasses.svg index 79e993b4c64..27d8c948228 100644 --- a/website/client/src/assets/svg/subscriber-hourglasses.svg +++ b/website/client/src/assets/svg/subscriber-hourglasses.svg @@ -1,26 +1,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/website/client/src/components/achievements/armoireEmpty.vue b/website/client/src/components/achievements/armoireEmpty.vue index f4aa5a008d3..6c2d8f3c505 100644 --- a/website/client/src/components/achievements/armoireEmpty.vue +++ b/website/client/src/components/achievements/armoireEmpty.vue @@ -8,7 +8,7 @@ - + {{ $t('armoireLastItem') }} {{ $t('armoireNotesEmpty') }} @@ -34,7 +34,12 @@
{{ $t('armoireLastItem') }}
{{ $t('armoireNotesEmpty') }}