diff --git a/test/api/v3/unit/libs/cron.test.js b/test/api/v3/unit/libs/cron.test.js index d4629b9104..19268c59f7 100644 --- a/test/api/v3/unit/libs/cron.test.js +++ b/test/api/v3/unit/libs/cron.test.js @@ -439,16 +439,30 @@ describe('cron', () => { expect(user.history.exp[0].value).to.equal(150); }); - it('increments perfect day achievement', () => { + it('increments perfect day achievement if all (at least 1) due dailies were completed', () => { + daysMissed = 1; tasksByType.dailys[0].completed = true; + tasksByType.dailys[0].startDate = moment(new Date()).subtract({days: 1}); cron({user, tasksByType, daysMissed, analytics}); expect(user.achievements.perfect).to.equal(1); }); - it('increments user buffs if they have a perfect day', () => { + it('does not increment perfect day achievement if no due dailies', () => { + daysMissed = 1; tasksByType.dailys[0].completed = true; + tasksByType.dailys[0].startDate = moment(new Date()).add({days: 1}); + + cron({user, tasksByType, daysMissed, analytics}); + + expect(user.achievements.perfect).to.equal(0); + }); + + it('increments user buffs if all (at least 1) due dailies were completed', () => { + daysMissed = 1; + tasksByType.dailys[0].completed = true; + tasksByType.dailys[0].startDate = moment(new Date()).subtract({days: 1}); let previousBuffs = clone(user.stats.buffs); @@ -460,24 +474,31 @@ describe('cron', () => { expect(user.stats.buffs.con).to.be.greaterThan(previousBuffs.con); }); - it('still grants a perfect day when CRON_SAFE_MODE is set', () => { - sandbox.stub(nconf, 'get').withArgs('CRON_SAFE_MODE').returns('true'); - let cronOverride = requireAgain(pathToCronLib).cron; + it('clears buffs if user does not have a perfect day (no due dailys)', () => { daysMissed = 1; - tasksByType.dailys[0].completed = false; - tasksByType.dailys[0].startDate = moment(new Date()).subtract({days: 1}); + tasksByType.dailys[0].completed = true; + tasksByType.dailys[0].startDate = moment(new Date()).add({days: 1}); - let previousBuffs = clone(user.stats.buffs); + user.stats.buffs = { + str: 1, + int: 1, + per: 1, + con: 1, + stealth: 0, + streaks: true, + }; - cronOverride({user, tasksByType, daysMissed, analytics}); + cron({user, tasksByType, daysMissed, analytics}); - expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str); - expect(user.stats.buffs.int).to.be.greaterThan(previousBuffs.int); - expect(user.stats.buffs.per).to.be.greaterThan(previousBuffs.per); - expect(user.stats.buffs.con).to.be.greaterThan(previousBuffs.con); + expect(user.stats.buffs.str).to.equal(0); + expect(user.stats.buffs.int).to.equal(0); + expect(user.stats.buffs.per).to.equal(0); + expect(user.stats.buffs.con).to.equal(0); + expect(user.stats.buffs.stealth).to.equal(0); + expect(user.stats.buffs.streaks).to.be.false; }); - it('clears buffs if user does not have a perfect day', () => { + it('clears buffs if user does not have a perfect day (at least one due daily not completed)', () => { daysMissed = 1; tasksByType.dailys[0].completed = false; tasksByType.dailys[0].startDate = moment(new Date()).subtract({days: 1}); @@ -500,6 +521,23 @@ describe('cron', () => { expect(user.stats.buffs.stealth).to.equal(0); expect(user.stats.buffs.streaks).to.be.false; }); + + it('still grants a perfect day when CRON_SAFE_MODE is set', () => { + sandbox.stub(nconf, 'get').withArgs('CRON_SAFE_MODE').returns('true'); + let cronOverride = requireAgain(pathToCronLib).cron; + daysMissed = 1; + tasksByType.dailys[0].completed = false; + tasksByType.dailys[0].startDate = moment(new Date()).subtract({days: 1}); + + let previousBuffs = clone(user.stats.buffs); + + cronOverride({user, tasksByType, daysMissed, analytics}); + + expect(user.stats.buffs.str).to.be.greaterThan(previousBuffs.str); + expect(user.stats.buffs.int).to.be.greaterThan(previousBuffs.int); + expect(user.stats.buffs.per).to.be.greaterThan(previousBuffs.per); + expect(user.stats.buffs.con).to.be.greaterThan(previousBuffs.con); + }); }); describe('adding mp', () => { diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index 704adcba02..cc451a84a8 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -118,7 +118,7 @@ export function cron (options = {}) { // User is only allowed a certain number of drops a day. This resets the count. if (user.items.lastDrop.count > 0) user.items.lastDrop.count = 0; - // "Perfect Day" achievement for perfect-days + // "Perfect Day" achievement for perfect days let perfect = true; if (user.isSubscribed()) { @@ -155,6 +155,7 @@ export function cron (options = {}) { // For incomplete Dailys, add value (further incentive), deduct health, keep records for later decreasing the nightly mana gain let dailyChecked = 0; // how many dailies were checked? let dailyDueUnchecked = 0; // how many dailies were un-checked? + let atLeastOneDailyDue = false; // were any dailies due? if (!user.party.quest.progress.down) user.party.quest.progress.down = 0; tasksByType.dailys.forEach((task) => { @@ -165,6 +166,10 @@ export function cron (options = {}) { if (completed) { dailyChecked += 1; + if (!atLeastOneDailyDue) { // only bother checking until the first thing is found + let thatDay = moment(now).subtract({days: daysMissed}); + atLeastOneDailyDue = shouldDo(thatDay.toDate(), task, user.preferences); + } } else { // dailys repeat, so need to calculate how many they've missed according to their own schedule scheduleMisses = 0; @@ -173,6 +178,7 @@ export function cron (options = {}) { let thatDay = moment(now).subtract({days: i + 1}); if (shouldDo(thatDay.toDate(), task, user.preferences)) { + atLeastOneDailyDue = true; scheduleMisses++; if (user.stats.buffs.stealth) { user.stats.buffs.stealth--; @@ -256,7 +262,7 @@ export function cron (options = {}) { // TODO also do while resting in the inn. Note that later we'll be allowing the value/color of tasks to change while sleeping (https://github.com/HabitRPG/habitrpg/issues/5232), so the code in performSleepTasks() might be best merged back into here for that. Perhaps wait until then to do preen history for sleeping users. preenUserHistory(user, tasksByType, user.preferences.timezoneOffset); - if (perfect) { + if (perfect && atLeastOneDailyDue) { user.achievements.perfect++; let lvlDiv2 = Math.ceil(common.capByLevel(user.stats.lvl) / 2); user.stats.buffs = {