mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
Compare commits
363 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c64d4b0914 | ||
|
|
f94fd0d69d | ||
|
|
2cd66436bc | ||
|
|
81a17738b8 | ||
|
|
b6b953ec46 | ||
|
|
ab34c83a9d | ||
|
|
9cea86f4e0 | ||
|
|
1b7a705bf9 | ||
|
|
e2c5b9058b | ||
|
|
a2b38ffb02 | ||
|
|
6395870c00 | ||
|
|
9562ba432f | ||
|
|
8a3a83de37 | ||
|
|
745edd731d | ||
|
|
53b195931c | ||
|
|
37ae467fff | ||
|
|
d7d7d64b45 | ||
|
|
e76bdbd62d | ||
|
|
067e869141 | ||
|
|
51224a69d9 | ||
|
|
f56018d46a | ||
|
|
b846185f8a | ||
|
|
ff81e55839 | ||
|
|
9a3cdb5deb | ||
|
|
47ad7305f5 | ||
|
|
d9b5bbe2a9 | ||
|
|
c2fe04367f | ||
|
|
abcc77b7d6 | ||
|
|
07cbf45265 | ||
|
|
c035435476 | ||
|
|
89fdd8a8bb | ||
|
|
d406da4081 | ||
|
|
d74786ef85 | ||
|
|
64a3d08ce3 | ||
|
|
f635f178da | ||
|
|
1a7461a8a2 | ||
|
|
cc13c4f28e | ||
|
|
239f78674b | ||
|
|
d691dee2ca | ||
|
|
481bd6727d | ||
|
|
d0fc1e0751 | ||
|
|
b07dbb7752 | ||
|
|
34e7690c38 | ||
|
|
eca7382545 | ||
|
|
be95cd967a | ||
|
|
ce03f837c7 | ||
|
|
808885425f | ||
|
|
39a35f44ef | ||
|
|
2b2e1d4b9a | ||
|
|
869411c0e9 | ||
|
|
7484ecf729 | ||
|
|
3265440bc4 | ||
|
|
acf514e9cb | ||
|
|
2789d44dbf | ||
|
|
b579f31e9e | ||
|
|
5e781017ab | ||
|
|
b48f850eac | ||
|
|
5d6b6ed29a | ||
|
|
7fbc68511b | ||
|
|
ee2858199b | ||
|
|
b1dd79f75c | ||
|
|
1ac4dd8171 | ||
|
|
4f86abd6b2 | ||
|
|
23b0688abb | ||
|
|
38efe83cc7 | ||
|
|
2dadd74097 | ||
|
|
9f494360ef | ||
|
|
4122bbdecf | ||
|
|
256a3abc26 | ||
|
|
b7f3c0f389 | ||
|
|
dca00bf4b7 | ||
|
|
d31c8913d3 | ||
|
|
d839d57299 | ||
|
|
ccc3b4d337 | ||
|
|
7195ac15b9 | ||
|
|
a5ef6a129e | ||
|
|
38f5d63d29 | ||
|
|
43194b71ce | ||
|
|
e4a347a3cb | ||
|
|
7eaf3e04ab | ||
|
|
b6b03751c4 | ||
|
|
818d5e4eb6 | ||
|
|
f871c7cf63 | ||
|
|
e9eddec0c4 | ||
|
|
a48a6a292d | ||
|
|
12aef475c8 | ||
|
|
112e4e1d76 | ||
|
|
90eebbcd70 | ||
|
|
1ad9ba4e71 | ||
|
|
c42b72f8a8 | ||
|
|
830c8d3104 | ||
|
|
86ae5f3e44 | ||
|
|
3922415314 | ||
|
|
6c71abfac8 | ||
|
|
6ab08a7d52 | ||
|
|
dc46127fc7 | ||
|
|
b54f031acd | ||
|
|
eafa2f8cdd | ||
|
|
fe45940d46 | ||
|
|
7dac53867b | ||
|
|
6f64cb7d9b | ||
|
|
7d989bcf50 | ||
|
|
7bd29c2dd7 | ||
|
|
9e10490102 | ||
|
|
5792bc0000 | ||
|
|
06812878b5 | ||
|
|
8714c7d162 | ||
|
|
e66f4e7812 | ||
|
|
c73f565f65 | ||
|
|
82e21df943 | ||
|
|
18ed148320 | ||
|
|
a5fc909f0d | ||
|
|
30a5192e19 | ||
|
|
79c0499672 | ||
|
|
dadb752087 | ||
|
|
37b29d3449 | ||
|
|
bb2ed249b9 | ||
|
|
bb90dde1b6 | ||
|
|
5299c8d406 | ||
|
|
8b81e38538 | ||
|
|
8e05a1b489 | ||
|
|
aafcbe60a3 | ||
|
|
56d1b77215 | ||
|
|
61da558a5d | ||
|
|
490531cc76 | ||
|
|
8cd4c502bc | ||
|
|
d8cacb653e | ||
|
|
59436a8bf7 | ||
|
|
6fade19f27 | ||
|
|
15d028a281 | ||
|
|
95b283676a | ||
|
|
6e7e81206a | ||
|
|
af74cc7c64 | ||
|
|
a9e2a17077 | ||
|
|
92057dbe17 | ||
|
|
b4ab525be5 | ||
|
|
d3c464d5ea | ||
|
|
804fe1c6d5 | ||
|
|
3c5025a78e | ||
|
|
e028232527 | ||
|
|
e3b270a62e | ||
|
|
fceeacec3b | ||
|
|
f93c67e57c | ||
|
|
192dc26fbe | ||
|
|
1c1f270f64 | ||
|
|
483768f4a7 | ||
|
|
65031cef3a | ||
|
|
2fc1f46359 | ||
|
|
30fd530576 | ||
|
|
f79999fde7 | ||
|
|
90d6e443ba | ||
|
|
4ed1082558 | ||
|
|
00717eda76 | ||
|
|
d1b86e6c14 | ||
|
|
c813afba44 | ||
|
|
d49db6d367 | ||
|
|
d6835aec56 | ||
|
|
960f7b5886 | ||
|
|
cd9630332d | ||
|
|
ed21a37e5a | ||
|
|
16256ee190 | ||
|
|
ff57e31f4f | ||
|
|
6e21d154ae | ||
|
|
fdecc8ce16 | ||
|
|
3cc49f6637 | ||
|
|
47f49f4256 | ||
|
|
4f4bb52360 | ||
|
|
3748b3046b | ||
|
|
5cd0f56811 | ||
|
|
fe5beac91b | ||
|
|
52fd6a1451 | ||
|
|
ae445555e9 | ||
|
|
c4fc6671b4 | ||
|
|
e7a096158e | ||
|
|
98473fcfaa | ||
|
|
e4300fc714 | ||
|
|
456c5e57bc | ||
|
|
ffba435923 | ||
|
|
1f44444a50 | ||
|
|
185b20995a | ||
|
|
fdf2e590ea | ||
|
|
994123c387 | ||
|
|
273590716c | ||
|
|
6818a094ee | ||
|
|
c99855cef4 | ||
|
|
6845943ed0 | ||
|
|
044fe17757 | ||
|
|
ad0ede8d01 | ||
|
|
23815e89e1 | ||
|
|
061d990e39 | ||
|
|
71f4e6bc08 | ||
|
|
659f160e22 | ||
|
|
5f27bc5f90 | ||
|
|
074837b274 | ||
|
|
cfd19ac694 | ||
|
|
0897ab5dc9 | ||
|
|
5c6e8a7331 | ||
|
|
c576c5261e | ||
|
|
bbd98517ff | ||
|
|
392b54aa7b | ||
|
|
60b26d4ec0 | ||
|
|
aa517e0ad6 | ||
|
|
5ca489dee7 | ||
|
|
fe39ef72ff | ||
|
|
eee5f2f1df | ||
|
|
fd8572c28a | ||
|
|
f161987e1e | ||
|
|
2304d970a5 | ||
|
|
25ed05ab0a | ||
|
|
fa1fef11d6 | ||
|
|
6f5b9ef119 | ||
|
|
c64ea0a9a9 | ||
|
|
2e36b896d4 | ||
|
|
6fe73d431e | ||
|
|
998621cefe | ||
|
|
67bb179c25 | ||
|
|
c875861dab | ||
|
|
418c18ddb2 | ||
|
|
0caab5c8d0 | ||
|
|
218e65b04b | ||
|
|
fcd7ba77a7 | ||
|
|
b0d177643c | ||
|
|
c0e0b10a95 | ||
|
|
0bee2caf2e | ||
|
|
e56d097b3a | ||
|
|
8c63a9e31f | ||
|
|
28ed9d8bcc | ||
|
|
36ead77e0c | ||
|
|
e7969987ec | ||
|
|
97021e3422 | ||
|
|
218d47d64a | ||
|
|
bdfc23717e | ||
|
|
464cd87736 | ||
|
|
67a8eebb96 | ||
|
|
cfc0f6a3ac | ||
|
|
9f76db12bd | ||
|
|
70192e4935 | ||
|
|
5cd4ead9d1 | ||
|
|
87cd000bb8 | ||
|
|
0de5d8273b | ||
|
|
379898cc4d | ||
|
|
adeaa6c754 | ||
|
|
539f0e33e2 | ||
|
|
405e053377 | ||
|
|
52fbb8f899 | ||
|
|
c880596a77 | ||
|
|
a35f04be46 | ||
|
|
8682cf1cf7 | ||
|
|
6e922cfb44 | ||
|
|
cafabd93e1 | ||
|
|
1001d48eb7 | ||
|
|
b5c4618d56 | ||
|
|
92a4ba93d2 | ||
|
|
90d35d2f1f | ||
|
|
fead027cd2 | ||
|
|
5578426985 | ||
|
|
1c39fae127 | ||
|
|
45a757b589 | ||
|
|
8b610d771c | ||
|
|
bd81d27145 | ||
|
|
8eb430cbcb | ||
|
|
f218133d25 | ||
|
|
5f440d9097 | ||
|
|
0294868747 | ||
|
|
9c8d870d16 | ||
|
|
a7acd863f3 | ||
|
|
f32ef0a6ba | ||
|
|
ebf3b4aa47 | ||
|
|
5a8366468b | ||
|
|
df57518815 | ||
|
|
7d342b5115 | ||
|
|
388de9a97d | ||
|
|
28c79d9d20 | ||
|
|
85cf322b30 | ||
|
|
362ca73c94 | ||
|
|
5632031f16 | ||
|
|
90273362c4 | ||
|
|
7aadc10fab | ||
|
|
bfd45596b5 | ||
|
|
2eed4d38ae | ||
|
|
29bbe8534b | ||
|
|
9e008890b2 | ||
|
|
5505bf1e45 | ||
|
|
d40781ce07 | ||
|
|
d9719cdc05 | ||
|
|
8cc6a96be0 | ||
|
|
c5fb2d6506 | ||
|
|
afc336461e | ||
|
|
31376c8461 | ||
|
|
3a849bac18 | ||
|
|
563f3e2012 | ||
|
|
e24a024091 | ||
|
|
dc7d3816fd | ||
|
|
a094e13352 | ||
|
|
83376a38de | ||
|
|
db9c13a05d | ||
|
|
8c8aa78a1a | ||
|
|
6e3f7c005a | ||
|
|
1395380dfe | ||
|
|
833ceb3bf3 | ||
|
|
0522aa1551 | ||
|
|
58a9e4a439 | ||
|
|
84e2b2f45e | ||
|
|
71c0939a15 | ||
|
|
26c8323e70 | ||
|
|
bb7d447003 | ||
|
|
97ea510a34 | ||
|
|
99610b4916 | ||
|
|
9a43b85492 | ||
|
|
ecbf39cee4 | ||
|
|
4394772ee3 | ||
|
|
dd7fa73961 | ||
|
|
6ec23ce790 | ||
|
|
b953519e2d | ||
|
|
33a8072d23 | ||
|
|
213316d807 | ||
|
|
44cd4d0708 | ||
|
|
063b7a9af0 | ||
|
|
c08b5a4f1e | ||
|
|
90117625d7 | ||
|
|
1b3dad749e | ||
|
|
a622a3ebe3 | ||
|
|
2c83c16644 | ||
|
|
1034675184 | ||
|
|
a420876697 | ||
|
|
dc265e26b3 | ||
|
|
b5203dda61 | ||
|
|
80e92a8767 | ||
|
|
1c51e62e43 | ||
|
|
a265bfac9d | ||
|
|
92e4d5cd68 | ||
|
|
a18e9b3b18 | ||
|
|
c1a6ba6242 | ||
|
|
ed761a8b7b | ||
|
|
81d5971829 | ||
|
|
eb2d320d1f | ||
|
|
67538a368e | ||
|
|
d55b95834d | ||
|
|
9ff9cd3b35 | ||
|
|
b1f24de3c4 | ||
|
|
2ce2100f89 | ||
|
|
dbaae4183e | ||
|
|
2009bb97cb | ||
|
|
3fc9501bac | ||
|
|
c845c337df | ||
|
|
418b57f9fb | ||
|
|
9725da258e | ||
|
|
4191ea1968 | ||
|
|
8ffe302a49 | ||
|
|
5c50a40f39 | ||
|
|
84329e5fad | ||
|
|
64507a161e | ||
|
|
0f7fc27663 | ||
|
|
07bc374078 | ||
|
|
8153674dc0 | ||
|
|
e4dbf09dda | ||
|
|
c862bdb76a | ||
|
|
b596576c53 | ||
|
|
f049d29d1b | ||
|
|
9fd26a88ea | ||
|
|
76860fe3f8 | ||
|
|
b16e700de5 | ||
|
|
b75e65f42d |
@@ -17,7 +17,7 @@
|
|||||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
||||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
||||||
"NODE_ENV":"development",
|
"NODE_ENV":"development",
|
||||||
"ENABLE_CONSOLE_LOGS_IN_TEST": false,
|
"ENABLE_CONSOLE_LOGS_IN_TEST": "false",
|
||||||
"CRON_SAFE_MODE":"false",
|
"CRON_SAFE_MODE":"false",
|
||||||
"CRON_SEMI_SAFE_MODE":"false",
|
"CRON_SEMI_SAFE_MODE":"false",
|
||||||
"MAINTENANCE_MODE": "false",
|
"MAINTENANCE_MODE": "false",
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
||||||
"GA_ID": "GA_ID",
|
"GA_ID": "GA_ID",
|
||||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||||
|
"AMPLITUDE_SECRET": "AMPLITUDE_SECRET",
|
||||||
"AMAZON_PAYMENTS": {
|
"AMAZON_PAYMENTS": {
|
||||||
"SELLER_ID": "SELLER_ID",
|
"SELLER_ID": "SELLER_ID",
|
||||||
"CLIENT_ID": "CLIENT_ID",
|
"CLIENT_ID": "CLIENT_ID",
|
||||||
@@ -88,12 +89,6 @@
|
|||||||
"USERNAME": "admin",
|
"USERNAME": "admin",
|
||||||
"PASSWORD": "password"
|
"PASSWORD": "password"
|
||||||
},
|
},
|
||||||
"PUSHER": {
|
|
||||||
"ENABLED": "false",
|
|
||||||
"APP_ID": "appId",
|
|
||||||
"KEY": "key",
|
|
||||||
"SECRET": "secret"
|
|
||||||
},
|
|
||||||
"SLACK": {
|
"SLACK": {
|
||||||
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
"FLAGGING_URL": "https://hooks.slack.com/services/id/id/id",
|
||||||
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
"FLAGGING_FOOTER_LINK": "https://habitrpg.github.io/flag-o-rama/",
|
||||||
|
|||||||
48
database_reports/20181001_backtoschool_challenge.js
Normal file
48
database_reports/20181001_backtoschool_challenge.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output data on users who completed all the To-Do tasks in the 2018 Back-to-School Challenge.
|
||||||
|
* User ID,Profile Name
|
||||||
|
*/
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
const CHALLENGE_ID = '0acb1d56-1660-41a4-af80-9259f080b62b';
|
||||||
|
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
let dbTasks = monk(CONNECTION_STRING).get('tasks', { castIds: false });
|
||||||
|
|
||||||
|
function usersReport() {
|
||||||
|
console.info('User ID,Profile Name');
|
||||||
|
let userCount = 0;
|
||||||
|
|
||||||
|
dbUsers.find(
|
||||||
|
{challenges: CHALLENGE_ID},
|
||||||
|
{fields:
|
||||||
|
{_id: 1, 'profile.name': 1}
|
||||||
|
},
|
||||||
|
).each((user, {close, pause, resume}) => {
|
||||||
|
pause();
|
||||||
|
userCount++;
|
||||||
|
let completedTodos = 0;
|
||||||
|
return dbTasks.find(
|
||||||
|
{
|
||||||
|
userId: user._id,
|
||||||
|
'challenge.id': CHALLENGE_ID,
|
||||||
|
type: 'todo',
|
||||||
|
},
|
||||||
|
{fields: {completed: 1}}
|
||||||
|
).each((task) => {
|
||||||
|
if (task.completed) completedTodos++;
|
||||||
|
}).then(() => {
|
||||||
|
if (completedTodos >= 7) {
|
||||||
|
console.info(`${user._id},${user.profile.name}`);
|
||||||
|
}
|
||||||
|
resume();
|
||||||
|
});
|
||||||
|
}).then(() => {
|
||||||
|
console.info(`${userCount} users reviewed`);
|
||||||
|
return process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = usersReport;
|
||||||
147
migrations/archive/2018/20180811_inboxOutsideUser.js
Normal file
147
migrations/archive/2018/20180811_inboxOutsideUser.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
const migrationName = '20180811_inboxOutsideUser.js';
|
||||||
|
const authorName = 'paglias'; // in case script author needs to know when their ...
|
||||||
|
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move inbox messages from the user model to their own collection
|
||||||
|
*/
|
||||||
|
|
||||||
|
const monk = require('monk');
|
||||||
|
const nconf = require('nconf');
|
||||||
|
const uuid = require('uuid').v4;
|
||||||
|
|
||||||
|
const Inbox = require('../website/server/models/message').inboxModel;
|
||||||
|
const connectionString = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
|
||||||
|
const dbInboxes = monk(connectionString).get('inboxes', { castIds: false });
|
||||||
|
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: migrationName},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 1000,
|
||||||
|
fields: ['_id', 'inbox'],
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
let msgCount = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users and their tasks found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let usersPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(usersPromises)
|
||||||
|
.then(() => {
|
||||||
|
return processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||||
|
if (msgCount % progressCount === 0) console.warn(`${msgCount } messages processed`);
|
||||||
|
if (user._id === authorUuid) console.warn(`${authorName } being processed`);
|
||||||
|
|
||||||
|
const oldInboxMessages = user.inbox.messages || {};
|
||||||
|
const oldInboxMessagesIds = Object.keys(oldInboxMessages);
|
||||||
|
|
||||||
|
msgCount += oldInboxMessagesIds.length;
|
||||||
|
|
||||||
|
const newInboxMessages = oldInboxMessagesIds.map(msgId => {
|
||||||
|
const msg = oldInboxMessages[msgId];
|
||||||
|
if (!msg || (!msg.id && !msg._id)) { // eslint-disable-line no-extra-parens
|
||||||
|
console.log('missing message or message _id and id', msg);
|
||||||
|
throw new Error('error!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.id && !msg._id) msg._id = msg.id;
|
||||||
|
if (msg._id && !msg.id) msg.id = msg._id;
|
||||||
|
|
||||||
|
const newMsg = new Inbox(msg);
|
||||||
|
newMsg.ownerId = user._id;
|
||||||
|
return newMsg.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
const promises = newInboxMessages.map(newMsg => {
|
||||||
|
return (async function fn () {
|
||||||
|
const existing = await dbInboxes.find({_id: newMsg._id});
|
||||||
|
|
||||||
|
if (existing.length > 0) {
|
||||||
|
if (
|
||||||
|
existing[0].ownerId === newMsg.ownerId &&
|
||||||
|
existing[0].text === newMsg.text &&
|
||||||
|
existing[0].uuid === newMsg.uuid &&
|
||||||
|
existing[0].sent === newMsg.sent
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
newMsg.id = newMsg._id = uuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMsg;
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then((filteredNewMsg) => {
|
||||||
|
filteredNewMsg = filteredNewMsg.filter(m => Boolean(m && m.id && m._id && m.id == m._id));
|
||||||
|
return dbInboxes.insert(filteredNewMsg);
|
||||||
|
}).then(() => {
|
||||||
|
return dbUsers.update({_id: user._id}, {
|
||||||
|
$set: {
|
||||||
|
migration: migrationName,
|
||||||
|
'inbox.messages': {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${ count } users processed\n`);
|
||||||
|
console.warn(`\n${ msgCount } messages processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
107
migrations/archive/2018/20181002_username_email.js
Normal file
107
migrations/archive/2018/20181002_username_email.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
const MIGRATION_NAME = '20181003_username_email.js';
|
||||||
|
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send emails to eligible users announcing upcoming username changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
import { sendTxn } from '../../website/server/libs/email';
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2018-04-01')},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 100,
|
||||||
|
fields: [
|
||||||
|
'_id',
|
||||||
|
'auth',
|
||||||
|
'preferences',
|
||||||
|
'profile',
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => delay(7000))
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}});
|
||||||
|
|
||||||
|
sendTxn(
|
||||||
|
user,
|
||||||
|
'username-change',
|
||||||
|
[{name: 'UNSUB_EMAIL_TYPE_URL', content: '/user/settings/notifications?unsubFrom=importantAnnouncements'},
|
||||||
|
{name: 'LOGIN_NAME', content: user.auth.local.username}]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
if (user._id === authorUuid) console.warn(`${authorName} processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay (t, v) {
|
||||||
|
return new Promise(function batchPause (resolve) {
|
||||||
|
setTimeout(resolve.bind(null, v), t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
109
migrations/archive/2018/20181108_username_email.js
Normal file
109
migrations/archive/2018/20181108_username_email.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
const MIGRATION_NAME = '20181108_username_email.js';
|
||||||
|
const AUTHOR_NAME = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
const AUTHOR_UUID = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send emails to eligible users announcing upcoming username changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
import { sendTxn } from '../../../website/server/libs/email';
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'flags.verifiedUsername': {$ne: true},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2018-10-25')},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 100,
|
||||||
|
fields: [
|
||||||
|
'_id',
|
||||||
|
'auth',
|
||||||
|
'preferences',
|
||||||
|
'profile',
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => delay(7000))
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}});
|
||||||
|
|
||||||
|
sendTxn(
|
||||||
|
user,
|
||||||
|
'username-change-follow-up',
|
||||||
|
[{name: 'LOGIN_NAME', content: user.auth.local.username},
|
||||||
|
{name: 'BASE_URL', content: BASE_URL}]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
if (user._id === AUTHOR_UUID) console.warn(`${AUTHOR_NAME} processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay (t, v) {
|
||||||
|
return new Promise(function batchPause (resolve) {
|
||||||
|
setTimeout(resolve.bind(null, v), t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
@@ -17,5 +17,12 @@ function setUpServer () {
|
|||||||
setUpServer();
|
setUpServer();
|
||||||
|
|
||||||
// Replace this with your migration
|
// Replace this with your migration
|
||||||
const processUsers = require('./users/takeThis.js');
|
const processUsers = require('./users/20181122_turkey_day.js');
|
||||||
processUsers();
|
processUsers()
|
||||||
|
.then(function success () {
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch(function failure (err) {
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|||||||
66
migrations/users/20181023_veteran_pet_ladder.js
Normal file
66
migrations/users/20181023_veteran_pet_ladder.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181023_veteran_pet_ladder';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (user.items.pets['Bear-Veteran']) {
|
||||||
|
set['items.pets.Fox-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Lion-Veteran']) {
|
||||||
|
set['items.pets.Bear-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Tiger-Veteran']) {
|
||||||
|
set['items.pets.Lion-Veteran'] = 5;
|
||||||
|
} else if (user.items.pets['Wolf-Veteran']) {
|
||||||
|
set['items.pets.Tiger-Veteran'] = 5;
|
||||||
|
} else {
|
||||||
|
set['items.pets.Wolf-Veteran'] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'flags.verifiedUsername': true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
migration: 1,
|
||||||
|
flags: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
116
migrations/users/20181030_habitoween_ladder.js
Normal file
116
migrations/users/20181030_habitoween_ladder.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Award Habitoween ladder items to participants in this month's Habitoween festivities
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
const MIGRATION_NAME = '20181030_habitoween_ladder.js'; // Update when running in future years
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
const AUTHOR_NAME = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
const AUTHOR_UUID = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2018-10-01')},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [
|
||||||
|
'items.mounts',
|
||||||
|
'items.pets',
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROGRESS_COUNT = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
let set = {};
|
||||||
|
let inc = {
|
||||||
|
'items.food.Candy_Skeleton': 1,
|
||||||
|
'items.food.Candy_Base': 1,
|
||||||
|
'items.food.Candy_CottonCandyBlue': 1,
|
||||||
|
'items.food.Candy_CottonCandyPink': 1,
|
||||||
|
'items.food.Candy_Shade': 1,
|
||||||
|
'items.food.Candy_White': 1,
|
||||||
|
'items.food.Candy_Golden': 1,
|
||||||
|
'items.food.Candy_Zombie': 1,
|
||||||
|
'items.food.Candy_Desert': 1,
|
||||||
|
'items.food.Candy_Red': 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user && user.items && user.items.pets && user.items.mounts['JackOLantern-Ghost']) {
|
||||||
|
set['items.pets.JackOLantern-Glow'] = 5;
|
||||||
|
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
|
||||||
|
set['items.mounts.JackOLantern-Ghost'] = true;
|
||||||
|
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
|
||||||
|
set['items.pets.JackOLantern-Ghost'] = 5;
|
||||||
|
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
|
||||||
|
set['items.mounts.JackOLantern-Base'] = true;
|
||||||
|
} else {
|
||||||
|
set['items.pets.JackOLantern-Base'] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set: set, $inc: inc});
|
||||||
|
|
||||||
|
if (count % PROGRESS_COUNT === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
if (user._id === AUTHOR_UUID) console.warn(`${AUTHOR_NAME} processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
109
migrations/users/20181122_turkey_day.js
Normal file
109
migrations/users/20181122_turkey_day.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const MIGRATION_NAME = '20181122_turkey_day';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { model as User } from '../../website/server/models/user';
|
||||||
|
|
||||||
|
const progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
async function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
const set = {};
|
||||||
|
let push;
|
||||||
|
|
||||||
|
set.migration = MIGRATION_NAME;
|
||||||
|
|
||||||
|
if (typeof user.items.gear.owned.armor_special_turkeyArmorBase !== 'undefined') {
|
||||||
|
set['items.gear.owned.head_special_turkeyHelmGilded'] = false;
|
||||||
|
set['items.gear.owned.armor_special_turkeyArmorGilded'] = false;
|
||||||
|
set['items.gear.owned.back_special_turkeyTailGilded'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_turkeyHelmGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.armor_special_turkeyArmorGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.back_special_turkeyTailGilded',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
|
||||||
|
set['items.gear.owned.head_special_turkeyHelmBase'] = false;
|
||||||
|
set['items.gear.owned.armor_special_turkeyArmorBase'] = false;
|
||||||
|
set['items.gear.owned.back_special_turkeyTailBase'] = false;
|
||||||
|
push = [
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.head_special_turkeyHelmBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.armor_special_turkeyArmorBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'marketGear',
|
||||||
|
path: 'gear.flat.back_special_turkeyTailBase',
|
||||||
|
_id: new mongoose.Types.ObjectId(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
|
||||||
|
set['items.mounts.Turkey-Gilded'] = true;
|
||||||
|
} else if (user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
|
||||||
|
set['items.pets.Turkey-Gilded'] = 5;
|
||||||
|
} else if (user.items && user.items.pets && user.items.pets['Turkey-Base']) {
|
||||||
|
set['items.mounts.Turkey-Base'] = true;
|
||||||
|
} else {
|
||||||
|
set['items.pets.Turkey-Base'] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||||
|
|
||||||
|
if (push) {
|
||||||
|
return await User.update({_id: user._id}, {$set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||||
|
} else {
|
||||||
|
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function processUsers () {
|
||||||
|
let query = {
|
||||||
|
migration: {$ne: MIGRATION_NAME},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
_id: 1,
|
||||||
|
items: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) { // eslint-disable-line no-constant-condition
|
||||||
|
const users = await User // eslint-disable-line no-await-in-loop
|
||||||
|
.find(query)
|
||||||
|
.limit(250)
|
||||||
|
.sort({_id: 1})
|
||||||
|
.select(fields)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
console.warn(`\n${count} users processed\n`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
query._id = {
|
||||||
|
$gt: users[users.length - 1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
};
|
||||||
99
migrations/users/generate-usernames.js
Normal file
99
migrations/users/generate-usernames.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate usernames for users who lack them
|
||||||
|
*/
|
||||||
|
|
||||||
|
import monk from 'monk';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
import { generateUsername } from '../../website/server/libs/auth/utils';
|
||||||
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING'); // FOR TEST DATABASE
|
||||||
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers (lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
let query = {
|
||||||
|
'auth.local.username': {$exists: false},
|
||||||
|
'auth.timestamps.loggedin': {$gt: new Date('2018-04-01')}, // Initial coverage for users active within last 6 months
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [
|
||||||
|
'auth',
|
||||||
|
], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, `ERROR! ${ err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressCount = 1000;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let userPromises = users.map(updateUser);
|
||||||
|
let lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(() => {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!user.auth.local.username) {
|
||||||
|
const newName = generateUsername();
|
||||||
|
dbUsers.update(
|
||||||
|
{_id: user._id},
|
||||||
|
{$set:
|
||||||
|
{
|
||||||
|
'auth.local.username': newName,
|
||||||
|
'auth.local.lowerCaseUsername': newName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||||
|
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData () {
|
||||||
|
console.warn(`\n${ count } users processed\n`);
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting (code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) {
|
||||||
|
msg = 'ERROR!';
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
if (code) {
|
||||||
|
console.error(msg);
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
@@ -8,7 +8,7 @@ const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is do
|
|||||||
/*
|
/*
|
||||||
* Award this month's mystery items to subscribers
|
* Award this month's mystery items to subscribers
|
||||||
*/
|
*/
|
||||||
const MYSTERY_ITEMS = ['armor_mystery_201808', 'head_mystery_201808'];
|
const MYSTERY_ITEMS = ['armor_mystery_201810', 'head_mystery_201810'];
|
||||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||||
|
|
||||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||||
|
|||||||
16922
package-lock.json
generated
16922
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "habitica",
|
"name": "habitica",
|
||||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||||
"version": "4.60.1",
|
"version": "4.73.1",
|
||||||
"main": "./website/server/index.js",
|
"main": "./website/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@slack/client": "^3.8.1",
|
"@slack/client": "^3.8.1",
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"apidoc": "^0.17.5",
|
"apidoc": "^0.17.5",
|
||||||
"apn": "^2.2.0",
|
"apn": "^2.2.0",
|
||||||
"autoprefixer": "^8.5.0",
|
"autoprefixer": "^8.5.0",
|
||||||
"aws-sdk": "^2.239.1",
|
"aws-sdk": "^2.329.0",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"axios-progress-bar": "^1.2.0",
|
"axios-progress-bar": "^1.2.0",
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"babel-preset-es2015": "^6.6.0",
|
"babel-preset-es2015": "^6.6.0",
|
||||||
"babel-register": "^6.6.0",
|
"babel-register": "^6.6.0",
|
||||||
"babel-runtime": "^6.11.6",
|
"babel-runtime": "^6.11.6",
|
||||||
"bcrypt": "^3.0.0",
|
"bcrypt": "^3.0.1",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"bootstrap": "^4.1.1",
|
"bootstrap": "^4.1.1",
|
||||||
"bootstrap-vue": "^2.0.0-rc.9",
|
"bootstrap-vue": "^2.0.0-rc.9",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"coupon-code": "^0.4.5",
|
"coupon-code": "^0.4.5",
|
||||||
"cross-env": "^5.1.5",
|
"cross-env": "^5.1.5",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"csv-stringify": "^3.0.0",
|
"csv-stringify": "^4.3.1",
|
||||||
"cwait": "^1.1.1",
|
"cwait": "^1.1.1",
|
||||||
"domain-middleware": "~0.1.0",
|
"domain-middleware": "~0.1.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
@@ -46,29 +46,29 @@
|
|||||||
"got": "^9.0.0",
|
"got": "^9.0.0",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"gulp-babel": "^7.0.1",
|
"gulp-babel": "^7.0.1",
|
||||||
"gulp-imagemin": "^4.1.0",
|
"gulp-imagemin": "^5.0.3",
|
||||||
"gulp-nodemon": "^2.2.1",
|
"gulp-nodemon": "^2.4.1",
|
||||||
"gulp.spritesmith": "^6.9.0",
|
"gulp.spritesmith": "^6.9.0",
|
||||||
"habitica-markdown": "^1.3.0",
|
"habitica-markdown": "^1.3.0",
|
||||||
"hellojs": "^1.15.1",
|
"hellojs": "^1.15.1",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"image-size": "^0.6.2",
|
"image-size": "^0.6.2",
|
||||||
"in-app-purchase": "^1.9.4",
|
"in-app-purchase": "^1.10.2",
|
||||||
"intro.js": "^2.9.3",
|
"intro.js": "^2.9.3",
|
||||||
"jquery": ">=3.0.0",
|
"jquery": ">=3.0.0",
|
||||||
"js2xmlparser": "^3.0.0",
|
"js2xmlparser": "^3.0.0",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"method-override": "^2.3.5",
|
"method-override": "^3.0.0",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
"moment-recur": "^1.0.7",
|
"moment-recur": "^1.0.7",
|
||||||
"mongoose": "^5.1.2",
|
"mongoose": "^5.3.4",
|
||||||
"morgan": "^1.7.0",
|
"morgan": "^1.7.0",
|
||||||
"nconf": "^0.10.0",
|
"nconf": "^0.10.0",
|
||||||
"node-gcm": "^0.14.4",
|
"node-gcm": "^1.0.2",
|
||||||
"node-sass": "^4.9.0",
|
"node-sass": "^4.9.0",
|
||||||
"nodemailer": "^4.6.4",
|
"nodemailer": "^4.6.4",
|
||||||
"ora": "^2.1.0",
|
"ora": "^3.0.0",
|
||||||
"pageres": "^4.1.1",
|
"pageres": "^4.1.1",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-facebook": "^2.0.0",
|
"passport-facebook": "^2.0.0",
|
||||||
@@ -79,13 +79,13 @@
|
|||||||
"postcss-easy-import": "^3.0.0",
|
"postcss-easy-import": "^3.0.0",
|
||||||
"ps-tree": "^1.0.0",
|
"ps-tree": "^1.0.0",
|
||||||
"pug": "^2.0.3",
|
"pug": "^2.0.3",
|
||||||
"pusher": "^1.3.0",
|
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"sass-loader": "^7.0.0",
|
"sass-loader": "^7.0.0",
|
||||||
"shelljs": "^0.8.2",
|
"shelljs": "^0.8.2",
|
||||||
|
"short-uuid": "^3.0.0",
|
||||||
"smartbanner.js": "^1.9.1",
|
"smartbanner.js": "^1.9.1",
|
||||||
"stripe": "^5.9.0",
|
"stripe": "^5.9.0",
|
||||||
"superagent": "^3.8.3",
|
"superagent": "^4.0.0",
|
||||||
"svg-inline-loader": "^0.8.0",
|
"svg-inline-loader": "^0.8.0",
|
||||||
"svg-url-loader": "^2.3.2",
|
"svg-url-loader": "^2.3.2",
|
||||||
"svgo": "^1.0.5",
|
"svgo": "^1.0.5",
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
"eslint-plugin-mocha": "^5.0.0",
|
"eslint-plugin-mocha": "^5.0.0",
|
||||||
"eventsource-polyfill": "^0.9.6",
|
"eventsource-polyfill": "^0.9.6",
|
||||||
"expect.js": "^0.3.1",
|
"expect.js": "^0.3.1",
|
||||||
"http-proxy-middleware": "^0.18.0",
|
"http-proxy-middleware": "^0.19.0",
|
||||||
"istanbul": "^1.1.0-alpha.1",
|
"istanbul": "^1.1.0-alpha.1",
|
||||||
"karma": "^3.0.0",
|
"karma": "^3.0.0",
|
||||||
"karma-babel-preprocessor": "^7.0.0",
|
"karma-babel-preprocessor": "^7.0.0",
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
"puppeteer": "^1.4.0",
|
"puppeteer": "^1.4.0",
|
||||||
"require-again": "^2.0.0",
|
"require-again": "^2.0.0",
|
||||||
"selenium-server": "^3.12.0",
|
"selenium-server": "^3.12.0",
|
||||||
"sinon": "^4.5.0",
|
"sinon": "^6.3.5",
|
||||||
"sinon-chai": "^3.0.0",
|
"sinon-chai": "^3.0.0",
|
||||||
"sinon-stub-promise": "^4.0.0",
|
"sinon-stub-promise": "^4.0.0",
|
||||||
"webpack-bundle-analyzer": "^2.12.0",
|
"webpack-bundle-analyzer": "^2.12.0",
|
||||||
|
|||||||
88
scripts/gdpr-delete-users.js
Normal file
88
scripts/gdpr-delete-users.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import axios from 'axios';
|
||||||
|
import { model as User } from '../website/server/models/user';
|
||||||
|
import nconf from 'nconf';
|
||||||
|
|
||||||
|
const AMPLITUDE_KEY = nconf.get('AMPLITUDE_KEY');
|
||||||
|
const AMPLITUDE_SECRET = nconf.get('AMPLITUDE_SECRET');
|
||||||
|
const BASE_URL = nconf.get('BASE_URL');
|
||||||
|
|
||||||
|
async function _deleteAmplitudeData (userId, email) {
|
||||||
|
const response = await axios.post(
|
||||||
|
'https://amplitude.com/api/2/deletions/users',
|
||||||
|
{
|
||||||
|
user_ids: userId, // eslint-disable-line camelcase
|
||||||
|
requester: email,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: AMPLITUDE_KEY,
|
||||||
|
password: AMPLITUDE_SECRET,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).catch((err) => {
|
||||||
|
console.log(err.response.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response) console.log(`${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _deleteHabiticaData (user) {
|
||||||
|
await User.update(
|
||||||
|
{_id: user._id},
|
||||||
|
{$set: {
|
||||||
|
'auth.local.passwordHashMethod': 'bcrypt',
|
||||||
|
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
const response = await axios.delete(
|
||||||
|
`${BASE_URL}/api/v3/user`,
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
password: 'test',
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'x-api-user': user._id,
|
||||||
|
'x-api-key': user.apiToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
).catch((err) => {
|
||||||
|
console.log(err.response.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
console.log(`${response.status} ${response.statusText}`);
|
||||||
|
if (response.status === 200) console.log(`${user._id} removed. Last login: ${user.auth.timestamps.loggedin}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _processEmailAddress (email) {
|
||||||
|
const emailRegex = new RegExp(`^${email}`, 'i');
|
||||||
|
const users = await User.find({
|
||||||
|
$or: [
|
||||||
|
{'auth.local.email': emailRegex},
|
||||||
|
{'auth.facebook.emails.value': emailRegex},
|
||||||
|
{'auth.google.emails.value': emailRegex},
|
||||||
|
]},
|
||||||
|
{
|
||||||
|
_id: 1,
|
||||||
|
apiToken: 1,
|
||||||
|
auth: 1,
|
||||||
|
}).exec();
|
||||||
|
|
||||||
|
if (users.length < 1) {
|
||||||
|
console.log(`No users found with email address ${email}`);
|
||||||
|
} else {
|
||||||
|
for (const user of users) {
|
||||||
|
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
||||||
|
await _deleteHabiticaData(user); // eslint-disable-line no-await-in-loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteUserData (emails) {
|
||||||
|
const emailPromises = emails.map(_processEmailAddress);
|
||||||
|
return Promise.all(emailPromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = deleteUserData;
|
||||||
@@ -5,10 +5,17 @@ describe('Base model plugin', () => {
|
|||||||
let schema;
|
let schema;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
schema = new mongoose.Schema();
|
schema = new mongoose.Schema({}, {
|
||||||
|
typeKey: '$type',
|
||||||
|
});
|
||||||
sandbox.stub(schema, 'add');
|
sandbox.stub(schema, 'add');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws if "typeKey" is not set to $type', () => {
|
||||||
|
const schemaWithoutTypeKey = new mongoose.Schema();
|
||||||
|
expect(() => schemaWithoutTypeKey.plugin(baseModel)).to.throw;
|
||||||
|
});
|
||||||
|
|
||||||
it('adds a _id field to the schema', () => {
|
it('adds a _id field to the schema', () => {
|
||||||
schema.plugin(baseModel);
|
schema.plugin(baseModel);
|
||||||
|
|
||||||
|
|||||||
@@ -1589,27 +1589,6 @@ describe('cron', () => {
|
|||||||
flagCount: 0,
|
flagCount: 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('does not clear pms under 200', () => {
|
|
||||||
cron({user, tasksByType, daysMissed, analytics});
|
|
||||||
expect(user.inbox.messages[lastMessageId]).to.exist;
|
|
||||||
});
|
|
||||||
|
|
||||||
xit('clears pms over 200', () => {
|
|
||||||
let messageId = common.uuid();
|
|
||||||
user.inbox.messages[messageId] = {
|
|
||||||
id: messageId,
|
|
||||||
text: `test ${messageId}`,
|
|
||||||
timestamp: Number(new Date()),
|
|
||||||
likes: {},
|
|
||||||
flags: {},
|
|
||||||
flagCount: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
cron({user, tasksByType, daysMissed, analytics});
|
|
||||||
|
|
||||||
expect(user.inbox.messages[messageId]).to.not.exist;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('login incentives', () => {
|
describe('login incentives', () => {
|
||||||
|
|||||||
@@ -107,6 +107,25 @@ describe('Password Utilities', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('defaults to SHA1 encryption if salt is provided', async () => {
|
||||||
|
let textPassword = 'mySecretPassword';
|
||||||
|
let salt = sha1MakeSalt();
|
||||||
|
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||||
|
|
||||||
|
let user = {
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
hashed_password: hashedPassword,
|
||||||
|
salt,
|
||||||
|
passwordHashMethod: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let isValidPassword = await compare(user, textPassword);
|
||||||
|
expect(isValidPassword).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('throws an error if an invalid hashing method is used', async () => {
|
it('throws an error if an invalid hashing method is used', async () => {
|
||||||
try {
|
try {
|
||||||
await compare({
|
await compare({
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
|||||||
|
|
||||||
function expectBillingAggreementDetailSpy () {
|
function expectBillingAggreementDetailSpy () {
|
||||||
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
|
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||||
.returnsPromise()
|
|
||||||
.resolves({
|
.resolves({
|
||||||
BillingAgreementDetails: {
|
BillingAgreementDetails: {
|
||||||
BillingAgreementStatus: {State: 'Open'},
|
BillingAgreementStatus: {State: 'Open'},
|
||||||
@@ -80,14 +79,14 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
|||||||
headers = {};
|
headers = {};
|
||||||
|
|
||||||
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
|
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
|
||||||
getBillingAgreementDetailsSpy.returnsPromise().resolves({
|
getBillingAgreementDetailsSpy.resolves({
|
||||||
BillingAgreementDetails: {
|
BillingAgreementDetails: {
|
||||||
BillingAgreementStatus: {State: 'Closed'},
|
BillingAgreementStatus: {State: 'Closed'},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription');
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription');
|
||||||
paymentCancelSubscriptionSpy.returnsPromise().resolves({});
|
paymentCancelSubscriptionSpy.resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
@@ -118,7 +117,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
|||||||
it('should close a user subscription if amazon not closed', async () => {
|
it('should close a user subscription if amazon not closed', async () => {
|
||||||
amzLib.getBillingAgreementDetails.restore();
|
amzLib.getBillingAgreementDetails.restore();
|
||||||
expectBillingAggreementDetailSpy();
|
expectBillingAggreementDetailSpy();
|
||||||
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
|
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||||
billingAgreementId = user.purchased.plan.customerId;
|
billingAgreementId = user.purchased.plan.customerId;
|
||||||
|
|
||||||
await amzLib.cancelSubscription({user, headers});
|
await amzLib.cancelSubscription({user, headers});
|
||||||
@@ -164,7 +163,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
|||||||
it('should close a group subscription if amazon not closed', async () => {
|
it('should close a group subscription if amazon not closed', async () => {
|
||||||
amzLib.getBillingAgreementDetails.restore();
|
amzLib.getBillingAgreementDetails.restore();
|
||||||
expectBillingAggreementDetailSpy();
|
expectBillingAggreementDetailSpy();
|
||||||
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
|
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||||
billingAgreementId = group.purchased.plan.customerId;
|
billingAgreementId = group.purchased.plan.customerId;
|
||||||
|
|
||||||
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
||||||
|
|||||||
@@ -68,22 +68,22 @@ describe('Amazon Payments - Checkout', () => {
|
|||||||
orderReferenceId = 'orderReferenceId';
|
orderReferenceId = 'orderReferenceId';
|
||||||
|
|
||||||
setOrderReferenceDetailsSpy = sinon.stub(amzLib, 'setOrderReferenceDetails');
|
setOrderReferenceDetailsSpy = sinon.stub(amzLib, 'setOrderReferenceDetails');
|
||||||
setOrderReferenceDetailsSpy.returnsPromise().resolves({});
|
setOrderReferenceDetailsSpy.resolves({});
|
||||||
|
|
||||||
confirmOrderReferenceSpy = sinon.stub(amzLib, 'confirmOrderReference');
|
confirmOrderReferenceSpy = sinon.stub(amzLib, 'confirmOrderReference');
|
||||||
confirmOrderReferenceSpy.returnsPromise().resolves({});
|
confirmOrderReferenceSpy.resolves({});
|
||||||
|
|
||||||
authorizeSpy = sinon.stub(amzLib, 'authorize');
|
authorizeSpy = sinon.stub(amzLib, 'authorize');
|
||||||
authorizeSpy.returnsPromise().resolves({});
|
authorizeSpy.resolves({});
|
||||||
|
|
||||||
closeOrderReferenceSpy = sinon.stub(amzLib, 'closeOrderReference');
|
closeOrderReferenceSpy = sinon.stub(amzLib, 'closeOrderReference');
|
||||||
closeOrderReferenceSpy.returnsPromise().resolves({});
|
closeOrderReferenceSpy.resolves({});
|
||||||
|
|
||||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems');
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems');
|
||||||
paymentBuyGemsStub.returnsPromise().resolves({});
|
paymentBuyGemsStub.resolves({});
|
||||||
|
|
||||||
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription');
|
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription');
|
||||||
paymentCreateSubscritionStub.returnsPromise().resolves({});
|
paymentCreateSubscritionStub.resolves({});
|
||||||
|
|
||||||
sinon.stub(common, 'uuid').returns('uuid-generated');
|
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||||
});
|
});
|
||||||
@@ -111,7 +111,7 @@ describe('Amazon Payments - Checkout', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('should purchase gems', async () => {
|
it('should purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await amzLib.checkout({user, orderReferenceId, headers});
|
await amzLib.checkout({user, orderReferenceId, headers});
|
||||||
|
|
||||||
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD);
|
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD);
|
||||||
@@ -140,7 +140,7 @@ describe('Amazon Payments - Checkout', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should error if user cannot get gems gems', async () => {
|
it('should error if user cannot get gems gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
|
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
message: i18n.t('groupPolicyCannotGetGems'),
|
message: i18n.t('groupPolicyCannotGetGems'),
|
||||||
|
|||||||
@@ -46,16 +46,16 @@ describe('Amazon Payments - Subscribe', () => {
|
|||||||
headers = {};
|
headers = {};
|
||||||
|
|
||||||
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
|
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
|
||||||
amazonSetBillingAgreementDetailsSpy.returnsPromise().resolves({});
|
amazonSetBillingAgreementDetailsSpy.resolves({});
|
||||||
|
|
||||||
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
|
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
|
||||||
amazonConfirmBillingAgreementSpy.returnsPromise().resolves({});
|
amazonConfirmBillingAgreementSpy.resolves({});
|
||||||
|
|
||||||
amazonAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
amazonAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
||||||
amazonAuthorizeOnBillingAgreementSpy.returnsPromise().resolves({});
|
amazonAuthorizeOnBillingAgreementSpy.resolves({});
|
||||||
|
|
||||||
createSubSpy = sinon.stub(payments, 'createSubscription');
|
createSubSpy = sinon.stub(payments, 'createSubscription');
|
||||||
createSubSpy.returnsPromise().resolves({});
|
createSubSpy.resolves({});
|
||||||
|
|
||||||
sinon.stub(common, 'uuid').returns('uuid-generated');
|
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe('#upgradeGroupPlan', () => {
|
|||||||
await group.save();
|
await group.save();
|
||||||
|
|
||||||
spy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
spy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
|
||||||
spy.returnsPromise().resolves([]);
|
spy.resolves([]);
|
||||||
|
|
||||||
uuidString = 'uuid-v4';
|
uuidString = 'uuid-v4';
|
||||||
sinon.stub(uuid, 'v4').returns(uuidString);
|
sinon.stub(uuid, 'v4').returns(uuidString);
|
||||||
|
|||||||
@@ -24,16 +24,16 @@ describe('Apple Payments', () => {
|
|||||||
headers = {};
|
headers = {};
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({});
|
.resolves({});
|
||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(true);
|
.returns(true);
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
.returns([{productId: 'com.habitrpg.ios.Habitica.21gems',
|
.returns([{productId: 'com.habitrpg.ios.Habitica.21gems',
|
||||||
transactionId: token,
|
transactionId: token,
|
||||||
}]);
|
}]);
|
||||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -70,7 +70,7 @@ describe('Apple Payments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors if the user cannot purchase gems', async () => {
|
it('errors if the user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
await expect(applePayments.verifyGemPurchase(user, receipt, headers))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
@@ -82,7 +82,7 @@ describe('Apple Payments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors if amount does not exist', async () => {
|
it('errors if amount does not exist', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
iapGetPurchaseDataStub.restore();
|
iapGetPurchaseDataStub.restore();
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
.returns([{productId: 'badProduct',
|
.returns([{productId: 'badProduct',
|
||||||
@@ -130,7 +130,7 @@ describe('Apple Payments', () => {
|
|||||||
transactionId: token,
|
transactionId: token,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await applePayments.verifyGemPurchase(user, receipt, headers);
|
await applePayments.verifyGemPurchase(user, receipt, headers);
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
@@ -167,9 +167,9 @@ describe('Apple Payments', () => {
|
|||||||
nextPaymentProcessing = moment.utc().add({days: 2});
|
nextPaymentProcessing = moment.utc().add({days: 2});
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({});
|
.resolves({});
|
||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(true);
|
.returns(true);
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
@@ -186,7 +186,7 @@ describe('Apple Payments', () => {
|
|||||||
productId: sku,
|
productId: sku,
|
||||||
transactionId: token,
|
transactionId: token,
|
||||||
}]);
|
}]);
|
||||||
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -297,9 +297,9 @@ describe('Apple Payments', () => {
|
|||||||
expirationDate = moment.utc();
|
expirationDate = moment.utc();
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
expirationDate,
|
expirationDate,
|
||||||
});
|
});
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
@@ -314,7 +314,7 @@ describe('Apple Payments', () => {
|
|||||||
user.purchased.plan.planId = subKey;
|
user.purchased.plan.planId = subKey;
|
||||||
user.purchased.plan.additionalData = receipt;
|
user.purchased.plan.additionalData = receipt;
|
||||||
|
|
||||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ describe('Google Payments', () => {
|
|||||||
headers = {};
|
headers = {};
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({});
|
.resolves({});
|
||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(true);
|
.returns(true);
|
||||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -64,7 +64,7 @@ describe('Google Payments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if user cannot purchase gems', async () => {
|
it('should throw an error if user cannot purchase gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
|
|
||||||
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
await expect(googlePayments.verifyGemPurchase(user, receipt, signature, headers))
|
||||||
.to.eventually.be.rejected.and.to.eql({
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
@@ -77,7 +77,7 @@ describe('Google Payments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('purchases gems', async () => {
|
it('purchases gems', async () => {
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
await googlePayments.verifyGemPurchase(user, receipt, signature, headers);
|
||||||
|
|
||||||
expect(iapSetupStub).to.be.calledOnce;
|
expect(iapSetupStub).to.be.calledOnce;
|
||||||
@@ -116,12 +116,12 @@ describe('Google Payments', () => {
|
|||||||
nextPaymentProcessing = moment.utc().add({days: 2});
|
nextPaymentProcessing = moment.utc().add({days: 2});
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({});
|
.resolves({});
|
||||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||||
.returns(true);
|
.returns(true);
|
||||||
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -193,9 +193,9 @@ describe('Google Payments', () => {
|
|||||||
expirationDate = moment.utc();
|
expirationDate = moment.utc();
|
||||||
|
|
||||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||||
.returnsPromise().resolves();
|
.resolves();
|
||||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
expirationDate,
|
expirationDate,
|
||||||
});
|
});
|
||||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||||
@@ -210,7 +210,7 @@ describe('Google Payments', () => {
|
|||||||
user.purchased.plan.planId = subKey;
|
user.purchased.plan.planId = subKey;
|
||||||
user.purchased.plan.additionalData = {data: receipt, signature};
|
user.purchased.plan.additionalData = {data: receipt, signature};
|
||||||
|
|
||||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ describe('Purchasing a group plan for group', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let subscriptionId = 'subId';
|
let subscriptionId = 'subId';
|
||||||
sinon.stub(stripe.customers, 'del').returnsPromise().resolves({});
|
sinon.stub(stripe.customers, 'del').resolves({});
|
||||||
|
|
||||||
let currentPeriodEndTimeStamp = moment().add(3, 'months').unix();
|
let currentPeriodEndTimeStamp = moment().add(3, 'months').unix();
|
||||||
sinon.stub(stripe.customers, 'retrieve')
|
sinon.stub(stripe.customers, 'retrieve')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
||||||
},
|
},
|
||||||
@@ -216,7 +216,6 @@ describe('Purchasing a group plan for group', () => {
|
|||||||
|
|
||||||
it('sends one email to subscribed member of group, stating subscription is cancelled (Amazon)', async () => {
|
it('sends one email to subscribed member of group, stating subscription is cancelled (Amazon)', async () => {
|
||||||
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||||
.returnsPromise()
|
|
||||||
.resolves({
|
.resolves({
|
||||||
BillingAgreementDetails: {
|
BillingAgreementDetails: {
|
||||||
BillingAgreementStatus: {State: 'Closed'},
|
BillingAgreementStatus: {State: 'Closed'},
|
||||||
@@ -251,9 +250,9 @@ describe('Purchasing a group plan for group', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sends one email to subscribed member of group, stating subscription is cancelled (PayPal)', async () => {
|
it('sends one email to subscribed member of group, stating subscription is cancelled (PayPal)', async () => {
|
||||||
sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').returnsPromise().resolves({});
|
sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').resolves({});
|
||||||
sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
agreement_details: { // eslint-disable-line camelcase
|
agreement_details: { // eslint-disable-line camelcase
|
||||||
next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase
|
next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase
|
||||||
cycles_completed: 1, // eslint-disable-line camelcase
|
cycles_completed: 1, // eslint-disable-line camelcase
|
||||||
@@ -449,7 +448,6 @@ describe('Purchasing a group plan for group', () => {
|
|||||||
|
|
||||||
it('adds months to members with existing recurring subscription (Amazon)', async () => {
|
it('adds months to members with existing recurring subscription (Amazon)', async () => {
|
||||||
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||||
.returnsPromise()
|
|
||||||
.resolves({
|
.resolves({
|
||||||
BillingAgreementDetails: {
|
BillingAgreementDetails: {
|
||||||
BillingAgreementStatus: {State: 'Closed'},
|
BillingAgreementStatus: {State: 'Closed'},
|
||||||
@@ -478,9 +476,9 @@ describe('Purchasing a group plan for group', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('adds months to members with existing recurring subscription (Paypal)', async () => {
|
it('adds months to members with existing recurring subscription (Paypal)', async () => {
|
||||||
sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').returnsPromise().resolves({});
|
sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').resolves({});
|
||||||
sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
agreement_details: { // eslint-disable-line camelcase
|
agreement_details: { // eslint-disable-line camelcase
|
||||||
next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase
|
next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase
|
||||||
cycles_completed: 1, // eslint-disable-line camelcase
|
cycles_completed: 1, // eslint-disable-line camelcase
|
||||||
|
|||||||
@@ -446,6 +446,19 @@ describe('payments/index', () => {
|
|||||||
fakeClock.restore();
|
fakeClock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not add a notification for mystery items if none was awarded', async () => {
|
||||||
|
const noMysteryItemTimeframe = 1462183920000; // May 2nd 2016
|
||||||
|
let fakeClock = sinon.useFakeTimers(noMysteryItemTimeframe);
|
||||||
|
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||||
|
|
||||||
|
await api.createSubscription(data);
|
||||||
|
|
||||||
|
expect(user.purchased.plan.mysteryItems).to.have.a.lengthOf(0);
|
||||||
|
expect(user.notifications.find(n => n.type === 'NEW_MYSTERY_ITEMS')).to.be.undefined;
|
||||||
|
|
||||||
|
fakeClock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
it('does not award mystery item when user already owns the item', async () => {
|
it('does not award mystery item when user already owns the item', async () => {
|
||||||
let mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
let mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||||
let fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
let fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ describe('checkout success', () => {
|
|||||||
customerId = 'customerId-test';
|
customerId = 'customerId-test';
|
||||||
paymentId = 'paymentId-test';
|
paymentId = 'paymentId-test';
|
||||||
|
|
||||||
paypalPaymentExecuteStub = sinon.stub(paypalPayments, 'paypalPaymentExecute').returnsPromise().resolves({});
|
paypalPaymentExecuteStub = sinon.stub(paypalPayments, 'paypalPaymentExecute').resolves({});
|
||||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||||
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe('checkout', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
approvalHerf = 'approval_href';
|
approvalHerf = 'approval_href';
|
||||||
paypalPaymentCreateStub = sinon.stub(paypalPayments, 'paypalPaymentCreate')
|
paypalPaymentCreateStub = sinon.stub(paypalPayments, 'paypalPaymentCreate')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
links: [{ rel: 'approval_url', href: approvalHerf }],
|
links: [{ rel: 'approval_url', href: approvalHerf }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -80,7 +80,7 @@ describe('checkout', () => {
|
|||||||
|
|
||||||
it('should error if the user cannot get gems', async () => {
|
it('should error if the user cannot get gems', async () => {
|
||||||
let user = new User();
|
let user = new User();
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
|
|
||||||
await expect(paypalPayments.checkout({user})).to.eventually.be.rejected.and.to.eql({
|
await expect(paypalPayments.checkout({user})).to.eventually.be.rejected.and.to.eql({
|
||||||
httpCode: 401,
|
httpCode: 401,
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ describe('ipn', () => {
|
|||||||
group.purchased.plan.lastBillingDate = new Date();
|
group.purchased.plan.lastBillingDate = new Date();
|
||||||
await group.save();
|
await group.save();
|
||||||
|
|
||||||
ipnVerifyAsyncStub = sinon.stub(paypalPayments, 'ipnVerifyAsync').returnsPromise().resolves({});
|
ipnVerifyAsyncStub = sinon.stub(paypalPayments, 'ipnVerifyAsync').resolves({});
|
||||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ describe('subscribeCancel', () => {
|
|||||||
|
|
||||||
nextBillingDate = new Date();
|
nextBillingDate = new Date();
|
||||||
|
|
||||||
paypalBillingAgreementCancelStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').returnsPromise().resolves({});
|
paypalBillingAgreementCancelStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').resolves({});
|
||||||
paypalBillingAgreementGetStub = sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
paypalBillingAgreementGetStub = sinon.stub(paypalPayments, 'paypalBillingAgreementGet')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
agreement_details: {
|
agreement_details: {
|
||||||
next_billing_date: nextBillingDate,
|
next_billing_date: nextBillingDate,
|
||||||
cycles_completed: 1,
|
cycles_completed: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ describe('subscribeSuccess', () => {
|
|||||||
customerId = 'test-customerId';
|
customerId = 'test-customerId';
|
||||||
|
|
||||||
paypalBillingAgreementExecuteStub = sinon.stub(paypalPayments, 'paypalBillingAgreementExecute')
|
paypalBillingAgreementExecuteStub = sinon.stub(paypalPayments, 'paypalBillingAgreementExecute')
|
||||||
.returnsPromise({}).resolves({
|
.resolves({
|
||||||
id: customerId,
|
id: customerId,
|
||||||
});
|
});
|
||||||
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('subscribe', () => {
|
|||||||
sub = Object.assign({}, common.content.subscriptionBlocks[subKey]);
|
sub = Object.assign({}, common.content.subscriptionBlocks[subKey]);
|
||||||
|
|
||||||
paypalBillingAgreementCreateStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCreate')
|
paypalBillingAgreementCreateStub = sinon.stub(paypalPayments, 'paypalBillingAgreementCreate')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
links: [{ rel: 'approval_url', href: approvalHerf }],
|
links: [{ rel: 'approval_url', href: approvalHerf }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,12 +82,12 @@ describe('cancel subscription', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
subscriptionId = 'subId';
|
subscriptionId = 'subId';
|
||||||
stripeDeleteCustomerStub = sinon.stub(stripe.customers, 'del').returnsPromise().resolves({});
|
stripeDeleteCustomerStub = sinon.stub(stripe.customers, 'del').resolves({});
|
||||||
paymentsCancelSubStub = sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
paymentsCancelSubStub = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
|
|
||||||
currentPeriodEndTimeStamp = (new Date()).getTime();
|
currentPeriodEndTimeStamp = (new Date()).getTime();
|
||||||
stripeRetrieveStub = sinon.stub(stripe.customers, 'retrieve')
|
stripeRetrieveStub = sinon.stub(stripe.customers, 'retrieve')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ describe('checkout with subscription', () => {
|
|||||||
token = 'test-token';
|
token = 'test-token';
|
||||||
|
|
||||||
spy = sinon.stub(stripe.subscriptions, 'update');
|
spy = sinon.stub(stripe.subscriptions, 'update');
|
||||||
spy.returnsPromise().resolves;
|
spy.resolves;
|
||||||
|
|
||||||
stripeCreateCustomerSpy = sinon.stub(stripe.customers, 'create');
|
stripeCreateCustomerSpy = sinon.stub(stripe.customers, 'create');
|
||||||
let stripCustomerResponse = {
|
let stripCustomerResponse = {
|
||||||
@@ -63,10 +63,10 @@ describe('checkout with subscription', () => {
|
|||||||
data: [{id: subscriptionId}],
|
data: [{id: subscriptionId}],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
stripeCreateCustomerSpy.returnsPromise().resolves(stripCustomerResponse);
|
stripeCreateCustomerSpy.resolves(stripCustomerResponse);
|
||||||
|
|
||||||
stripePaymentsCreateSubSpy = sinon.stub(payments, 'createSubscription');
|
stripePaymentsCreateSubSpy = sinon.stub(payments, 'createSubscription');
|
||||||
stripePaymentsCreateSubSpy.returnsPromise().resolves({});
|
stripePaymentsCreateSubSpy.resolves({});
|
||||||
|
|
||||||
data.groupId = group._id;
|
data.groupId = group._id;
|
||||||
data.sub.quantity = 3;
|
data.sub.quantity = 3;
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ describe('checkout', () => {
|
|||||||
let stripCustomerResponse = {
|
let stripCustomerResponse = {
|
||||||
id: customerIdResponse,
|
id: customerIdResponse,
|
||||||
};
|
};
|
||||||
stripeChargeStub = sinon.stub(stripe.charges, 'create').returnsPromise().resolves(stripCustomerResponse);
|
stripeChargeStub = sinon.stub(stripe.charges, 'create').resolves(stripCustomerResponse);
|
||||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').returnsPromise().resolves({});
|
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||||
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription').returnsPromise().resolves({});
|
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -82,7 +82,7 @@ describe('checkout', () => {
|
|||||||
|
|
||||||
it('should error if user cannot get gems', async () => {
|
it('should error if user cannot get gems', async () => {
|
||||||
gift = undefined;
|
gift = undefined;
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
|
sinon.stub(user, 'canGetGems').resolves(false);
|
||||||
|
|
||||||
await expect(stripePayments.checkout({
|
await expect(stripePayments.checkout({
|
||||||
token,
|
token,
|
||||||
@@ -101,7 +101,7 @@ describe('checkout', () => {
|
|||||||
|
|
||||||
it('should purchase gems', async () => {
|
it('should purchase gems', async () => {
|
||||||
gift = undefined;
|
gift = undefined;
|
||||||
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
|
sinon.stub(user, 'canGetGems').resolves(true);
|
||||||
|
|
||||||
await stripePayments.checkout({
|
await stripePayments.checkout({
|
||||||
token,
|
token,
|
||||||
|
|||||||
@@ -98,11 +98,11 @@ describe('edit subscription', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
subscriptionId = 'subId';
|
subscriptionId = 'subId';
|
||||||
stripeListSubscriptionStub = sinon.stub(stripe.customers, 'listSubscriptions')
|
stripeListSubscriptionStub = sinon.stub(stripe.customers, 'listSubscriptions')
|
||||||
.returnsPromise().resolves({
|
.resolves({
|
||||||
data: [{id: subscriptionId}],
|
data: [{id: subscriptionId}],
|
||||||
});
|
});
|
||||||
|
|
||||||
stripeUpdateSubscriptionStub = sinon.stub(stripe.customers, 'updateSubscription').returnsPromise().resolves({});
|
stripeUpdateSubscriptionStub = sinon.stub(stripe.customers, 'updateSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
const eventRetrieved = {type: eventType};
|
const eventRetrieved = {type: eventType};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves(eventRetrieved);
|
sinon.stub(stripe.events, 'retrieve').resolves(eventRetrieved);
|
||||||
sinon.stub(logger, 'error');
|
sinon.stub(logger, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ describe('Stripe - Webhooks', () => {
|
|||||||
const eventType = 'customer.subscription.deleted';
|
const eventType = 'customer.subscription.deleted';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(stripe.customers, 'del').returnsPromise().resolves({});
|
sinon.stub(stripe.customers, 'del').resolves({});
|
||||||
sinon.stub(payments, 'cancelSubscription').returnsPromise().resolves({});
|
sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -62,7 +62,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not do anything if event.request is null (subscription cancelled manually)', async () => {
|
it('does not do anything if event.request is null (subscription cancelled manually)', async () => {
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
request: 123,
|
request: 123,
|
||||||
@@ -79,7 +79,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
describe('user subscription', () => {
|
describe('user subscription', () => {
|
||||||
it('throws an error if the user is not found', async () => {
|
it('throws an error if the user is not found', async () => {
|
||||||
const customerId = 456;
|
const customerId = 456;
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data: {
|
data: {
|
||||||
@@ -113,7 +113,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
||||||
await subscriber.save();
|
await subscriber.save();
|
||||||
|
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data: {
|
data: {
|
||||||
@@ -146,7 +146,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
describe('group plan subscription', () => {
|
describe('group plan subscription', () => {
|
||||||
it('throws an error if the group is not found', async () => {
|
it('throws an error if the group is not found', async () => {
|
||||||
const customerId = 456;
|
const customerId = 456;
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data: {
|
data: {
|
||||||
@@ -185,7 +185,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
||||||
await subscriber.save();
|
await subscriber.save();
|
||||||
|
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data: {
|
data: {
|
||||||
@@ -227,7 +227,7 @@ describe('Stripe - Webhooks', () => {
|
|||||||
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
subscriber.purchased.plan.paymentMethod = 'Stripe';
|
||||||
await subscriber.save();
|
await subscriber.save();
|
||||||
|
|
||||||
sinon.stub(stripe.events, 'retrieve').returnsPromise().resolves({
|
sinon.stub(stripe.events, 'retrieve').resolves({
|
||||||
id: 123,
|
id: 123,
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe('Stripe - Upgrade Group Plan', () => {
|
|||||||
await group.save();
|
await group.save();
|
||||||
|
|
||||||
spy = sinon.stub(stripe.subscriptions, 'update');
|
spy = sinon.stub(stripe.subscriptions, 'update');
|
||||||
spy.returnsPromise().resolves([]);
|
spy.resolves([]);
|
||||||
data.groupId = group._id;
|
data.groupId = group._id;
|
||||||
data.sub.quantity = 3;
|
data.sub.quantity = 3;
|
||||||
stripePayments.setStripeApi(stripe);
|
stripePayments.setStripeApi(stripe);
|
||||||
|
|||||||
@@ -178,4 +178,12 @@ describe('taskManager', () => {
|
|||||||
|
|
||||||
expect(order).to.eql(['task-id-2', 'task-id-1']);
|
expect(order).to.eql(['task-id-2', 'task-id-1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('moves tasks to a specified position out of length', async () => {
|
||||||
|
let order = ['task-id-1'];
|
||||||
|
|
||||||
|
moveTask(order, 'task-id-2', 2);
|
||||||
|
|
||||||
|
expect(order).to.eql(['task-id-1', 'task-id-2']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ describe('Group Model', () => {
|
|||||||
party.quest.active = false;
|
party.quest.active = false;
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
Group.prototype.sendChat.reset();
|
Group.prototype.sendChat.resetHistory();
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
@@ -496,7 +496,7 @@ describe('Group Model', () => {
|
|||||||
party.quest.active = false;
|
party.quest.active = false;
|
||||||
|
|
||||||
await party.startQuest(questLeader);
|
await party.startQuest(questLeader);
|
||||||
Group.prototype.sendChat.reset();
|
Group.prototype.sendChat.resetHistory();
|
||||||
await party.save();
|
await party.save();
|
||||||
|
|
||||||
await Group.processQuestProgress(participatingMember, progress);
|
await Group.processQuestProgress(participatingMember, progress);
|
||||||
@@ -569,7 +569,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if no uuids or emails are passed in', async () => {
|
it('throws an error if no uuids or emails are passed in', async () => {
|
||||||
await expect(Group.validateInvitations(null, null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -579,7 +579,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if only uuids are passed in, but they are not an array', async () => {
|
it('throws an error if only uuids are passed in, but they are not an array', async () => {
|
||||||
await expect(Group.validateInvitations({ uuid: 'user-id'}, null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({ uuids: 'user-id'}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -589,7 +589,7 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if only emails are passed in, but they are not an array', async () => {
|
it('throws an error if only emails are passed in, but they are not an array', async () => {
|
||||||
await expect(Group.validateInvitations(null, { emails: 'user@example.com'}, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: 'user@example.com'}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -599,27 +599,27 @@ describe('Group Model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if emails are not passed in, and uuid array is empty', async () => {
|
it('throws an error if emails are not passed in, and uuid array is empty', async () => {
|
||||||
await expect(Group.validateInvitations([], null, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({uuids: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
});
|
});
|
||||||
expect(res.t).to.be.calledOnce;
|
expect(res.t).to.be.calledOnce;
|
||||||
expect(res.t).to.be.calledWith('inviteMissingUuid');
|
expect(res.t).to.be.calledWith('inviteMustNotBeEmpty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if uuids are not passed in, and email array is empty', async () => {
|
it('throws an error if uuids are not passed in, and email array is empty', async () => {
|
||||||
await expect(Group.validateInvitations(null, [], res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
});
|
});
|
||||||
expect(res.t).to.be.calledOnce;
|
expect(res.t).to.be.calledOnce;
|
||||||
expect(res.t).to.be.calledWith('inviteMissingEmail');
|
expect(res.t).to.be.calledWith('inviteMustNotBeEmpty');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if uuids and emails are passed in as empty arrays', async () => {
|
it('throws an error if uuids and emails are passed in as empty arrays', async () => {
|
||||||
await expect(Group.validateInvitations([], [], res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({emails: [], uuids: []}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -639,7 +639,7 @@ describe('Group Model', () => {
|
|||||||
|
|
||||||
uuids.push('one-more-uuid'); // to put it over the limit
|
uuids.push('one-more-uuid'); // to put it over the limit
|
||||||
|
|
||||||
await expect(Group.validateInvitations(uuids, emails, res)).to.eventually.be.rejected.and.eql({
|
await expect(Group.validateInvitations({uuids, emails}, res)).to.eventually.be.rejected.and.eql({
|
||||||
httpCode: 400,
|
httpCode: 400,
|
||||||
message: 'Bad request.',
|
message: 'Bad request.',
|
||||||
name: 'BadRequest',
|
name: 'BadRequest',
|
||||||
@@ -657,33 +657,33 @@ describe('Group Model', () => {
|
|||||||
emails.push(`user-${i}@example.com`);
|
emails.push(`user-${i}@example.com`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Group.validateInvitations(uuids, emails, res);
|
await Group.validateInvitations({uuids, emails}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('does not throw an error if only user ids are passed in', async () => {
|
it('does not throw an error if only user ids are passed in', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], null, res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if only emails are passed in', async () => {
|
it('does not throw an error if only emails are passed in', async () => {
|
||||||
await Group.validateInvitations(null, ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if both uuids and emails are passed in', async () => {
|
it('does not throw an error if both uuids and emails are passed in', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2'], emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if uuids are passed in and emails are an empty array', async () => {
|
it('does not throw an error if uuids are passed in and emails are an empty array', async () => {
|
||||||
await Group.validateInvitations(['user-id', 'user-id2'], [], res);
|
await Group.validateInvitations({uuids: ['user-id', 'user-id2'], emails: []}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw an error if emails are passed in and uuids are an empty array', async () => {
|
it('does not throw an error if emails are passed in and uuids are an empty array', async () => {
|
||||||
await Group.validateInvitations([], ['user1@example.com', 'user2@example.com'], res);
|
await Group.validateInvitations({uuids: [], emails: ['user1@example.com', 'user2@example.com']}, res);
|
||||||
expect(res.t).to.not.be.called;
|
expect(res.t).to.not.be.called;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1020,32 +1020,6 @@ describe('Group Model', () => {
|
|||||||
expect(chat.user).to.not.exist;
|
expect(chat.user).to.not.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cuts down chat to 200 messages', () => {
|
|
||||||
for (let i = 0; i < 220; i++) {
|
|
||||||
party.chat.push({ text: 'a message' });
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(party.chat).to.have.a.lengthOf(220);
|
|
||||||
|
|
||||||
party.sendChat('message');
|
|
||||||
|
|
||||||
expect(party.chat).to.have.a.lengthOf(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('cuts down chat to 400 messages when group is subcribed', () => {
|
|
||||||
party.purchased.plan.customerId = 'test-customer-id';
|
|
||||||
|
|
||||||
for (let i = 0; i < 420; i++) {
|
|
||||||
party.chat.push({ text: 'a message' });
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(party.chat).to.have.a.lengthOf(420);
|
|
||||||
|
|
||||||
party.sendChat('message');
|
|
||||||
|
|
||||||
expect(party.chat).to.have.a.lengthOf(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('updates users about new messages in party', () => {
|
it('updates users about new messages in party', () => {
|
||||||
party.sendChat('message');
|
party.sendChat('message');
|
||||||
|
|
||||||
@@ -1869,6 +1843,62 @@ describe('Group Model', () => {
|
|||||||
expect(options.chat).to.eql(chat);
|
expect(options.chat).to.eql(chat);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sends webhooks for users with webhooks triggered by system messages', async () => {
|
||||||
|
let guild = new Group({
|
||||||
|
name: 'some guild',
|
||||||
|
type: 'guild',
|
||||||
|
});
|
||||||
|
|
||||||
|
let memberWithWebhook = new User({
|
||||||
|
guilds: [guild._id],
|
||||||
|
webhooks: [{
|
||||||
|
type: 'groupChatReceived',
|
||||||
|
url: 'http://someurl.com',
|
||||||
|
options: {
|
||||||
|
groupId: guild._id,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let memberWithoutWebhook = new User({
|
||||||
|
guilds: [guild._id],
|
||||||
|
});
|
||||||
|
let nonMemberWithWebhooks = new User({
|
||||||
|
webhooks: [{
|
||||||
|
type: 'groupChatReceived',
|
||||||
|
url: 'http://a-different-url.com',
|
||||||
|
options: {
|
||||||
|
groupId: generateUUID(),
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
memberWithWebhook.save(),
|
||||||
|
memberWithoutWebhook.save(),
|
||||||
|
nonMemberWithWebhooks.save(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
guild.leader = memberWithWebhook._id;
|
||||||
|
|
||||||
|
await guild.save();
|
||||||
|
|
||||||
|
const groupMessage = guild.sendChat('Test message.');
|
||||||
|
await groupMessage.save();
|
||||||
|
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
expect(groupChatReceivedWebhook.send).to.be.calledOnce;
|
||||||
|
|
||||||
|
let args = groupChatReceivedWebhook.send.args[0];
|
||||||
|
let webhooks = args[0].webhooks;
|
||||||
|
let options = args[1];
|
||||||
|
|
||||||
|
expect(webhooks).to.have.a.lengthOf(1);
|
||||||
|
expect(webhooks[0].id).to.eql(memberWithWebhook.webhooks[0].id);
|
||||||
|
expect(options.group).to.eql(guild);
|
||||||
|
expect(options.chat).to.eql(groupMessage);
|
||||||
|
});
|
||||||
|
|
||||||
it('sends webhooks for each user with webhooks in group', async () => {
|
it('sends webhooks for each user with webhooks in group', async () => {
|
||||||
let guild = new Group({
|
let guild = new Group({
|
||||||
name: 'some guild',
|
name: 'some guild',
|
||||||
@@ -1958,28 +1988,54 @@ describe('Group Model', () => {
|
|||||||
|
|
||||||
context('hasNotCancelled', () => {
|
context('hasNotCancelled', () => {
|
||||||
it('returns false if group does not have customer id', () => {
|
it('returns false if group does not have customer id', () => {
|
||||||
expect(party.hasNotCancelled()).to.be.undefined;
|
expect(party.hasNotCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns true if party does not have plan.dateTerminated', () => {
|
it('returns true if group does not have plan.dateTerminated', () => {
|
||||||
party.purchased.plan.customerId = 'test-id';
|
party.purchased.plan.customerId = 'test-id';
|
||||||
|
|
||||||
expect(party.hasNotCancelled()).to.be.true;
|
expect(party.hasNotCancelled()).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns false if party if plan.dateTerminated is after today', () => {
|
it('returns false if group if plan.dateTerminated is after today', () => {
|
||||||
party.purchased.plan.customerId = 'test-id';
|
party.purchased.plan.customerId = 'test-id';
|
||||||
party.purchased.plan.dateTerminated = moment().add(1, 'days').toDate();
|
party.purchased.plan.dateTerminated = moment().add(1, 'days').toDate();
|
||||||
|
|
||||||
expect(party.hasNotCancelled()).to.be.false;
|
expect(party.hasNotCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns false if party if plan.dateTerminated is before today', () => {
|
it('returns false if group if plan.dateTerminated is before today', () => {
|
||||||
party.purchased.plan.customerId = 'test-id';
|
party.purchased.plan.customerId = 'test-id';
|
||||||
party.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate();
|
party.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate();
|
||||||
|
|
||||||
expect(party.hasNotCancelled()).to.be.false;
|
expect(party.hasNotCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('hasCancelled', () => {
|
||||||
|
it('returns false if group does not have customer id', () => {
|
||||||
|
expect(party.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if group does not have plan.dateTerminated', () => {
|
||||||
|
party.purchased.plan.customerId = 'test-id';
|
||||||
|
|
||||||
|
expect(party.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if group if plan.dateTerminated is after today', () => {
|
||||||
|
party.purchased.plan.customerId = 'test-id';
|
||||||
|
party.purchased.plan.dateTerminated = moment().add(1, 'days').toDate();
|
||||||
|
|
||||||
|
expect(party.hasCancelled()).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if group if plan.dateTerminated is before today', () => {
|
||||||
|
party.purchased.plan.customerId = 'test-id';
|
||||||
|
party.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate();
|
||||||
|
|
||||||
|
expect(party.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -315,9 +315,8 @@ describe('User Model', () => {
|
|||||||
user = new User();
|
user = new User();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('returns false if user does not have customer id', () => {
|
it('returns false if user does not have customer id', () => {
|
||||||
expect(user.hasNotCancelled()).to.be.undefined;
|
expect(user.hasNotCancelled()).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns true if user does not have plan.dateTerminated', () => {
|
it('returns true if user does not have plan.dateTerminated', () => {
|
||||||
@@ -341,6 +340,38 @@ describe('User Model', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
context('hasCancelled', () => {
|
||||||
|
let user;
|
||||||
|
beforeEach(() => {
|
||||||
|
user = new User();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if user does not have customer id', () => {
|
||||||
|
expect(user.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if user does not have plan.dateTerminated', () => {
|
||||||
|
user.purchased.plan.customerId = 'test-id';
|
||||||
|
|
||||||
|
expect(user.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if user if plan.dateTerminated is after today', () => {
|
||||||
|
user.purchased.plan.customerId = 'test-id';
|
||||||
|
user.purchased.plan.dateTerminated = moment().add(1, 'days').toDate();
|
||||||
|
|
||||||
|
expect(user.hasCancelled()).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if user if plan.dateTerminated is before today', () => {
|
||||||
|
user.purchased.plan.customerId = 'test-id';
|
||||||
|
user.purchased.plan.dateTerminated = moment().subtract(1, 'days').toDate();
|
||||||
|
|
||||||
|
expect(user.hasCancelled()).to.be.false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
context('pre-save hook', () => {
|
context('pre-save hook', () => {
|
||||||
it('does not try to award achievements when achievements or items not selected in query', async () => {
|
it('does not try to award achievements when achievements or items not selected in query', async () => {
|
||||||
let user = new User();
|
let user = new User();
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
_id: groupLeader._id,
|
_id: groupLeader._id,
|
||||||
id: groupLeader._id,
|
id: groupLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
@@ -63,45 +71,56 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
|
|
||||||
context('private guild', () => {
|
context('private guild', () => {
|
||||||
let groupLeader;
|
let groupLeader;
|
||||||
|
let challengeLeader;
|
||||||
let group;
|
let group;
|
||||||
let challenge;
|
let challenge;
|
||||||
let members;
|
let members;
|
||||||
let user;
|
let nonMember;
|
||||||
|
let otherMember;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
nonMember = await generateUser();
|
||||||
|
|
||||||
let populatedGroup = await createAndPopulateGroup({
|
let populatedGroup = await createAndPopulateGroup({
|
||||||
groupDetails: {type: 'guild', privacy: 'private'},
|
groupDetails: {type: 'guild', privacy: 'private'},
|
||||||
members: 1,
|
members: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
groupLeader = populatedGroup.groupLeader;
|
groupLeader = populatedGroup.groupLeader;
|
||||||
group = populatedGroup.group;
|
group = populatedGroup.group;
|
||||||
members = populatedGroup.members;
|
members = populatedGroup.members;
|
||||||
|
|
||||||
challenge = await generateChallenge(groupLeader, group);
|
challengeLeader = members[0];
|
||||||
await members[0].post(`/challenges/${challenge._id}/join`);
|
otherMember = members[1];
|
||||||
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
|
||||||
|
challenge = await generateChallenge(challengeLeader, group);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if user doesn\'t have access to the challenge', async () => {
|
it('fails if user isn\'t in the guild and isn\'t challenge leader', async () => {
|
||||||
await expect(user.get(`/challenges/${challenge._id}`)).to.eventually.be.rejected.and.eql({
|
await expect(nonMember.get(`/challenges/${challenge._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotFound',
|
||||||
message: t('challengeNotFound'),
|
message: t('challengeNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return challenge data', async () => {
|
it('returns challenge data for any user in the guild', async () => {
|
||||||
let chal = await members[0].get(`/challenges/${challenge._id}`);
|
let chal = await otherMember.get(`/challenges/${challenge._id}`);
|
||||||
expect(chal.name).to.equal(challenge.name);
|
expect(chal.name).to.equal(challenge.name);
|
||||||
expect(chal._id).to.equal(challenge._id);
|
expect(chal._id).to.equal(challenge._id);
|
||||||
|
|
||||||
expect(chal.leader).to.eql({
|
expect(chal.leader).to.eql({
|
||||||
_id: groupLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: groupLeader._id,
|
id: challengeLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
@@ -114,53 +133,88 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
leader: groupLeader.id,
|
leader: groupLeader.id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns challenge data if challenge leader isn\'t in the guild or challenge', async () => {
|
||||||
|
await challengeLeader.post(`/groups/${group._id}/leave`);
|
||||||
|
await challengeLeader.sync();
|
||||||
|
expect(challengeLeader.guilds).to.be.empty; // check that leaving worked
|
||||||
|
|
||||||
|
let chal = await challengeLeader.get(`/challenges/${challenge._id}`);
|
||||||
|
expect(chal.name).to.equal(challenge.name);
|
||||||
|
expect(chal._id).to.equal(challenge._id);
|
||||||
|
|
||||||
|
expect(chal.leader).to.eql({
|
||||||
|
_id: challengeLeader._id,
|
||||||
|
id: challengeLeader._id,
|
||||||
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('party', () => {
|
context('party', () => {
|
||||||
let groupLeader;
|
let groupLeader;
|
||||||
|
let challengeLeader;
|
||||||
let group;
|
let group;
|
||||||
let challenge;
|
let challenge;
|
||||||
let members;
|
let members;
|
||||||
let user;
|
let nonMember;
|
||||||
|
let otherMember;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
nonMember = await generateUser();
|
||||||
|
|
||||||
let populatedGroup = await createAndPopulateGroup({
|
let populatedGroup = await createAndPopulateGroup({
|
||||||
groupDetails: {type: 'party'},
|
groupDetails: {type: 'party', privacy: 'private'},
|
||||||
members: 1,
|
members: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
groupLeader = populatedGroup.groupLeader;
|
groupLeader = populatedGroup.groupLeader;
|
||||||
group = populatedGroup.group;
|
group = populatedGroup.group;
|
||||||
members = populatedGroup.members;
|
members = populatedGroup.members;
|
||||||
|
|
||||||
challenge = await generateChallenge(groupLeader, group);
|
challengeLeader = members[0];
|
||||||
await members[0].post(`/challenges/${challenge._id}/join`);
|
otherMember = members[1];
|
||||||
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
|
||||||
|
challenge = await generateChallenge(challengeLeader, group);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if user doesn\'t have access to the challenge', async () => {
|
it('fails if user isn\'t in the party and isn\'t challenge leader', async () => {
|
||||||
await expect(user.get(`/challenges/${challenge._id}`)).to.eventually.be.rejected.and.eql({
|
await expect(nonMember.get(`/challenges/${challenge._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotFound',
|
||||||
message: t('challengeNotFound'),
|
message: t('challengeNotFound'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return challenge data', async () => {
|
it('returns challenge data for any user in the party', async () => {
|
||||||
let chal = await members[0].get(`/challenges/${challenge._id}`);
|
let chal = await otherMember.get(`/challenges/${challenge._id}`);
|
||||||
expect(chal.name).to.equal(challenge.name);
|
expect(chal.name).to.equal(challenge.name);
|
||||||
expect(chal._id).to.equal(challenge._id);
|
expect(chal._id).to.equal(challenge._id);
|
||||||
|
|
||||||
expect(chal.leader).to.eql({
|
expect(chal.leader).to.eql({
|
||||||
_id: groupLeader._id,
|
_id: challengeLeader._id,
|
||||||
id: groupLeader.id,
|
id: challengeLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(chal.group).to.eql({
|
expect(chal.group).to.eql({
|
||||||
_id: group._id,
|
_id: group._id,
|
||||||
id: group.id,
|
id: group._id,
|
||||||
categories: [],
|
categories: [],
|
||||||
name: group.name,
|
name: group.name,
|
||||||
summary: group.name,
|
summary: group.name,
|
||||||
@@ -169,5 +223,29 @@ describe('GET /challenges/:challengeId', () => {
|
|||||||
leader: groupLeader.id,
|
leader: groupLeader.id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns challenge data if challenge leader isn\'t in the party or challenge', async () => {
|
||||||
|
await challengeLeader.post('/groups/party/leave');
|
||||||
|
await challengeLeader.sync();
|
||||||
|
expect(challengeLeader.party._id).to.be.undefined; // check that leaving worked
|
||||||
|
|
||||||
|
let chal = await challengeLeader.get(`/challenges/${challenge._id}`);
|
||||||
|
expect(chal.name).to.equal(challenge.name);
|
||||||
|
expect(chal._id).to.equal(challenge._id);
|
||||||
|
|
||||||
|
expect(chal.leader).to.eql({
|
||||||
|
_id: challengeLeader._id,
|
||||||
|
id: challengeLeader._id,
|
||||||
|
profile: {name: challengeLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: challengeLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
generateGroup,
|
generateGroup,
|
||||||
|
createAndPopulateGroup,
|
||||||
generateChallenge,
|
generateChallenge,
|
||||||
translate as t,
|
translate as t,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
@@ -10,7 +11,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let user;
|
let user;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
user = await generateUser({ balance: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('validates optional req.query.lastId to be an UUID', async () => {
|
it('validates optional req.query.lastId to be an UUID', async () => {
|
||||||
@@ -21,7 +22,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if challenge doesn\'t exists', async () => {
|
it('fails if challenge doesn\'t exist', async () => {
|
||||||
await expect(user.get(`/challenges/${generateUUID()}/members`)).to.eventually.be.rejected.and.eql({
|
await expect(user.get(`/challenges/${generateUUID()}/members`)).to.eventually.be.rejected.and.eql({
|
||||||
code: 404,
|
code: 404,
|
||||||
error: 'NotFound',
|
error: 'NotFound',
|
||||||
@@ -29,8 +30,8 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if user doesn\'t have access to the challenge', async () => {
|
it('fails if user isn\'t in the private group and isn\'t challenge leader', async () => {
|
||||||
let group = await generateGroup(user);
|
let group = await generateGroup(user, {type: 'party', privacy: 'private'});
|
||||||
let challenge = await generateChallenge(user, group);
|
let challenge = await generateChallenge(user, group);
|
||||||
let anotherUser = await generateUser();
|
let anotherUser = await generateUser();
|
||||||
|
|
||||||
@@ -41,6 +42,35 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('works if user isn\'t in the private group but is challenge leader', async () => {
|
||||||
|
let populatedGroup = await createAndPopulateGroup({
|
||||||
|
groupDetails: {type: 'party', privacy: 'private'},
|
||||||
|
members: 1,
|
||||||
|
});
|
||||||
|
let groupLeader = populatedGroup.groupLeader;
|
||||||
|
let challengeLeader = populatedGroup.members[0];
|
||||||
|
let challenge = await generateChallenge(challengeLeader, populatedGroup.group);
|
||||||
|
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
||||||
|
await challengeLeader.post('/groups/party/leave');
|
||||||
|
await challengeLeader.sync();
|
||||||
|
expect(challengeLeader.party._id).to.be.undefined; // check that leaving worked
|
||||||
|
|
||||||
|
let res = await challengeLeader.get(`/challenges/${challenge._id}/members`);
|
||||||
|
expect(res[0]).to.eql({
|
||||||
|
_id: groupLeader._id,
|
||||||
|
id: groupLeader._id,
|
||||||
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('works with challenges belonging to public guild', async () => {
|
it('works with challenges belonging to public guild', async () => {
|
||||||
let leader = await generateUser({balance: 4});
|
let leader = await generateUser({balance: 4});
|
||||||
let group = await generateGroup(leader, {type: 'guild', privacy: 'public', name: generateUUID()});
|
let group = await generateGroup(leader, {type: 'guild', privacy: 'public', name: generateUUID()});
|
||||||
@@ -51,8 +81,16 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
_id: leader._id,
|
_id: leader._id,
|
||||||
id: leader._id,
|
id: leader._id,
|
||||||
profile: {name: leader.profile.name},
|
profile: {name: leader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: leader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,8 +104,16 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
_id: anotherUser._id,
|
_id: anotherUser._id,
|
||||||
id: anotherUser._id,
|
id: anotherUser._id,
|
||||||
profile: {name: anotherUser.profile.name},
|
profile: {name: anotherUser.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: anotherUser.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,7 +131,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=not-true`);
|
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=not-true`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -104,7 +150,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members`);
|
let res = await user.get(`/challenges/${challenge._id}/members`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -123,7 +169,7 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=true`);
|
let res = await user.get(`/challenges/${challenge._id}/members?includeAllMembers=true`);
|
||||||
expect(res.length).to.equal(32);
|
expect(res.length).to.equal(32);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ describe('GET /challenges/:challengeId/members/:memberId', () => {
|
|||||||
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [{type: 'habit', text: taskText}]);
|
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [{type: 'habit', text: taskText}]);
|
||||||
|
|
||||||
let memberProgress = await user.get(`/challenges/${challenge._id}/members/${groupLeader._id}`);
|
let memberProgress = await user.get(`/challenges/${challenge._id}/members/${groupLeader._id}`);
|
||||||
expect(memberProgress).to.have.all.keys(['_id', 'id', 'profile', 'tasks']);
|
expect(memberProgress).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile', 'tasks']);
|
||||||
expect(memberProgress.profile).to.have.all.keys(['name']);
|
expect(memberProgress.profile).to.have.all.keys(['name']);
|
||||||
expect(memberProgress.tasks.length).to.equal(1);
|
expect(memberProgress.tasks.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -46,6 +54,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,6 +74,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -65,6 +89,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -125,6 +157,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: privateGuild.leader._id,
|
_id: privateGuild.leader._id,
|
||||||
id: privateGuild.leader._id,
|
id: privateGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -132,6 +172,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: privateGuild.leader._id,
|
_id: privateGuild.leader._id,
|
||||||
id: privateGuild.leader._id,
|
id: privateGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -235,6 +283,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -242,6 +298,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -254,6 +318,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -261,6 +333,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: party.leader._id,
|
_id: party.leader._id,
|
||||||
id: party.leader._id,
|
id: party.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -288,6 +368,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -295,6 +383,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,6 +403,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
|
||||||
expect(foundChallenge2).to.exist;
|
expect(foundChallenge2).to.exist;
|
||||||
@@ -314,6 +418,14 @@ describe('GET challenges/groups/:groupId', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge.group).to.eql({
|
expect(foundChallenge.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -62,6 +70,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge1.group).to.eql({
|
expect(foundChallenge1.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -79,6 +95,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge2.group).to.eql({
|
expect(foundChallenge2.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -101,6 +125,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge1.group).to.eql({
|
expect(foundChallenge1.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
@@ -118,6 +150,14 @@ describe('GET challenges/user', () => {
|
|||||||
_id: publicGuild.leader._id,
|
_id: publicGuild.leader._id,
|
||||||
id: publicGuild.leader._id,
|
id: publicGuild.leader._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(foundChallenge2.group).to.eql({
|
expect(foundChallenge2.group).to.eql({
|
||||||
_id: publicGuild._id,
|
_id: publicGuild._id,
|
||||||
|
|||||||
@@ -94,16 +94,6 @@ describe('POST /challenges', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when non-leader member creates a challenge in leaderOnly group', async () => {
|
|
||||||
await expect(groupMember.post('/challenges', {
|
|
||||||
group: group._id,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('onlyGroupLeaderChal'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows non-leader member to create a challenge', async () => {
|
it('allows non-leader member to create a challenge', async () => {
|
||||||
let populatedGroup = await createAndPopulateGroup({
|
let populatedGroup = await createAndPopulateGroup({
|
||||||
members: 1,
|
members: 1,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ describe('POST /challenges/:challengeId/join', () => {
|
|||||||
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user doesn\'t have permissions to access the challenge', async () => {
|
it('returns an error when user isn\'t in the private group and isn\'t challenge leader', async () => {
|
||||||
let unauthorizedUser = await generateUser();
|
let unauthorizedUser = await generateUser();
|
||||||
|
|
||||||
await expect(unauthorizedUser.post(`/challenges/${challenge._id}/join`)).to.eventually.be.rejected.and.eql({
|
await expect(unauthorizedUser.post(`/challenges/${challenge._id}/join`)).to.eventually.be.rejected.and.eql({
|
||||||
@@ -56,6 +56,16 @@ describe('POST /challenges/:challengeId/join', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('succeeds when user isn\'t in the private group but is challenge leader', async () => {
|
||||||
|
await groupLeader.post(`/challenges/${challenge._id}/leave`);
|
||||||
|
await groupLeader.post(`/groups/${group._id}/leave`);
|
||||||
|
await groupLeader.sync();
|
||||||
|
expect(groupLeader.guilds).to.be.empty; // check that leaving worked
|
||||||
|
|
||||||
|
let res = await groupLeader.post(`/challenges/${challenge._id}/join`);
|
||||||
|
expect(res.name).to.equal(challenge.name);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns challenge data', async () => {
|
it('returns challenge data', async () => {
|
||||||
let res = await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
let res = await authorizedUser.post(`/challenges/${challenge._id}/join`);
|
||||||
|
|
||||||
@@ -69,6 +79,14 @@ describe('POST /challenges/:challengeId/join', () => {
|
|||||||
_id: groupLeader._id,
|
_id: groupLeader._id,
|
||||||
id: groupLeader._id,
|
id: groupLeader._id,
|
||||||
profile: {name: groupLeader.profile.name},
|
profile: {name: groupLeader.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: groupLeader.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res.name).to.equal(challenge.name);
|
expect(res.name).to.equal(challenge.name);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ describe('PUT /challenges/:challengeId', () => {
|
|||||||
_id: member._id,
|
_id: member._id,
|
||||||
id: member._id,
|
id: member._id,
|
||||||
profile: {name: member.profile.name},
|
profile: {name: member.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: member.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
expect(res.name).to.equal('New Challenge Name');
|
expect(res.name).to.equal('New Challenge Name');
|
||||||
expect(res.description).to.equal('New challenge description.');
|
expect(res.description).to.equal('New challenge description.');
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ describe('GET /groups', () => {
|
|||||||
let page2 = await expect(user.get('/groups?type=publicGuilds&paginate=true&page=2'))
|
let page2 = await expect(user.get('/groups?type=publicGuilds&paginate=true&page=2'))
|
||||||
.to.eventually.have.a.lengthOf(1 + 4); // 1 created now, 4 by other tests
|
.to.eventually.have.a.lengthOf(1 + 4); // 1 created now, 4 by other tests
|
||||||
expect(page2[4].name).to.equal('guild with less members');
|
expect(page2[4].name).to.equal('guild with less members');
|
||||||
});
|
}).timeout(10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns all the user\'s guilds when guilds passed in as query', async () => {
|
it('returns all the user\'s guilds when guilds passed in as query', async () => {
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
_id: invited._id,
|
_id: invited._id,
|
||||||
id: invited._id,
|
id: invited._id,
|
||||||
profile: {name: invited.profile.name},
|
profile: {name: invited.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: invited.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +66,7 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
let invited = await generateUser();
|
let invited = await generateUser();
|
||||||
await user.post(`/groups/${group._id}/invite`, {uuids: [invited._id]});
|
await user.post(`/groups/${group._id}/invite`, {uuids: [invited._id]});
|
||||||
let res = await user.get('/groups/party/invites');
|
let res = await user.get('/groups/party/invites');
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,10 +84,10 @@ describe('GET /groups/:groupId/invites', () => {
|
|||||||
let res = await leader.get(`/groups/${group._id}/invites`);
|
let res = await leader.get(`/groups/${group._id}/invites`);
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
}).timeout(10000);
|
||||||
|
|
||||||
it('supports using req.query.lastId to get more invites', async function () {
|
it('supports using req.query.lastId to get more invites', async function () {
|
||||||
this.timeout(30000); // @TODO: times out after 8 seconds
|
this.timeout(30000); // @TODO: times out after 8 seconds
|
||||||
|
|||||||
@@ -56,13 +56,21 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
_id: user._id,
|
_id: user._id,
|
||||||
id: user._id,
|
id: user._id,
|
||||||
profile: {name: user.profile.name},
|
profile: {name: user.profile.name},
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
username: user.auth.local.username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
verifiedUsername: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('populates only some fields', async () => {
|
it('populates only some fields', async () => {
|
||||||
await generateGroup(user, {type: 'party', name: generateUUID()});
|
await generateGroup(user, {type: 'party', name: generateUUID()});
|
||||||
let res = await user.get('/groups/party/members');
|
let res = await user.get('/groups/party/members');
|
||||||
expect(res[0]).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(res[0]).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(res[0].profile).to.have.all.keys(['name']);
|
expect(res[0].profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,7 +82,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
@@ -95,7 +103,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
@@ -120,7 +128,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
let res = await user.get('/groups/party/members');
|
let res = await user.get('/groups/party/members');
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -137,7 +145,7 @@ describe('GET /groups/:groupId/members', () => {
|
|||||||
let res = await user.get('/groups/party/members?includeAllMembers=true');
|
let res = await user.get('/groups/party/members?includeAllMembers=true');
|
||||||
expect(res.length).to.equal(30);
|
expect(res.length).to.equal(30);
|
||||||
res.forEach(member => {
|
res.forEach(member => {
|
||||||
expect(member).to.have.all.keys(['_id', 'id', 'profile']);
|
expect(member).to.have.all.keys(['_id', 'auth', 'flags', 'id', 'profile']);
|
||||||
expect(member.profile).to.have.all.keys(['name']);
|
expect(member.profile).to.have.all.keys(['name']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,73 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('username invites', () => {
|
||||||
|
it('returns an error when invited user is not found', async () => {
|
||||||
|
const fakeID = 'fakeuserid';
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [fakeID],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userWithUsernameNotFound', {username: fakeID}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when inviting yourself to a group', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [inviter.auth.local.lowerCaseUsername],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('cannotInviteSelfToGroup'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites a user to a group by username', async () => {
|
||||||
|
const userToInvite = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [userToInvite.auth.local.lowerCaseUsername],
|
||||||
|
})).to.eventually.deep.equal([{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
publicGuild: false,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
await expect(userToInvite.get('/user'))
|
||||||
|
.to.eventually.have.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites multiple users to a group by uuid', async () => {
|
||||||
|
const userToInvite = await generateUser();
|
||||||
|
const userToInvite2 = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
usernames: [userToInvite.auth.local.lowerCaseUsername, userToInvite2.auth.local.lowerCaseUsername],
|
||||||
|
})).to.eventually.deep.equal([
|
||||||
|
{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
publicGuild: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
publicGuild: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(userToInvite.get('/user')).to.eventually.have.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
await expect(userToInvite2.get('/user')).to.eventually.have.nested.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('user id invites', () => {
|
describe('user id invites', () => {
|
||||||
it('returns an error when inviter has no chat privileges', async () => {
|
it('returns an error when inviter has no chat privileges', async () => {
|
||||||
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
let inviterMuted = await inviter.update({'flags.chatRevoked': true});
|
||||||
@@ -93,7 +160,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('inviteMissingUuid'),
|
message: t('inviteMustNotBeEmpty'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -228,7 +295,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('inviteMissingEmail'),
|
message: t('inviteMustNotBeEmpty'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -417,7 +484,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
expect(await inviter.post(`/groups/${group._id}/invite`, {
|
expect(await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
uuids: generatedInvites.map(invite => invite._id),
|
uuids: generatedInvites.map(invite => invite._id),
|
||||||
})).to.be.an('array');
|
})).to.be.an('array');
|
||||||
});
|
}).timeout(10000);
|
||||||
|
|
||||||
// @TODO: Add this after we are able to mock the group plan route
|
// @TODO: Add this after we are able to mock the group plan route
|
||||||
xit('returns an error when a non-leader invites to a group plan', async () => {
|
xit('returns an error when a non-leader invites to a group plan', async () => {
|
||||||
@@ -564,7 +631,7 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
expect(await inviter.post(`/groups/${party._id}/invite`, {
|
expect(await inviter.post(`/groups/${party._id}/invite`, {
|
||||||
uuids: generatedInvites.map(invite => invite._id),
|
uuids: generatedInvites.map(invite => invite._id),
|
||||||
})).to.be.an('array');
|
})).to.be.an('array');
|
||||||
});
|
}).timeout(10000);
|
||||||
|
|
||||||
it('does not allow 30+ members in a party', async () => {
|
it('does not allow 30+ members in a party', async () => {
|
||||||
let invitesToGenerate = [];
|
let invitesToGenerate = [];
|
||||||
@@ -582,6 +649,6 @@ describe('Post /groups/:groupId/invite', () => {
|
|||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: t('partyExceedsMembersLimit', {maxMembersParty: PARTY_LIMIT_MEMBERS}),
|
message: t('partyExceedsMembersLimit', {maxMembersParty: PARTY_LIMIT_MEMBERS}),
|
||||||
});
|
});
|
||||||
});
|
}).timeout(10000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,5 +56,5 @@ describe('GET /hall/patrons', () => {
|
|||||||
expect(morePatrons.length).to.equal(2);
|
expect(morePatrons.length).to.equal(2);
|
||||||
expect(morePatrons[0].backer.tier).to.equal(2);
|
expect(morePatrons[0].backer.tier).to.equal(2);
|
||||||
expect(morePatrons[1].backer.tier).to.equal(1);
|
expect(morePatrons[1].backer.tier).to.equal(1);
|
||||||
});
|
}).timeout(10000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../helpers/api-integration/v4';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('GET /inbox/messages', () => {
|
describe('GET /inbox/messages', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -22,17 +22,27 @@ describe('GET /inbox/messages', () => {
|
|||||||
message: 'third',
|
message: 'third',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// message to yourself
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'fourth',
|
||||||
|
});
|
||||||
|
|
||||||
await user.sync();
|
await user.sync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the user inbox messages as an array of ordered messages (from most to least recent)', async () => {
|
it('returns the user inbox messages as an array of ordered messages (from most to least recent)', async () => {
|
||||||
const messages = await user.get('/inbox/messages');
|
const messages = await user.get('/inbox/messages');
|
||||||
|
|
||||||
expect(messages.length).to.equal(3);
|
expect(messages.length).to.equal(4);
|
||||||
expect(messages.length).to.equal(Object.keys(user.inbox.messages).length);
|
|
||||||
|
|
||||||
expect(messages[0].text).to.equal('third');
|
// message to yourself
|
||||||
expect(messages[1].text).to.equal('second');
|
expect(messages[0].text).to.equal('fourth');
|
||||||
expect(messages[2].text).to.equal('first');
|
expect(messages[0].sent).to.equal(false);
|
||||||
|
expect(messages[0].uuid).to.equal(user._id);
|
||||||
|
|
||||||
|
expect(messages[1].text).to.equal('third');
|
||||||
|
expect(messages[2].text).to.equal('second');
|
||||||
|
expect(messages[3].text).to.equal('first');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -34,7 +34,7 @@ describe('GET /members/:memberId', () => {
|
|||||||
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
|
||||||
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives', 'flags',
|
||||||
]);
|
]);
|
||||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
expect(Object.keys(memberRes.auth)).to.eql(['local', 'timestamps']);
|
||||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||||
'size', 'hair', 'skin', 'shirt',
|
'size', 'hair', 'skin', 'shirt',
|
||||||
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
'chair', 'costume', 'sleep', 'background', 'tasks', 'disableClasses',
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ describe('POST /members/send-private-message', () => {
|
|||||||
let receiver = await generateUser();
|
let receiver = await generateUser();
|
||||||
// const initialNotifications = receiver.notifications.length;
|
// const initialNotifications = receiver.notifications.length;
|
||||||
|
|
||||||
await userToSendMessage.post('/members/send-private-message', {
|
const response = await userToSendMessage.post('/members/send-private-message', {
|
||||||
message: messageToSend,
|
message: messageToSend,
|
||||||
toUserId: receiver._id,
|
toUserId: receiver._id,
|
||||||
});
|
});
|
||||||
@@ -116,6 +116,9 @@ describe('POST /members/send-private-message', () => {
|
|||||||
return message.uuid === receiver._id && message.text === messageToSend;
|
return message.uuid === receiver._id && message.text === messageToSend;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(response.message.text).to.deep.equal(sendersMessageInSendersInbox.text);
|
||||||
|
expect(response.message.uuid).to.deep.equal(sendersMessageInSendersInbox.uuid);
|
||||||
|
|
||||||
// @TODO waiting for mobile support
|
// @TODO waiting for mobile support
|
||||||
// expect(updatedReceiver.notifications.length).to.equal(initialNotifications + 1);
|
// expect(updatedReceiver.notifications.length).to.equal(initialNotifications + 1);
|
||||||
// const notification = updatedReceiver.notifications[updatedReceiver.notifications.length - 1];
|
// const notification = updatedReceiver.notifications[updatedReceiver.notifications.length - 1];
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ describe('payments : amazon #subscribeCancel', () => {
|
|||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
amazonSubscribeCancelStub = sinon.stub(amzLib, 'cancelSubscription').returnsPromise().resolves({});
|
amazonSubscribeCancelStub = sinon.stub(amzLib, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('payments - amazon - #checkout', () => {
|
|||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
amazonCheckoutStub = sinon.stub(amzLib, 'checkout').returnsPromise().resolves({});
|
amazonCheckoutStub = sinon.stub(amzLib, 'checkout').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ describe('payments - amazon - #subscribe', () => {
|
|||||||
let coupon;
|
let coupon;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
subscribeWithAmazonStub = sinon.stub(amzLib, 'subscribe').returnsPromise().resolves({});
|
subscribeWithAmazonStub = sinon.stub(amzLib, 'subscribe').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe('payments : apple #cancelSubscribe', () => {
|
|||||||
let cancelStub;
|
let cancelStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cancelStub = sinon.stub(applePayments, 'cancelSubscribe').returnsPromise().resolves({});
|
cancelStub = sinon.stub(applePayments, 'cancelSubscribe').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe('payments : apple #verify', () => {
|
|||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
verifyStub = sinon.stub(applePayments, 'verifyGemPurchase').returnsPromise().resolves({});
|
verifyStub = sinon.stub(applePayments, 'verifyGemPurchase').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('payments : apple #subscribe', () => {
|
|||||||
let subscribeStub;
|
let subscribeStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
subscribeStub = sinon.stub(applePayments, 'subscribe').returnsPromise().resolves({});
|
subscribeStub = sinon.stub(applePayments, 'subscribe').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe('payments : google #cancelSubscribe', () => {
|
|||||||
let cancelStub;
|
let cancelStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cancelStub = sinon.stub(googlePayments, 'cancelSubscribe').returnsPromise().resolves({});
|
cancelStub = sinon.stub(googlePayments, 'cancelSubscribe').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('payments : google #subscribe', () => {
|
|||||||
let subscribeStub;
|
let subscribeStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
subscribeStub = sinon.stub(googlePayments, 'subscribe').returnsPromise().resolves({});
|
subscribeStub = sinon.stub(googlePayments, 'subscribe').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe('payments : google #verify', () => {
|
|||||||
let verifyStub;
|
let verifyStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
verifyStub = sinon.stub(googlePayments, 'verifyGemPurchase').returnsPromise().resolves({});
|
verifyStub = sinon.stub(googlePayments, 'verifyGemPurchase').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe('payments : paypal #checkout', () => {
|
|||||||
let checkoutStub;
|
let checkoutStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
checkoutStub = sinon.stub(paypalPayments, 'checkout').returnsPromise().resolves('/');
|
checkoutStub = sinon.stub(paypalPayments, 'checkout').resolves('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe('payments : paypal #checkoutSuccess', () => {
|
|||||||
let checkoutSuccessStub;
|
let checkoutSuccessStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
checkoutSuccessStub = sinon.stub(paypalPayments, 'checkoutSuccess').returnsPromise().resolves({});
|
checkoutSuccessStub = sinon.stub(paypalPayments, 'checkoutSuccess').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe('payments : paypal #subscribe', () => {
|
|||||||
let subscribeStub;
|
let subscribeStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
subscribeStub = sinon.stub(paypalPayments, 'subscribe').returnsPromise().resolves('/');
|
subscribeStub = sinon.stub(paypalPayments, 'subscribe').resolves('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe('payments : paypal #subscribeCancel', () => {
|
|||||||
let subscribeCancelStub;
|
let subscribeCancelStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
subscribeCancelStub = sinon.stub(paypalPayments, 'subscribeCancel').returnsPromise().resolves('/');
|
subscribeCancelStub = sinon.stub(paypalPayments, 'subscribeCancel').resolves('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe('payments : paypal #subscribeSuccess', () => {
|
|||||||
let subscribeSuccessStub;
|
let subscribeSuccessStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
subscribeSuccessStub = sinon.stub(paypalPayments, 'subscribeSuccess').returnsPromise().resolves({});
|
subscribeSuccessStub = sinon.stub(paypalPayments, 'subscribeSuccess').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe('payments - paypal - #ipn', () => {
|
|||||||
let ipnStub;
|
let ipnStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
ipnStub = sinon.stub(paypalPayments, 'ipn').returnsPromise().resolves({});
|
ipnStub = sinon.stub(paypalPayments, 'ipn').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ describe('payments - stripe - #subscribeCancel', () => {
|
|||||||
|
|
||||||
describe('success', () => {
|
describe('success', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
stripeCancelSubscriptionStub = sinon.stub(stripePayments, 'cancelSubscription').returnsPromise().resolves({});
|
stripeCancelSubscriptionStub = sinon.stub(stripePayments, 'cancelSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe('payments - stripe - #checkout', () => {
|
|||||||
let stripeCheckoutSubscriptionStub;
|
let stripeCheckoutSubscriptionStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').returnsPromise().resolves({});
|
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe('payments - stripe - #subscribeEdit', () => {
|
|||||||
let stripeEditSubscriptionStub;
|
let stripeEditSubscriptionStub;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').returnsPromise().resolves({});
|
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').resolves({});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
sleep,
|
sleep,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
import { chatModel as Chat } from '../../../../../website/server/models/message';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/accept', () => {
|
describe('POST /groups/:groupId/quests/accept', () => {
|
||||||
const PET_QUEST = 'whale';
|
const PET_QUEST = 'whale';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
sleep,
|
sleep,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
import { chatModel as Chat } from '../../../../../website/server/models/message';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/force-start', () => {
|
describe('POST /groups/:groupId/quests/force-start', () => {
|
||||||
const PET_QUEST = 'whale';
|
const PET_QUEST = 'whale';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
import { quests as questScrolls } from '../../../../../website/common/script/content';
|
import { quests as questScrolls } from '../../../../../website/common/script/content';
|
||||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
import { chatModel as Chat } from '../../../../../website/server/models/message';
|
||||||
import apiError from '../../../../../website/server/libs/apiError';
|
import apiError from '../../../../../website/server/libs/apiError';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
sleep,
|
sleep,
|
||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
import { model as Chat } from '../../../../../website/server/models/chat';
|
import { chatModel as Chat } from '../../../../../website/server/models/message';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/reject', () => {
|
describe('POST /groups/:groupId/quests/reject', () => {
|
||||||
let questingGroup;
|
let questingGroup;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
createAndPopulateGroup,
|
createAndPopulateGroup, translate as t,
|
||||||
} from '../../../../../helpers/api-integration/v3';
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
import { find } from 'lodash';
|
import {find} from 'lodash';
|
||||||
|
|
||||||
describe('PUT /tasks/:id', () => {
|
describe('PUT /tasks/:id', () => {
|
||||||
let user, guild, member, member2, task;
|
let user, guild, member, member2, task;
|
||||||
@@ -38,16 +38,64 @@ describe('PUT /tasks/:id', () => {
|
|||||||
|
|
||||||
it('updates a group task', async () => {
|
it('updates a group task', async () => {
|
||||||
let savedHabit = await user.put(`/tasks/${task._id}`, {
|
let savedHabit = await user.put(`/tasks/${task._id}`, {
|
||||||
text: 'some new text',
|
|
||||||
up: false,
|
|
||||||
down: false,
|
|
||||||
notes: 'some new notes',
|
notes: 'some new notes',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(savedHabit.text).to.eql('some new text');
|
|
||||||
expect(savedHabit.notes).to.eql('some new notes');
|
expect(savedHabit.notes).to.eql('some new notes');
|
||||||
expect(savedHabit.up).to.eql(false);
|
});
|
||||||
expect(savedHabit.down).to.eql(false);
|
|
||||||
|
it('updates a group task - approval is required', async () => {
|
||||||
|
// allow to manage
|
||||||
|
await user.post(`/groups/${guild._id}/add-manager`, {
|
||||||
|
managerId: member._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// change the todo
|
||||||
|
task = await member.put(`/tasks/${task._id}`, {
|
||||||
|
text: 'new text!',
|
||||||
|
requiresApproval: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let memberTasks = await member.get('/tasks/user');
|
||||||
|
let syncedTask = find(memberTasks, (memberTask) => memberTask.group.taskId === task._id);
|
||||||
|
|
||||||
|
// score up to trigger approval
|
||||||
|
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('taskApprovalHasBeenRequested'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates a group task with checklist', async () => {
|
||||||
|
// add a new todo
|
||||||
|
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo',
|
||||||
|
type: 'todo',
|
||||||
|
checklist: [
|
||||||
|
{
|
||||||
|
text: 'checklist 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||||
|
|
||||||
|
// change the checklist text
|
||||||
|
task = await user.put(`/tasks/${task._id}`, {
|
||||||
|
checklist: [
|
||||||
|
{
|
||||||
|
id: task.checklist[0].id,
|
||||||
|
text: 'checklist 1 - edit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'checklist 2 - edit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(task.checklist.length).to.eql(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates the linked tasks', async () => {
|
it('updates the linked tasks', async () => {
|
||||||
|
|||||||
@@ -3,25 +3,41 @@ import {
|
|||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('DELETE user message', () => {
|
describe('DELETE user message', () => {
|
||||||
let user;
|
let user, messagesId, otherUser;
|
||||||
|
|
||||||
beforeEach(async () => {
|
before(async () => {
|
||||||
user = await generateUser({ inbox: { messages: { first: 'message', second: 'message' } } });
|
[user, otherUser] = await Promise.all([generateUser(), generateUser()]);
|
||||||
expect(user.inbox.messages.first).to.eql('message');
|
await user.post('/members/send-private-message', {
|
||||||
expect(user.inbox.messages.second).to.eql('message');
|
toUserId: otherUser.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: otherUser.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
|
||||||
|
let userRes = await user.get('/user');
|
||||||
|
|
||||||
|
messagesId = Object.keys(userRes.inbox.messages);
|
||||||
|
expect(messagesId.length).to.eql(2);
|
||||||
|
expect(userRes.inbox.messages[messagesId[0]].text).to.eql('second');
|
||||||
|
expect(userRes.inbox.messages[messagesId[1]].text).to.eql('first');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('one message', async () => {
|
it('one message', async () => {
|
||||||
let result = await user.del('/user/messages/first');
|
let result = await user.del(`/user/messages/${messagesId[0]}`);
|
||||||
await user.sync();
|
messagesId = Object.keys(result);
|
||||||
expect(result).to.eql({ second: 'message' });
|
expect(messagesId.length).to.eql(1);
|
||||||
expect(user.inbox.messages).to.eql({ second: 'message' });
|
|
||||||
|
let userRes = await user.get('/user');
|
||||||
|
expect(Object.keys(userRes.inbox.messages).length).to.eql(1);
|
||||||
|
expect(userRes.inbox.messages[messagesId[0]].text).to.eql('first');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clear all', async () => {
|
it('clear all', async () => {
|
||||||
let result = await user.del('/user/messages');
|
let result = await user.del('/user/messages');
|
||||||
await user.sync();
|
let userRes = await user.get('/user');
|
||||||
expect(user.inbox.messages).to.eql({});
|
expect(userRes.inbox.messages).to.eql({});
|
||||||
expect(result).to.eql({});
|
expect(result).to.eql({});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,14 +39,16 @@ describe('POST /user/push-devices', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if user already has the push device', async () => {
|
it('fails silently if user already has the push device', async () => {
|
||||||
await user.post('/user/push-devices', {type, regId});
|
await user.post('/user/push-devices', {type, regId});
|
||||||
await expect(user.post('/user/push-devices', {type, regId}))
|
const response = await user.post('/user/push-devices', {type, regId});
|
||||||
.to.eventually.be.rejected.and.eql({
|
await user.sync();
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
expect(response.message).to.equal(t('pushDeviceAdded'));
|
||||||
message: t('pushDeviceAlreadyAdded'),
|
expect(response.data[0].type).to.equal(type);
|
||||||
});
|
expect(response.data[0].regId).to.equal(regId);
|
||||||
|
expect(user.pushDevices[0].type).to.equal(type);
|
||||||
|
expect(user.pushDevices[0].regId).to.equal(regId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds a push device to the user', async () => {
|
it('adds a push device to the user', async () => {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ describe('PUT /user', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('profile.name cannot be an empty string or null', async () => {
|
it('validates profile.name', async () => {
|
||||||
await expect(user.put('/user', {
|
await expect(user.put('/user', {
|
||||||
'profile.name': ' ', // string should be trimmed
|
'profile.name': ' ', // string should be trimmed
|
||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
@@ -76,7 +76,23 @@ describe('PUT /user', () => {
|
|||||||
})).to.eventually.be.rejected.and.eql({
|
})).to.eventually.be.rejected.and.eql({
|
||||||
code: 400,
|
code: 400,
|
||||||
error: 'BadRequest',
|
error: 'BadRequest',
|
||||||
message: 'User validation failed',
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': 'this is a very long display name that will not be allowed due to length',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('displaynameIssueLength'),
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': 'TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('displaynameIssueSlur'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,6 +41,23 @@ describe('POST /user/auth/local/register', () => {
|
|||||||
expect(user.newUser).to.eql(true);
|
expect(user.newUser).to.eql(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('registers a new user and sets verifiedUsername to true', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user._id).to.exist;
|
||||||
|
expect(user.apiToken).to.exist;
|
||||||
|
expect(user.flags.verifiedUsername).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
xit('remove spaces from username', async () => {
|
xit('remove spaces from username', async () => {
|
||||||
// TODO can probably delete this test now
|
// TODO can probably delete this test now
|
||||||
let username = ' usernamewithspaces ';
|
let username = ' usernamewithspaces ';
|
||||||
@@ -259,7 +276,7 @@ describe('POST /user/auth/local/register', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enrolls new users in an A/B test', async () => {
|
xit('enrolls new users in an A/B test', async () => {
|
||||||
let username = generateRandomUserName();
|
let username = generateRandomUserName();
|
||||||
let email = `${username}@example.com`;
|
let email = `${username}@example.com`;
|
||||||
let password = 'password';
|
let password = 'password';
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
/* eslint-disable camelcase */
|
|
||||||
|
|
||||||
import {
|
|
||||||
generateUser,
|
|
||||||
requester,
|
|
||||||
translate as t,
|
|
||||||
} from '../../../../../helpers/api-integration/v3';
|
|
||||||
import { v4 as generateUUID } from 'uuid';
|
|
||||||
|
|
||||||
describe('POST /user/auth/pusher', () => {
|
|
||||||
let user;
|
|
||||||
let endpoint = '/user/auth/pusher';
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('requires authentication', async () => {
|
|
||||||
let api = requester();
|
|
||||||
|
|
||||||
await expect(api.post(endpoint)).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('missingAuthHeaders'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if req.body.socket_id is missing', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('invalidReqParams'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if req.body.channel_name is missing', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('invalidReqParams'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if req.body.channel_name is badly formatted', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: '123',
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: 'Invalid Pusher channel type.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if an invalid channel type is passed', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: 'invalid-group-123',
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: 'Invalid Pusher channel type.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if an invalid resource type is passed', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: 'presence-user-123',
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: 'Invalid Pusher resource type.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if an invalid resource id is passed', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: 'presence-group-123',
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: 'Invalid Pusher resource id, must be a UUID.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if the passed resource id doesn\'t match the user\'s party', async () => {
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
channel_name: `presence-group-${generateUUID()}`,
|
|
||||||
socket_id: '123',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 404,
|
|
||||||
error: 'NotFound',
|
|
||||||
message: 'Resource id must be the user\'s party.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -39,7 +39,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('registers a new user', async () => {
|
it('registers a new user', async () => {
|
||||||
let response = await api.post(endpoint, {
|
const response = await api.post(endpoint, {
|
||||||
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
||||||
network,
|
network,
|
||||||
});
|
});
|
||||||
@@ -47,7 +47,10 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.apiToken).to.exist;
|
expect(response.apiToken).to.exist;
|
||||||
expect(response.id).to.exist;
|
expect(response.id).to.exist;
|
||||||
expect(response.newUser).to.be.true;
|
expect(response.newUser).to.be.true;
|
||||||
|
expect(response.username).to.exist;
|
||||||
|
|
||||||
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
|
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
|
||||||
|
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('logs an existing user in', async () => {
|
it('logs an existing user in', async () => {
|
||||||
@@ -77,7 +80,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.newUser).to.be.false;
|
expect(response.newUser).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enrolls a new user in an A/B test', async () => {
|
xit('enrolls a new user in an A/B test', async () => {
|
||||||
await api.post(endpoint, {
|
await api.post(endpoint, {
|
||||||
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
||||||
network,
|
network,
|
||||||
@@ -133,7 +136,7 @@ describe('POST /user/auth/social', () => {
|
|||||||
expect(response.newUser).to.be.false;
|
expect(response.newUser).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enrolls a new user in an A/B test', async () => {
|
xit('enrolls a new user in an A/B test', async () => {
|
||||||
await api.post(endpoint, {
|
await api.post(endpoint, {
|
||||||
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
authResponse: {access_token: randomAccessToken}, // eslint-disable-line camelcase
|
||||||
network,
|
network,
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ const ENDPOINT = '/user/auth/update-username';
|
|||||||
|
|
||||||
describe('PUT /user/auth/update-username', async () => {
|
describe('PUT /user/auth/update-username', async () => {
|
||||||
let user;
|
let user;
|
||||||
let newUsername = 'new-username';
|
let password = 'password'; // from habitrpg/test/helpers/api-integration/v4/object-generators.js
|
||||||
let password = 'password'; // from habitrpg/test/helpers/api-integration/v3/object-generators.js
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
user = await generateUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successfully changes username', async () => {
|
it('successfully changes username with password', async () => {
|
||||||
|
let newUsername = 'new-username';
|
||||||
let response = await user.put(ENDPOINT, {
|
let response = await user.put(ENDPOINT, {
|
||||||
username: newUsername,
|
username: newUsername,
|
||||||
password,
|
password,
|
||||||
@@ -29,6 +29,38 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
expect(user.auth.local.username).to.eql(newUsername);
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('successfully changes username without password', async () => {
|
||||||
|
let newUsername = 'new-username-nopw';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successfully changes username containing number and underscore', async () => {
|
||||||
|
let newUsername = 'new_username9';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.username).to.eql(newUsername);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets verifiedUsername when changing username', async () => {
|
||||||
|
user.flags.verifiedUsername = false;
|
||||||
|
await user.sync();
|
||||||
|
let newUsername = 'new-username-verify';
|
||||||
|
let response = await user.put(ENDPOINT, {
|
||||||
|
username: newUsername,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ username: newUsername });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.flags.verifiedUsername).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||||
let myNewUsername = 'my-new-username';
|
let myNewUsername = 'my-new-username';
|
||||||
let textPassword = 'mySecretPassword';
|
let textPassword = 'mySecretPassword';
|
||||||
@@ -80,6 +112,7 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors if password is wrong', async () => {
|
it('errors if password is wrong', async () => {
|
||||||
|
let newUsername = 'new-username';
|
||||||
await expect(user.put(ENDPOINT, {
|
await expect(user.put(ENDPOINT, {
|
||||||
username: newUsername,
|
username: newUsername,
|
||||||
password: 'wrong-password',
|
password: 'wrong-password',
|
||||||
@@ -90,19 +123,6 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents social-only user from changing username', async () => {
|
|
||||||
let socialUser = await generateUser({ 'auth.local': { ok: true } });
|
|
||||||
|
|
||||||
await expect(socialUser.put(ENDPOINT, {
|
|
||||||
username: newUsername,
|
|
||||||
password,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('userHasNoLocalRegistration'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if new username is not provided', async () => {
|
it('errors if new username is not provided', async () => {
|
||||||
await expect(user.put(ENDPOINT, {
|
await expect(user.put(ENDPOINT, {
|
||||||
password,
|
password,
|
||||||
@@ -112,5 +132,93 @@ describe('PUT /user/auth/update-username', async () => {
|
|||||||
message: t('invalidReqParams'),
|
message: t('invalidReqParams'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors if new username is a slur', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username contains a slur', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'something_TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username is not allowed', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'support',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueForbidden'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username is not allowed regardless of casing', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'SUppORT',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueForbidden'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if username has incorrect length', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'thisisaverylongusernameover20characters',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueLength'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if new username contains invalid characters', async () => {
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'Eichhörnchen',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: 'test.name',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
await expect(user.put(ENDPOINT, {
|
||||||
|
username: '🤬',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('usernameIssueInvalidCharacters'),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,4 +53,15 @@ describe('POST /user/buy-gear/:key', () => {
|
|||||||
message: 'You need to purchase a lower level gear before this one.',
|
message: 'You need to purchase a lower level gear before this one.',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error if tries to buy gear from a different class', async () => {
|
||||||
|
let key = 'armor_rogue_1';
|
||||||
|
|
||||||
|
return expect(user.post(`/user/buy-gear/${key}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: 'You can\'t buy this item.',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ describe('POST /user/allocate', () => {
|
|||||||
let user;
|
let user;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
user = await generateUser({
|
||||||
|
'stats.lvl': 10,
|
||||||
|
'flags.classSelected': true,
|
||||||
|
'preferences.disableClasses': false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// More tests in common code unit tests
|
// More tests in common code unit tests
|
||||||
@@ -31,6 +35,16 @@ describe('POST /user/allocate', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error if the user hasn\'t selected class', async () => {
|
||||||
|
await user.update({'flags.classSelected': false});
|
||||||
|
await expect(user.post('/user/allocate'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('classNotSelected'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('allocates attribute points', async () => {
|
it('allocates attribute points', async () => {
|
||||||
await user.update({'stats.points': 1});
|
await user.update({'stats.points': 1});
|
||||||
let res = await user.post('/user/allocate?stat=con');
|
let res = await user.post('/user/allocate?stat=con');
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ describe('POST /user/allocate-bulk', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser();
|
user = await generateUser({
|
||||||
|
'stats.lvl': 10,
|
||||||
|
'flags.classSelected': true,
|
||||||
|
'preferences.disableClasses': false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// More tests in common code unit tests
|
// More tests in common code unit tests
|
||||||
@@ -27,6 +31,16 @@ describe('POST /user/allocate-bulk', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns an error if user has not selected class', async () => {
|
||||||
|
await user.update({'flags.classSelected': false});
|
||||||
|
await expect(user.post('/user/allocate-bulk', statsUpdate))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('classNotSelected'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('allocates attribute points', async () => {
|
it('allocates attribute points', async () => {
|
||||||
await user.update({'stats.points': 3});
|
await user.update({'stats.points': 3});
|
||||||
|
|
||||||
|
|||||||
62
test/api/v4/coupon/POST-coupons_enter_code.test.js
Normal file
62
test/api/v4/coupon/POST-coupons_enter_code.test.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
resetHabiticaDB,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('POST /coupons/enter/:code', () => {
|
||||||
|
let user;
|
||||||
|
let sudoUser;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await resetHabiticaDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
sudoUser = await generateUser({
|
||||||
|
'contributor.sudo': true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if code is missing', async () => {
|
||||||
|
await expect(user.post('/coupons/enter')).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: 'Not found.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if code is invalid', async () => {
|
||||||
|
await expect(user.post('/coupons/enter/notValid')).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidCoupon'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if coupon has been used', async () => {
|
||||||
|
let [coupon] = await sudoUser.post('/coupons/generate/wondercon?count=1');
|
||||||
|
await user.post(`/coupons/enter/${coupon._id}`); // use coupon
|
||||||
|
|
||||||
|
await expect(user.post(`/coupons/enter/${coupon._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('couponUsed'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply the coupon to the user', async () => {
|
||||||
|
let [coupon] = await sudoUser.post('/coupons/generate/wondercon?count=1');
|
||||||
|
let userRes = await user.post(`/coupons/enter/${coupon._id}`);
|
||||||
|
expect(userRes._id).to.equal(user._id);
|
||||||
|
expect(userRes.items.gear.owned.eyewear_special_wondercon_red).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.eyewear_special_wondercon_black).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.back_special_wondercon_black).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.back_special_wondercon_red).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.body_special_wondercon_red).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.body_special_wondercon_black).to.be.true;
|
||||||
|
expect(userRes.items.gear.owned.body_special_wondercon_gold).to.be.true;
|
||||||
|
expect(userRes.extra).to.eql({signupEvent: 'wondercon'});
|
||||||
|
});
|
||||||
|
});
|
||||||
30
test/api/v4/inbox/DELETE-inbox_clear.test.js
Normal file
30
test/api/v4/inbox/DELETE-inbox_clear.test.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('DELETE /inbox/clear', () => {
|
||||||
|
it('removes all inbox messages for the user', async () => {
|
||||||
|
const [user, otherUser] = await Promise.all([generateUser(), generateUser()]);
|
||||||
|
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: otherUser.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'third',
|
||||||
|
});
|
||||||
|
|
||||||
|
let messages = await user.get('/inbox/messages');
|
||||||
|
expect(messages.length).to.equal(3);
|
||||||
|
|
||||||
|
await user.del('/inbox/clear/');
|
||||||
|
|
||||||
|
messages = await user.get('/inbox/messages');
|
||||||
|
expect(messages.length).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
62
test/api/v4/inbox/DELETE-inbox_messages_messageId.test.js
Normal file
62
test/api/v4/inbox/DELETE-inbox_messages_messageId.test.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
describe('DELETE /inbox/messages/:messageId', () => {
|
||||||
|
let user;
|
||||||
|
let otherUser;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[user, otherUser] = await Promise.all([generateUser(), generateUser()]);
|
||||||
|
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: otherUser.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'third',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if the messageId parameter is not an UUID', async () => {
|
||||||
|
await expect(user.del('/inbox/messages/123'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'Invalid request parameters.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if the message does not exist', async () => {
|
||||||
|
await expect(user.del(`/inbox/messages/${generateUUID()}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('messageGroupChatNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletes one message', async () => {
|
||||||
|
const messages = await user.get('/inbox/messages');
|
||||||
|
|
||||||
|
expect(messages.length).to.equal(3);
|
||||||
|
|
||||||
|
expect(messages[0].text).to.equal('third');
|
||||||
|
expect(messages[1].text).to.equal('second');
|
||||||
|
expect(messages[2].text).to.equal('first');
|
||||||
|
|
||||||
|
await user.del(`/inbox/messages/${messages[1]._id}`);
|
||||||
|
const updatedMessages = await user.get('/inbox/messages');
|
||||||
|
expect(updatedMessages.length).to.equal(2);
|
||||||
|
|
||||||
|
expect(updatedMessages[0].text).to.equal('third');
|
||||||
|
expect(updatedMessages[1].text).to.equal('first');
|
||||||
|
});
|
||||||
|
});
|
||||||
58
test/api/v4/user/GET-user.test.js
Normal file
58
test/api/v4/user/GET-user.test.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
import common from '../../../../website/common';
|
||||||
|
|
||||||
|
describe('GET /user', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the authenticated user with computed stats', async () => {
|
||||||
|
let returnedUser = await user.get('/user');
|
||||||
|
expect(returnedUser._id).to.equal(user._id);
|
||||||
|
|
||||||
|
expect(returnedUser.stats.maxMP).to.exist;
|
||||||
|
expect(returnedUser.stats.maxHealth).to.equal(common.maxHealth);
|
||||||
|
expect(returnedUser.stats.toNextLevel).to.equal(common.tnl(returnedUser.stats.lvl));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not return private paths (and apiToken)', async () => {
|
||||||
|
let returnedUser = await user.get('/user');
|
||||||
|
|
||||||
|
expect(returnedUser.auth.local.hashed_password).to.not.exist;
|
||||||
|
expect(returnedUser.auth.local.passwordHashMethod).to.not.exist;
|
||||||
|
expect(returnedUser.auth.local.salt).to.not.exist;
|
||||||
|
expect(returnedUser.apiToken).to.not.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns only user properties requested', async () => {
|
||||||
|
let returnedUser = await user.get('/user?userFields=achievements,items.mounts');
|
||||||
|
|
||||||
|
expect(returnedUser._id).to.equal(user._id);
|
||||||
|
expect(returnedUser.achievements).to.exist;
|
||||||
|
expect(returnedUser.items.mounts).to.exist;
|
||||||
|
// Notifications are always returned
|
||||||
|
expect(returnedUser.notifications).to.exist;
|
||||||
|
expect(returnedUser.stats).to.not.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not return new inbox messages', async () => {
|
||||||
|
const otherUser = await generateUser();
|
||||||
|
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
let returnedUser = await user.get('/user');
|
||||||
|
|
||||||
|
expect(returnedUser._id).to.equal(user._id);
|
||||||
|
expect(returnedUser.inbox.messages).to.be.undefined;
|
||||||
|
});
|
||||||
|
});
|
||||||
324
test/api/v4/user/POST-user_class_cast_spellId.test.js
Normal file
324
test/api/v4/user/POST-user_class_cast_spellId.test.js
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
createAndPopulateGroup,
|
||||||
|
generateGroup,
|
||||||
|
generateChallenge,
|
||||||
|
sleep,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
import apiError from '../../../../website/server/libs/apiError';
|
||||||
|
|
||||||
|
describe('POST /user/class/cast/:spellId', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if spell does not exist', async () => {
|
||||||
|
await user.update({'stats.class': 'rogue'});
|
||||||
|
let spellId = 'invalidSpell';
|
||||||
|
await expect(user.post(`/user/class/cast/${spellId}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: apiError('spellNotFound', {spellId}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if spell does not exist in user\'s class', async () => {
|
||||||
|
let spellId = 'pickPocket';
|
||||||
|
await expect(user.post(`/user/class/cast/${spellId}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: apiError('spellNotFound', {spellId}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if spell.mana > user.mana', async () => {
|
||||||
|
await user.update({'stats.class': 'rogue'});
|
||||||
|
await expect(user.post('/user/class/cast/backStab'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('notEnoughMana'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if spell.value > user.gold', async () => {
|
||||||
|
await expect(user.post('/user/class/cast/birthday'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('messageNotEnoughGold'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if spell.lvl > user.level', async () => {
|
||||||
|
await user.update({'stats.mp': 200, 'stats.class': 'wizard'});
|
||||||
|
await expect(user.post('/user/class/cast/earth'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('spellLevelTooHigh', {level: 13}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if user doesn\'t own the spell', async () => {
|
||||||
|
await expect(user.post('/user/class/cast/snowball'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('spellNotOwned'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if targetId is not an UUID', async () => {
|
||||||
|
await expect(user.post('/user/class/cast/spellId?targetId=notAnUUID'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if targetId is required but missing', async () => {
|
||||||
|
await user.update({'stats.class': 'rogue', 'stats.lvl': 11});
|
||||||
|
await expect(user.post('/user/class/cast/pickPocket'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('targetIdUUID'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if targeted task doesn\'t exist', async () => {
|
||||||
|
await user.update({'stats.class': 'rogue', 'stats.lvl': 11});
|
||||||
|
await expect(user.post(`/user/class/cast/pickPocket?targetId=${generateUUID()}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if a challenge task was targeted', async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup();
|
||||||
|
let challenge = await generateChallenge(groupLeader, group);
|
||||||
|
await groupLeader.post(`/challenges/${challenge._id}/join`);
|
||||||
|
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [
|
||||||
|
{type: 'habit', text: 'task text'},
|
||||||
|
]);
|
||||||
|
await groupLeader.update({'stats.class': 'rogue', 'stats.lvl': 11});
|
||||||
|
await sleep(0.5);
|
||||||
|
await groupLeader.sync();
|
||||||
|
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${groupLeader.tasksOrder.habits[0]}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('challengeTasksNoCast'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if a group task was targeted', async () => {
|
||||||
|
let {group, groupLeader} = await createAndPopulateGroup();
|
||||||
|
|
||||||
|
let groupTask = await groupLeader.post(`/tasks/group/${group._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await groupLeader.post(`/tasks/${groupTask._id}/assign/${groupLeader._id}`);
|
||||||
|
let memberTasks = await groupLeader.get('/tasks/user');
|
||||||
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === group._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
await groupLeader.update({'stats.class': 'rogue', 'stats.lvl': 11});
|
||||||
|
await sleep(0.5);
|
||||||
|
await groupLeader.sync();
|
||||||
|
|
||||||
|
await expect(groupLeader.post(`/user/class/cast/pickPocket?targetId=${syncedGroupTask._id}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('groupTasksNoCast'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if targeted party member doesn\'t exist', async () => {
|
||||||
|
let {groupLeader} = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
members: 1,
|
||||||
|
});
|
||||||
|
await groupLeader.update({'items.special.snowball': 3});
|
||||||
|
|
||||||
|
let target = generateUUID();
|
||||||
|
await expect(groupLeader.post(`/user/class/cast/snowball?targetId=${target}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userWithIDNotFound', {userId: target}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if party does not exists', async () => {
|
||||||
|
await user.update({'items.special.snowball': 3});
|
||||||
|
|
||||||
|
await expect(user.post(`/user/class/cast/snowball?targetId=${generateUUID()}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('partyNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('send message in party chat if party && !spell.silent', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
members: 1,
|
||||||
|
});
|
||||||
|
await groupLeader.update({'stats.mp': 200, 'stats.class': 'wizard', 'stats.lvl': 13});
|
||||||
|
|
||||||
|
await groupLeader.post('/user/class/cast/earth');
|
||||||
|
await sleep(1);
|
||||||
|
const groupMessages = await groupLeader.get(`/groups/${group._id}/chat`);
|
||||||
|
|
||||||
|
expect(groupMessages[0]).to.exist;
|
||||||
|
expect(groupMessages[0].uuid).to.equal('system');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ethereal Surge does not recover mp of other mages', async () => {
|
||||||
|
let group = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
members: 4,
|
||||||
|
});
|
||||||
|
|
||||||
|
let promises = [];
|
||||||
|
promises.push(group.groupLeader.update({'stats.mp': 200, 'stats.class': 'wizard', 'stats.lvl': 20}));
|
||||||
|
promises.push(group.members[0].update({'stats.mp': 0, 'stats.class': 'warrior', 'stats.lvl': 20}));
|
||||||
|
promises.push(group.members[1].update({'stats.mp': 0, 'stats.class': 'wizard', 'stats.lvl': 20}));
|
||||||
|
promises.push(group.members[2].update({'stats.mp': 0, 'stats.class': 'rogue', 'stats.lvl': 20}));
|
||||||
|
promises.push(group.members[3].update({'stats.mp': 0, 'stats.class': 'healer', 'stats.lvl': 20}));
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
await group.groupLeader.post('/user/class/cast/mpheal');
|
||||||
|
|
||||||
|
promises = [];
|
||||||
|
promises.push(group.members[0].sync());
|
||||||
|
promises.push(group.members[1].sync());
|
||||||
|
promises.push(group.members[2].sync());
|
||||||
|
promises.push(group.members[3].sync());
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
expect(group.members[0].stats.mp).to.be.greaterThan(0); // warrior
|
||||||
|
expect(group.members[1].stats.mp).to.equal(0); // wizard
|
||||||
|
expect(group.members[2].stats.mp).to.be.greaterThan(0); // rogue
|
||||||
|
expect(group.members[3].stats.mp).to.be.greaterThan(0); // healer
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cast bulk', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
members: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
await groupLeader.update({'stats.mp': 200, 'stats.class': 'wizard', 'stats.lvl': 13});
|
||||||
|
await groupLeader.post('/user/class/cast/earth', {quantity: 2});
|
||||||
|
|
||||||
|
await sleep(1);
|
||||||
|
group = await groupLeader.get(`/groups/${group._id}`);
|
||||||
|
|
||||||
|
expect(group.chat[0]).to.exist;
|
||||||
|
expect(group.chat[0].uuid).to.equal('system');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('searing brightness does not affect challenge or group tasks', async () => {
|
||||||
|
let guild = await generateGroup(user);
|
||||||
|
let challenge = await generateChallenge(user, guild);
|
||||||
|
await user.post(`/challenges/${challenge._id}/join`);
|
||||||
|
await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test challenge habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
|
||||||
|
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await user.update({'stats.class': 'healer', 'stats.mp': 200, 'stats.lvl': 15});
|
||||||
|
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||||
|
|
||||||
|
await user.post('/user/class/cast/brightness');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
let memberTasks = await user.get('/tasks/user');
|
||||||
|
|
||||||
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === guild._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
let userChallengeTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.challenge.id === challenge._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(userChallengeTask).to.exist;
|
||||||
|
expect(syncedGroupTask).to.exist;
|
||||||
|
expect(userChallengeTask.value).to.equal(0);
|
||||||
|
expect(syncedGroupTask.value).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increases both user\'s achievement values', async () => {
|
||||||
|
let party = await createAndPopulateGroup({
|
||||||
|
members: 1,
|
||||||
|
});
|
||||||
|
let leader = party.groupLeader;
|
||||||
|
let recipient = party.members[0];
|
||||||
|
await leader.update({'stats.gp': 10});
|
||||||
|
await leader.post(`/user/class/cast/birthday?targetId=${recipient._id}`);
|
||||||
|
await leader.sync();
|
||||||
|
await recipient.sync();
|
||||||
|
expect(leader.achievements.birthday).to.equal(1);
|
||||||
|
expect(recipient.achievements.birthday).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only increases user\'s achievement one if target == caster', async () => {
|
||||||
|
await user.update({'stats.gp': 10});
|
||||||
|
await user.post(`/user/class/cast/birthday?targetId=${user._id}`);
|
||||||
|
await user.sync();
|
||||||
|
expect(user.achievements.birthday).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes correct target to spell when targetType === \'task\'', async () => {
|
||||||
|
await user.update({'stats.class': 'wizard', 'stats.lvl': 11});
|
||||||
|
|
||||||
|
let task = await user.post('/tasks/user', {
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = await user.post(`/user/class/cast/fireball?targetId=${task._id}`);
|
||||||
|
|
||||||
|
expect(result.task._id).to.equal(task._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes correct target to spell when targetType === \'self\'', async () => {
|
||||||
|
await user.update({'stats.class': 'wizard', 'stats.lvl': 14, 'stats.mp': 50});
|
||||||
|
|
||||||
|
let result = await user.post('/user/class/cast/frost');
|
||||||
|
|
||||||
|
expect(result.user.stats.mp).to.equal(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// TODO find a way to have sinon working in integration tests
|
||||||
|
// it doesn't work when tests are running separately from server
|
||||||
|
it('passes correct target to spell when targetType === \'tasks\'');
|
||||||
|
it('passes correct target to spell when targetType === \'party\'');
|
||||||
|
it('passes correct target to spell when targetType === \'user\'');
|
||||||
|
it('passes correct target to spell when targetType === \'party\' and user is not in a party');
|
||||||
|
it('passes correct target to spell when targetType === \'user\' and user is not in a party');
|
||||||
|
});
|
||||||
60
test/api/v4/user/POST-user_rebirth.test.js
Normal file
60
test/api/v4/user/POST-user_rebirth.test.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateDaily,
|
||||||
|
generateReward,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('POST /user/rebirth', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when user balance is too low', async () => {
|
||||||
|
await expect(user.post('/user/rebirth'))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('notEnoughGems'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('resets user\'s tasks', async () => {
|
||||||
|
await user.update({
|
||||||
|
balance: 1.5,
|
||||||
|
});
|
||||||
|
|
||||||
|
let daily = await generateDaily({
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'daily',
|
||||||
|
value: 1,
|
||||||
|
streak: 1,
|
||||||
|
userId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let reward = await generateReward({
|
||||||
|
text: 'test reward',
|
||||||
|
type: 'reward',
|
||||||
|
value: 1,
|
||||||
|
userId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await user.post('/user/rebirth');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.notifications.length).to.equal(1);
|
||||||
|
expect(user.notifications[0].type).to.equal('REBIRTH_ACHIEVEMENT');
|
||||||
|
|
||||||
|
let updatedDaily = await user.get(`/tasks/${daily._id}`);
|
||||||
|
let updatedReward = await user.get(`/tasks/${reward._id}`);
|
||||||
|
|
||||||
|
expect(response.message).to.equal(t('rebirthComplete'));
|
||||||
|
expect(updatedDaily.streak).to.equal(0);
|
||||||
|
expect(updatedDaily.value).to.equal(0);
|
||||||
|
expect(updatedReward.value).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
54
test/api/v4/user/POST-user_reroll.test.js
Normal file
54
test/api/v4/user/POST-user_reroll.test.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateDaily,
|
||||||
|
generateReward,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('POST /user/reroll', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when user balance is too low', async () => {
|
||||||
|
await expect(user.post('/user/reroll'))
|
||||||
|
.to.eventually.be.rejected.and.to.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('notEnoughGems'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('resets user\'s tasks', async () => {
|
||||||
|
await user.update({
|
||||||
|
balance: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
let daily = await generateDaily({
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'daily',
|
||||||
|
userId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let reward = await generateReward({
|
||||||
|
text: 'test reward',
|
||||||
|
type: 'reward',
|
||||||
|
value: 1,
|
||||||
|
userId: user._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await user.post('/user/reroll');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
let updatedDaily = await user.get(`/tasks/${daily._id}`);
|
||||||
|
let updatedReward = await user.get(`/tasks/${reward._id}`);
|
||||||
|
|
||||||
|
expect(response.message).to.equal(t('fortifyComplete'));
|
||||||
|
expect(updatedDaily.value).to.equal(0);
|
||||||
|
expect(updatedReward.value).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
121
test/api/v4/user/POST-user_reset.test.js
Normal file
121
test/api/v4/user/POST-user_reset.test.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
generateChallenge,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
|
describe('POST /user/reset', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('resets user\'s habits', async () => {
|
||||||
|
let task = await user.post('/tasks/user', {
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post('/user/reset');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
await expect(user.get(`/tasks/${task._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.habits).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resets user\'s dailys', async () => {
|
||||||
|
let task = await user.post('/tasks/user', {
|
||||||
|
text: 'test daily',
|
||||||
|
type: 'daily',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post('/user/reset');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
await expect(user.get(`/tasks/${task._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.dailys).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resets user\'s todos', async () => {
|
||||||
|
let task = await user.post('/tasks/user', {
|
||||||
|
text: 'test todo',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post('/user/reset');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
await expect(user.get(`/tasks/${task._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.todos).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resets user\'s rewards', async () => {
|
||||||
|
let task = await user.post('/tasks/user', {
|
||||||
|
text: 'test reward',
|
||||||
|
type: 'reward',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.post('/user/reset');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
await expect(user.get(`/tasks/${task._id}`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('taskNotFound'),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.rewards).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not delete challenge or group tasks', async () => {
|
||||||
|
let guild = await generateGroup(user);
|
||||||
|
let challenge = await generateChallenge(user, guild);
|
||||||
|
await user.post(`/challenges/${challenge._id}/join`);
|
||||||
|
await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test challenge habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
|
||||||
|
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||||
|
text: 'todo group',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||||
|
|
||||||
|
await user.post('/user/reset');
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
let memberTasks = await user.get('/tasks/user');
|
||||||
|
|
||||||
|
let syncedGroupTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.group.id === guild._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
let userChallengeTask = find(memberTasks, function findAssignedTask (memberTask) {
|
||||||
|
return memberTask.challenge.id === challenge._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(userChallengeTask).to.exist;
|
||||||
|
expect(syncedGroupTask).to.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
256
test/api/v4/user/PUT-user.test.js
Normal file
256
test/api/v4/user/PUT-user.test.js
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
import { each, get } from 'lodash';
|
||||||
|
|
||||||
|
describe('PUT /user', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Allowed Operations', () => {
|
||||||
|
it('updates the user', async () => {
|
||||||
|
await user.put('/user', {
|
||||||
|
'profile.name': 'Frodo',
|
||||||
|
'preferences.costume': true,
|
||||||
|
'stats.hp': 14,
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.profile.name).to.eql('Frodo');
|
||||||
|
expect(user.preferences.costume).to.eql(true);
|
||||||
|
expect(user.stats.hp).to.eql(14);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tags must be an array', async () => {
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
tags: {
|
||||||
|
tag: true,
|
||||||
|
},
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'mustBeArray',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('update tags', async () => {
|
||||||
|
let userTags = user.tags;
|
||||||
|
|
||||||
|
await user.put('/user', {
|
||||||
|
tags: [...user.tags, {
|
||||||
|
name: 'new tag',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.tags.length).to.be.eql(userTags.length + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('profile.name cannot be an empty string or null', async () => {
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': ' ', // string should be trimmed
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': '',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'profile.name': null,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Top Level Protected Operations', () => {
|
||||||
|
let protectedOperations = {
|
||||||
|
'gem balance': {balance: 100},
|
||||||
|
auth: {'auth.blocked': true, 'auth.timestamps.created': new Date()},
|
||||||
|
contributor: {'contributor.level': 9, 'contributor.admin': true, 'contributor.text': 'some text'},
|
||||||
|
backer: {'backer.tier': 10, 'backer.npc': 'Bilbo'},
|
||||||
|
subscriptions: {'purchased.plan.extraMonths': 500, 'purchased.plan.consecutive.trinkets': 1000},
|
||||||
|
'customization gem purchases': {'purchased.background.tavern': true, 'purchased.skin.bear': true},
|
||||||
|
notifications: [{type: 123}],
|
||||||
|
webhooks: {webhooks: [{url: 'https://foobar.com'}]},
|
||||||
|
};
|
||||||
|
|
||||||
|
each(protectedOperations, (data, testName) => {
|
||||||
|
it(`does not allow updating ${testName}`, async () => {
|
||||||
|
let errorText = t('messageUserOperationProtected', { operation: Object.keys(data)[0] });
|
||||||
|
|
||||||
|
await expect(user.put('/user', data)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: errorText,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Sub-Level Protected Operations', () => {
|
||||||
|
let protectedOperations = {
|
||||||
|
'class stat': {'stats.class': 'wizard'},
|
||||||
|
'flags unless whitelisted': {'flags.dropsEnabled': true},
|
||||||
|
webhooks: {'preferences.webhooks': [1, 2, 3]},
|
||||||
|
sleep: {'preferences.sleep': true},
|
||||||
|
'disable classes': {'preferences.disableClasses': true},
|
||||||
|
};
|
||||||
|
|
||||||
|
each(protectedOperations, (data, testName) => {
|
||||||
|
it(`does not allow updating ${testName}`, async () => {
|
||||||
|
let errorText = t('messageUserOperationProtected', { operation: Object.keys(data)[0] });
|
||||||
|
|
||||||
|
await expect(user.put('/user', data)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: errorText,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Default Appearance Preferences', () => {
|
||||||
|
let testCases = {
|
||||||
|
shirt: 'yellow',
|
||||||
|
skin: 'ddc994',
|
||||||
|
'hair.color': 'blond',
|
||||||
|
'hair.bangs': 2,
|
||||||
|
'hair.base': 1,
|
||||||
|
'hair.flower': 4,
|
||||||
|
size: 'broad',
|
||||||
|
};
|
||||||
|
|
||||||
|
each(testCases, (item, type) => {
|
||||||
|
const update = {};
|
||||||
|
update[`preferences.${type}`] = item;
|
||||||
|
|
||||||
|
it(`updates user with ${type} that is a default`, async () => {
|
||||||
|
let dbUpdate = {};
|
||||||
|
dbUpdate[`purchased.${type}.${item}`] = true;
|
||||||
|
await user.update(dbUpdate);
|
||||||
|
|
||||||
|
// Sanity checks to make sure user is not already equipped with item
|
||||||
|
expect(get(user.preferences, type)).to.not.eql(item);
|
||||||
|
|
||||||
|
let updatedUser = await user.put('/user', update);
|
||||||
|
|
||||||
|
expect(get(updatedUser.preferences, type)).to.eql(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if user tries to update body size with invalid type', async () => {
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'preferences.size': 'round',
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('mustPurchaseToSet', { val: 'round', key: 'preferences.size' }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set beard to default', async () => {
|
||||||
|
await user.update({
|
||||||
|
'purchased.hair.beard': 3,
|
||||||
|
'preferences.hair.beard': 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedUser = await user.put('/user', {
|
||||||
|
'preferences.hair.beard': 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(updatedUser.preferences.hair.beard).to.eql(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set mustache to default', async () => {
|
||||||
|
await user.update({
|
||||||
|
'purchased.hair.mustache': 2,
|
||||||
|
'preferences.hair.mustache': 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedUser = await user.put('/user', {
|
||||||
|
'preferences.hair.mustache': 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(updatedUser.preferences.hair.mustache).to.eql(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Purchasable Appearance Preferences', () => {
|
||||||
|
let testCases = {
|
||||||
|
background: 'volcano',
|
||||||
|
shirt: 'convict',
|
||||||
|
skin: 'cactus',
|
||||||
|
'hair.base': 7,
|
||||||
|
'hair.beard': 2,
|
||||||
|
'hair.color': 'rainbow',
|
||||||
|
'hair.mustache': 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
each(testCases, (item, type) => {
|
||||||
|
const update = {};
|
||||||
|
update[`preferences.${type}`] = item;
|
||||||
|
|
||||||
|
it(`returns an error if user tries to update ${type} with ${type} the user does not own`, async () => {
|
||||||
|
await expect(user.put('/user', update)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('mustPurchaseToSet', {val: item, key: `preferences.${type}`}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`updates user with ${type} user does own`, async () => {
|
||||||
|
let dbUpdate = {};
|
||||||
|
dbUpdate[`purchased.${type}.${item}`] = true;
|
||||||
|
await user.update(dbUpdate);
|
||||||
|
|
||||||
|
// Sanity check to make sure user is not already equipped with item
|
||||||
|
expect(get(user.preferences, type)).to.not.eql(item);
|
||||||
|
|
||||||
|
let updatedUser = await user.put('/user', update);
|
||||||
|
|
||||||
|
expect(get(updatedUser.preferences, type)).to.eql(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Improvement Categories', () => {
|
||||||
|
it('sets valid categories', async () => {
|
||||||
|
await user.put('/user', {
|
||||||
|
'preferences.improvementCategories': ['work', 'school'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(user.preferences.improvementCategories).to.eql(['work', 'school']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discards invalid categories', async () => {
|
||||||
|
await expect(user.put('/user', {
|
||||||
|
'preferences.improvementCategories': ['work', 'procrastination', 'school'],
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
739
test/api/v4/user/auth/POST-register_local.test.js
Normal file
739
test/api/v4/user/auth/POST-register_local.test.js
Normal file
@@ -0,0 +1,739 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
requester,
|
||||||
|
translate as t,
|
||||||
|
createAndPopulateGroup,
|
||||||
|
getProperty,
|
||||||
|
} from '../../../../helpers/api-integration/v4';
|
||||||
|
import { ApiUser } from '../../../../helpers/api-integration/api-classes';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { each } from 'lodash';
|
||||||
|
import { encrypt } from '../../../../../website/server/libs/encryption';
|
||||||
|
|
||||||
|
function generateRandomUserName () {
|
||||||
|
return (Date.now() + uuid()).substring(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('POST /user/auth/local/register', () => {
|
||||||
|
context('username and email are free', () => {
|
||||||
|
let api;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
api = requester();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registers a new user', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user._id).to.exist;
|
||||||
|
expect(user.apiToken).to.exist;
|
||||||
|
expect(user.auth.local.username).to.eql(username);
|
||||||
|
expect(user.profile.name).to.eql(username);
|
||||||
|
expect(user.newUser).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('remove spaces from username', async () => {
|
||||||
|
// TODO can probably delete this test now
|
||||||
|
let username = ' usernamewithspaces ';
|
||||||
|
let email = 'test@example.com';
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.auth.local.username).to.eql(username.trim());
|
||||||
|
expect(user.profile.name).to.eql(username.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
context('validates username', () => {
|
||||||
|
const email = 'test@example.com';
|
||||||
|
const password = 'password';
|
||||||
|
|
||||||
|
it('requires to username to be less than 20', async () => {
|
||||||
|
const username = (Date.now() + uuid()).substring(0, 21);
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'Invalid request parameters.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects chracters not in [-_a-zA-Z0-9]', async () => {
|
||||||
|
const username = 'a-zA_Z09*';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'Invalid request parameters.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows only [-_a-zA-Z0-9] characters', async () => {
|
||||||
|
const username = 'a-zA_Z09';
|
||||||
|
|
||||||
|
const user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.auth.local.username).to.eql(username);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('provides default tags and tasks', async () => {
|
||||||
|
it('for a generic API consumer', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests = new ApiUser(user);
|
||||||
|
|
||||||
|
let habits = await requests.get('/tasks/user?type=habits');
|
||||||
|
let dailys = await requests.get('/tasks/user?type=dailys');
|
||||||
|
let todos = await requests.get('/tasks/user?type=todos');
|
||||||
|
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||||
|
let tags = await requests.get('/tags');
|
||||||
|
|
||||||
|
expect(habits).to.have.a.lengthOf(0);
|
||||||
|
expect(dailys).to.have.a.lengthOf(0);
|
||||||
|
expect(todos).to.have.a.lengthOf(1);
|
||||||
|
expect(rewards).to.have.a.lengthOf(0);
|
||||||
|
|
||||||
|
expect(tags).to.have.a.lengthOf(7);
|
||||||
|
expect(tags[0].name).to.eql(t('defaultTag1'));
|
||||||
|
expect(tags[1].name).to.eql(t('defaultTag2'));
|
||||||
|
expect(tags[2].name).to.eql(t('defaultTag3'));
|
||||||
|
expect(tags[3].name).to.eql(t('defaultTag4'));
|
||||||
|
expect(tags[4].name).to.eql(t('defaultTag5'));
|
||||||
|
expect(tags[5].name).to.eql(t('defaultTag6'));
|
||||||
|
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||||
|
});
|
||||||
|
|
||||||
|
xit('for Web', async () => {
|
||||||
|
api = requester(
|
||||||
|
null,
|
||||||
|
{'x-client': 'habitica-web'},
|
||||||
|
);
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests = new ApiUser(user);
|
||||||
|
|
||||||
|
let habits = await requests.get('/tasks/user?type=habits');
|
||||||
|
let dailys = await requests.get('/tasks/user?type=dailys');
|
||||||
|
let todos = await requests.get('/tasks/user?type=todos');
|
||||||
|
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||||
|
let tags = await requests.get('/tags');
|
||||||
|
|
||||||
|
expect(habits).to.have.a.lengthOf(3);
|
||||||
|
expect(habits[0].text).to.eql(t('defaultHabit1Text'));
|
||||||
|
expect(habits[0].notes).to.eql('');
|
||||||
|
expect(habits[1].text).to.eql(t('defaultHabit2Text'));
|
||||||
|
expect(habits[1].notes).to.eql('');
|
||||||
|
expect(habits[2].text).to.eql(t('defaultHabit3Text'));
|
||||||
|
expect(habits[2].notes).to.eql('');
|
||||||
|
|
||||||
|
expect(dailys).to.have.a.lengthOf(0);
|
||||||
|
|
||||||
|
expect(todos).to.have.a.lengthOf(1);
|
||||||
|
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
|
||||||
|
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
|
||||||
|
|
||||||
|
expect(rewards).to.have.a.lengthOf(1);
|
||||||
|
expect(rewards[0].text).to.eql(t('defaultReward1Text'));
|
||||||
|
expect(rewards[0].notes).to.eql('');
|
||||||
|
|
||||||
|
expect(tags).to.have.a.lengthOf(7);
|
||||||
|
expect(tags[0].name).to.eql(t('defaultTag1'));
|
||||||
|
expect(tags[1].name).to.eql(t('defaultTag2'));
|
||||||
|
expect(tags[2].name).to.eql(t('defaultTag3'));
|
||||||
|
expect(tags[3].name).to.eql(t('defaultTag4'));
|
||||||
|
expect(tags[4].name).to.eql(t('defaultTag5'));
|
||||||
|
expect(tags[5].name).to.eql(t('defaultTag6'));
|
||||||
|
expect(tags[6].name).to.eql(t('defaultTag7'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('does not provide default tags and tasks', async () => {
|
||||||
|
it('for Android', async () => {
|
||||||
|
api = requester(
|
||||||
|
null,
|
||||||
|
{'x-client': 'habitica-android'},
|
||||||
|
);
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests = new ApiUser(user);
|
||||||
|
|
||||||
|
let habits = await requests.get('/tasks/user?type=habits');
|
||||||
|
let dailys = await requests.get('/tasks/user?type=dailys');
|
||||||
|
let todos = await requests.get('/tasks/user?type=todos');
|
||||||
|
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||||
|
let tags = await requests.get('/tags');
|
||||||
|
|
||||||
|
expect(habits).to.have.a.lengthOf(0);
|
||||||
|
expect(dailys).to.have.a.lengthOf(0);
|
||||||
|
expect(todos).to.have.a.lengthOf(0);
|
||||||
|
expect(rewards).to.have.a.lengthOf(0);
|
||||||
|
expect(tags).to.have.a.lengthOf(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for iOS', async () => {
|
||||||
|
api = requester(
|
||||||
|
null,
|
||||||
|
{'x-client': 'habitica-ios'},
|
||||||
|
);
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests = new ApiUser(user);
|
||||||
|
|
||||||
|
let habits = await requests.get('/tasks/user?type=habits');
|
||||||
|
let dailys = await requests.get('/tasks/user?type=dailys');
|
||||||
|
let todos = await requests.get('/tasks/user?type=todos');
|
||||||
|
let rewards = await requests.get('/tasks/user?type=rewards');
|
||||||
|
let tags = await requests.get('/tags');
|
||||||
|
|
||||||
|
expect(habits).to.have.a.lengthOf(0);
|
||||||
|
expect(dailys).to.have.a.lengthOf(0);
|
||||||
|
expect(todos).to.have.a.lengthOf(0);
|
||||||
|
expect(rewards).to.have.a.lengthOf(0);
|
||||||
|
expect(tags).to.have.a.lengthOf(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enrolls new users in an A/B test', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('includes items awarded by default when creating a new user', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.items.quests.dustbunnies).to.equal(1);
|
||||||
|
expect(user.purchased.background.violet).to.be.ok;
|
||||||
|
expect(user.preferences.background).to.equal('violet');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires password and confirmPassword to match', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
let confirmPassword = 'not password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires a username', async () => {
|
||||||
|
let email = `${generateRandomUserName()}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
let confirmPassword = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires an email', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires a valid email', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = 'notanemail@sdf';
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes email params to a lowercase string before creating the user', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = 'ISANEmAiL@ExAmPle.coM';
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.auth.local.email).to.equal(email.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on a habitica.com email', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@habitica.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails on a habitrpg.com email', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@habitrpg.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'User validation failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires a password', async () => {
|
||||||
|
let username = generateRandomUserName();
|
||||||
|
let email = `${username}@example.com`;
|
||||||
|
let confirmPassword = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
confirmPassword,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('attach to facebook user', () => {
|
||||||
|
let user;
|
||||||
|
let email = 'some@email.net';
|
||||||
|
let username = 'some-username';
|
||||||
|
let password = 'some-password';
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
it('checks onlySocialAttachLocal', async () => {
|
||||||
|
await expect(user.post('/user/auth/local/register', {
|
||||||
|
email,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('onlySocialAttachLocal'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('succeeds', async () => {
|
||||||
|
await user.update({ 'auth.facebook.id': 'some-fb-id', 'auth.local': { ok: true } });
|
||||||
|
await user.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.username).to.eql(username);
|
||||||
|
expect(user.auth.local.email).to.eql(email);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('login is already taken', () => {
|
||||||
|
let username, email, api;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
api = requester();
|
||||||
|
username = generateRandomUserName();
|
||||||
|
email = `${username}@example.com`;
|
||||||
|
|
||||||
|
return generateUser({
|
||||||
|
'auth.local.username': username,
|
||||||
|
'auth.local.lowerCaseUsername': username,
|
||||||
|
'auth.local.email': email,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects if username is already taken', async () => {
|
||||||
|
let uniqueEmail = `${generateRandomUserName()}@example.com`;
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email: uniqueEmail,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('usernameTaken'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects if email is already taken', async () => {
|
||||||
|
let uniqueUsername = generateRandomUserName();
|
||||||
|
let password = 'password';
|
||||||
|
|
||||||
|
await expect(api.post('/user/auth/local/register', {
|
||||||
|
username: uniqueUsername,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('emailTaken'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('req.query.groupInvite', () => {
|
||||||
|
let api, username, email, password;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
api = requester();
|
||||||
|
username = generateRandomUserName();
|
||||||
|
email = `${username}@example.com`;
|
||||||
|
password = 'password';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not crash the signup process when it\'s invalid', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register?groupInvite=aaaaInvalid', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user._id).to.be.a('string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports invite using req.query.groupInvite', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
});
|
||||||
|
|
||||||
|
let invite = encrypt(JSON.stringify({
|
||||||
|
id: group._id,
|
||||||
|
inviter: groupLeader._id,
|
||||||
|
sentAt: Date.now(), // so we can let it expire
|
||||||
|
}));
|
||||||
|
|
||||||
|
let user = await api.post(`/user/auth/local/register?groupInvite=${invite}`, {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.invitations.parties[0].id).to.eql(group._id);
|
||||||
|
expect(user.invitations.parties[0].name).to.eql(group.name);
|
||||||
|
expect(user.invitations.parties[0].inviter).to.eql(groupLeader._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('awards achievement to inviter', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
});
|
||||||
|
|
||||||
|
let invite = encrypt(JSON.stringify({
|
||||||
|
id: group._id,
|
||||||
|
inviter: groupLeader._id,
|
||||||
|
sentAt: Date.now(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
await api.post(`/user/auth/local/register?groupInvite=${invite}`, {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
await groupLeader.sync();
|
||||||
|
expect(groupLeader.achievements.invitedFriend).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('user not added to a party on expired invite', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
|
});
|
||||||
|
|
||||||
|
let invite = encrypt(JSON.stringify({
|
||||||
|
id: group._id,
|
||||||
|
inviter: groupLeader._id,
|
||||||
|
sentAt: Date.now() - 6.912e8, // 8 days old
|
||||||
|
}));
|
||||||
|
|
||||||
|
let user = await api.post(`/user/auth/local/register?groupInvite=${invite}`, {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.invitations.party).to.eql({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds a user to a guild on an invite of type other than party', async () => {
|
||||||
|
let { group, groupLeader } = await createAndPopulateGroup({
|
||||||
|
groupDetails: { type: 'guild', privacy: 'private' },
|
||||||
|
});
|
||||||
|
|
||||||
|
let invite = encrypt(JSON.stringify({
|
||||||
|
id: group._id,
|
||||||
|
inviter: groupLeader._id,
|
||||||
|
sentAt: Date.now(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let user = await api.post(`/user/auth/local/register?groupInvite=${invite}`, {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.invitations.guilds[0]).to.eql({
|
||||||
|
id: group._id,
|
||||||
|
name: group.name,
|
||||||
|
inviter: groupLeader._id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('successful login via api', () => {
|
||||||
|
let api, username, email, password;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
api = requester();
|
||||||
|
username = generateRandomUserName();
|
||||||
|
email = `${username}@example.com`;
|
||||||
|
password = 'password';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets all site tour values to -2 (already seen)', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.flags.tour).to.not.be.empty;
|
||||||
|
|
||||||
|
each(user.flags.tour, (value) => {
|
||||||
|
expect(value).to.eql(-2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('populates user with default todos, not no other task types', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.todos).to.not.be.empty;
|
||||||
|
expect(user.tasksOrder.dailys).to.be.empty;
|
||||||
|
expect(user.tasksOrder.habits).to.be.empty;
|
||||||
|
expect(user.tasksOrder.rewards).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('populates user with default tags', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tags).to.not.be.empty;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('successful login with habitica-web header', () => {
|
||||||
|
let api, username, email, password;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
api = requester({}, {'x-client': 'habitica-web'});
|
||||||
|
username = generateRandomUserName();
|
||||||
|
email = `${username}@example.com`;
|
||||||
|
password = 'password';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets all common tutorial flags to true', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.flags.tour).to.not.be.empty;
|
||||||
|
|
||||||
|
each(user.flags.tutorial.common, (value) => {
|
||||||
|
expect(value).to.eql(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('populates user with default todos, habits, and rewards', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tasksOrder.todos).to.be.empty;
|
||||||
|
expect(user.tasksOrder.dailys).to.be.empty;
|
||||||
|
expect(user.tasksOrder.habits).to.be.empty;
|
||||||
|
expect(user.tasksOrder.rewards).to.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('populates user with default tags', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.tags).to.not.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds the correct tags to the correct tasks', async () => {
|
||||||
|
let user = await api.post('/user/auth/local/register', {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
confirmPassword: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests = new ApiUser(user);
|
||||||
|
|
||||||
|
let habits = await requests.get('/tasks/user?type=habits');
|
||||||
|
let todos = await requests.get('/tasks/user?type=todos');
|
||||||
|
|
||||||
|
expect(habits).to.have.a.lengthOf(0);
|
||||||
|
expect(todos).to.have.a.lengthOf(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
test/api/v4/user/auth/POST-user_verify_display_name.test.js
Normal file
57
test/api/v4/user/auth/POST-user_verify_display_name.test.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
const ENDPOINT = '/user/auth/verify-display-name';
|
||||||
|
|
||||||
|
describe('POST /user/auth/verify-display-name', async () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successfully verifies display name including funky characters', async () => {
|
||||||
|
let newDisplayName = 'Sabé 🤬';
|
||||||
|
let response = await user.post(ENDPOINT, {
|
||||||
|
displayName: newDisplayName,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ isUsable: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
context('errors', async () => {
|
||||||
|
it('errors if display name is not provided', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
})).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name is a slur', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueSlur')] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name contains a slur', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'something_TESTPLACEHOLDERSLURWORDHERE',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength'), t('displaynameIssueSlur')] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('errors if display name has incorrect length', async () => {
|
||||||
|
await expect(user.post(ENDPOINT, {
|
||||||
|
displayName: 'this is a very long display name over 30 characters',
|
||||||
|
})).to.eventually.eql({ isUsable: false, issues: [t('displaynameIssueLength')] });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user