diff --git a/test/api/v3/integration/challenges/GET-challenges_challengeId_export_csv.test.js b/test/api/v3/integration/challenges/GET-challenges_challengeId_export_csv.test.js index d82bc498ad..a683513180 100644 --- a/test/api/v3/integration/challenges/GET-challenges_challengeId_export_csv.test.js +++ b/test/api/v3/integration/challenges/GET-challenges_challengeId_export_csv.test.js @@ -60,9 +60,9 @@ describe('GET /challenges/:challengeId/export/csv', () => { }); it('should return a valid CSV file with export data', async () => { - let res = await members[0].get(`/challenges/${challenge._id}/export/csv`); - let sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id'); - let splitRes = res.split('\n'); + const res = await members[0].get(`/challenges/${challenge._id}/export/csv`); + const sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id'); + const splitRes = res.split('\n'); expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak'); expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`); @@ -71,4 +71,16 @@ describe('GET /challenges/:challengeId/export/csv', () => { expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`); expect(splitRes[5]).to.equal(''); }); + + it('should successfully return when it contains erroneous residue user data', async () => { + await members[0].update({challenges: []}); + const res = await members[1].get(`/challenges/${challenge._id}/export/csv`); + const sortedMembers = _.sortBy([members[1], members[2], groupLeader], '_id'); + const splitRes = res.split('\n'); + expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak'); + expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`); + expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`); + expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`); + expect(splitRes[4]).to.equal(''); + }); }); diff --git a/website/server/controllers/api-v3/challenges.js b/website/server/controllers/api-v3/challenges.js index 0d6d2a4e50..8beca520f4 100644 --- a/website/server/controllers/api-v3/challenges.js +++ b/website/server/controllers/api-v3/challenges.js @@ -579,6 +579,16 @@ api.exportChallengeCsv = { let lastUserId; let index = -1; tasks.forEach(task => { + /** + * Occasional error does not unlink a user's challenge tasks from that challenge's data + * after the user leaves that challenge, which previously caused a failure when exporting + * to a CSV. The following if statement makes sure that the task's attached user still + * belongs to the challenge. + * See more at https://github.com/HabitRPG/habitica/issues/8350 + */ + if (!resArray.map(line => line[0]).includes(task.userId)) { + return; + } while (task.userId !== lastUserId) { index++; lastUserId = resArray[index][0]; // resArray[index][0] is an user id