mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-27 11:12:28 +01:00
Compare commits
408 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7df18a97a | ||
|
|
ff5c44c5a8 | ||
|
|
c929fa1351 | ||
|
|
d30e7b9251 | ||
|
|
c6c6632405 | ||
|
|
e0d9ecca52 | ||
|
|
78f187f87a | ||
|
|
af047d3c82 | ||
|
|
20e8f47def | ||
|
|
c496f94c79 | ||
|
|
ad6073220d | ||
|
|
d669db0f9a | ||
|
|
c244fe488d | ||
|
|
aef71db0f5 | ||
|
|
ac566882fe | ||
|
|
1ad662a090 | ||
|
|
1db52de45d | ||
|
|
f8cfdfa37d | ||
|
|
ffd36465c9 | ||
|
|
dbb1e3aa18 | ||
|
|
07ce68c596 | ||
|
|
4c5d72c96f | ||
|
|
7121653515 | ||
|
|
ce2b66a2be | ||
|
|
43e381cfc1 | ||
|
|
ca5161b48a | ||
|
|
44dec6dc4b | ||
|
|
fb8f0bed43 | ||
|
|
c391de9e86 | ||
|
|
7a534ab81d | ||
|
|
32c7f40c4b | ||
|
|
569fbff244 | ||
|
|
3fe847f329 | ||
|
|
d52d759733 | ||
|
|
5e5a755022 | ||
|
|
8d148b4d69 | ||
|
|
248b64a43f | ||
|
|
57ed0f0a10 | ||
|
|
3455adaef5 | ||
|
|
4d0295a60d | ||
|
|
a002bc5e20 | ||
|
|
b9d1086e24 | ||
|
|
412a0ecc8c | ||
|
|
d44c9ea853 | ||
|
|
c44c581265 | ||
|
|
f666f3cd04 | ||
|
|
236bd6cec4 | ||
|
|
e124c36274 | ||
|
|
d5511a0047 | ||
|
|
87f003f392 | ||
|
|
930a869365 | ||
|
|
809da8add0 | ||
|
|
1dad176320 | ||
|
|
1596c6218f | ||
|
|
0d3aba950a | ||
|
|
f1110e0f89 | ||
|
|
7273f8f6d9 | ||
|
|
a0ae200a54 | ||
|
|
ca448f081d | ||
|
|
cd27afa9f0 | ||
|
|
8bc8183895 | ||
|
|
c26c52f1fe | ||
|
|
8d5becc9ce | ||
|
|
cf7f6e2a67 | ||
|
|
acad3b8873 | ||
|
|
04f4eb8490 | ||
|
|
8c8af83dfc | ||
|
|
86d65956d9 | ||
|
|
42c5e6c22b | ||
|
|
79b51a40ce | ||
|
|
79c3efaf9c | ||
|
|
74c6a891fc | ||
|
|
9a5d17f538 | ||
|
|
070c4a8fbd | ||
|
|
2bbc4f4f4d | ||
|
|
39c00ea433 | ||
|
|
dd6c1c764a | ||
|
|
9b456d1760 | ||
|
|
acf1031317 | ||
|
|
5d45204d8b | ||
|
|
37a71924fe | ||
|
|
9cf2408988 | ||
|
|
638525f8d8 | ||
|
|
2c37ba3cee | ||
|
|
ad5b2fe540 | ||
|
|
bfb6daad51 | ||
|
|
281b8e2b7c | ||
|
|
3f88ea2378 | ||
|
|
9c6275d4ab | ||
|
|
fd3c8ddc8b | ||
|
|
72f47ad4e6 | ||
|
|
74c9a1b02d | ||
|
|
ffa561473c | ||
|
|
080ffae4e1 | ||
|
|
e395182c95 | ||
|
|
68f4275c44 | ||
|
|
4bf4c3a6c2 | ||
|
|
f00bb29192 | ||
|
|
016447ec77 | ||
|
|
fa024e071b | ||
|
|
28fec237fe | ||
|
|
e4bb82768c | ||
|
|
65eca22407 | ||
|
|
cea1597ee1 | ||
|
|
3906952154 | ||
|
|
6169b9d0ae | ||
|
|
69cac7e504 | ||
|
|
febf3f0024 | ||
|
|
563f40e4b7 | ||
|
|
e2b06161e1 | ||
|
|
e7de8b8e2f | ||
|
|
a0624d9507 | ||
|
|
cddd0df4f2 | ||
|
|
220bfb3517 | ||
|
|
2106a5ebd3 | ||
|
|
bbffa9830b | ||
|
|
caa546eb62 | ||
|
|
4e83059652 | ||
|
|
903cdb36ef | ||
|
|
d8128cc3db | ||
|
|
f888e80b01 | ||
|
|
fd7aedbff2 | ||
|
|
6c5234313d | ||
|
|
08aa5758b4 | ||
|
|
1415e344c0 | ||
|
|
2ce7915f06 | ||
|
|
838c8b4e08 | ||
|
|
1590d955cd | ||
|
|
2690caed35 | ||
|
|
dc2d4fa10b | ||
|
|
1540ec89ee | ||
|
|
f304d4fe52 | ||
|
|
023e433a5c | ||
|
|
ef4aeb29ab | ||
|
|
2b80931202 | ||
|
|
2950713712 | ||
|
|
118f3bd1bb | ||
|
|
69f1343ea8 | ||
|
|
918ee02d64 | ||
|
|
0cac34dd26 | ||
|
|
1c859fc91f | ||
|
|
857aa5827b | ||
|
|
28e8ec2d2c | ||
|
|
856f13d213 | ||
|
|
121fd38bd1 | ||
|
|
36d72f5f7a | ||
|
|
f1b8bd80e7 | ||
|
|
84d2ce6a3f | ||
|
|
76010e6c8f | ||
|
|
c707b6c99b | ||
|
|
e4bd466cc7 | ||
|
|
001b8eb894 | ||
|
|
9abcfe8614 | ||
|
|
bc6102551d | ||
|
|
959a3ff85b | ||
|
|
518b874f64 | ||
|
|
6cc359a935 | ||
|
|
514d35c0be | ||
|
|
13da92ea68 | ||
|
|
03c4d82b7d | ||
|
|
d905ab7f86 | ||
|
|
c6560b6b1b | ||
|
|
c61f660255 | ||
|
|
2f1b683ec9 | ||
|
|
47bb217068 | ||
|
|
f49fd05da1 | ||
|
|
b0341aa06f | ||
|
|
b07ec18e33 | ||
|
|
12930a2bac | ||
|
|
91f5c47d9d | ||
|
|
fe7850d10f | ||
|
|
c5c2da75bf | ||
|
|
969607cd3b | ||
|
|
2a1f52a359 | ||
|
|
47d9594679 | ||
|
|
97e40c81f3 | ||
|
|
c8b61a2f7d | ||
|
|
e9543f0d28 | ||
|
|
77b88490e4 | ||
|
|
7fc2500bfd | ||
|
|
fb229acb58 | ||
|
|
6ce83d1fa4 | ||
|
|
2be4815aea | ||
|
|
1dbc42f48a | ||
|
|
89279c8aed | ||
|
|
faedb13598 | ||
|
|
c0c74659c5 | ||
|
|
bf5ad2db1f | ||
|
|
7d99873960 | ||
|
|
e02ef00397 | ||
|
|
23c5c4211c | ||
|
|
69cc134fff | ||
|
|
ffd9400cb7 | ||
|
|
5be91ef842 | ||
|
|
3123183e46 | ||
|
|
49cca7a601 | ||
|
|
7fbd38d18c | ||
|
|
1f95376d39 | ||
|
|
2da0a1e88c | ||
|
|
afacd3e1cf | ||
|
|
a69b9e6705 | ||
|
|
e4e5d10316 | ||
|
|
27c38bdf45 | ||
|
|
ea24eeb019 | ||
|
|
55a8eef3e1 | ||
|
|
92cbb4a07d | ||
|
|
3f96d05365 | ||
|
|
0b72f6a613 | ||
|
|
5e1e6be518 | ||
|
|
472ec99291 | ||
|
|
0284e9a4e3 | ||
|
|
1a0721c078 | ||
|
|
6b6b548ac5 | ||
|
|
30f3d786bb | ||
|
|
07bbba6789 | ||
|
|
6afb2bd0d4 | ||
|
|
f1a3bd5001 | ||
|
|
3f6a13d209 | ||
|
|
3658e41fec | ||
|
|
c69d5c7ae6 | ||
|
|
747f9e6a99 | ||
|
|
7755ab090b | ||
|
|
9ed17df1e3 | ||
|
|
faeb040a83 | ||
|
|
0a1ae1375e | ||
|
|
9756030fa2 | ||
|
|
c66172b74b | ||
|
|
281f6d1806 | ||
|
|
237095d109 | ||
|
|
fa788f49fc | ||
|
|
0817cf96e1 | ||
|
|
97e1d75dce | ||
|
|
52bf20c27d | ||
|
|
5dbaf39aba | ||
|
|
66d402c985 | ||
|
|
8048146223 | ||
|
|
e2c07e458d | ||
|
|
90a9e8e192 | ||
|
|
f8039f48a6 | ||
|
|
04337f8e83 | ||
|
|
45297f8bf9 | ||
|
|
6f112c29f2 | ||
|
|
4d1edb363c | ||
|
|
4e303cc592 | ||
|
|
798a975185 | ||
|
|
eb2b46fc5d | ||
|
|
29854d3bdb | ||
|
|
f8751b002c | ||
|
|
cd545e08d5 | ||
|
|
f69bb4f023 | ||
|
|
847081d2b2 | ||
|
|
8112d46ea4 | ||
|
|
d13bded647 | ||
|
|
1de4ab3612 | ||
|
|
f9f22f313f | ||
|
|
f57eed85a8 | ||
|
|
10dd3318ab | ||
|
|
cbef83c14a | ||
|
|
59709a8590 | ||
|
|
f85f2a0c6d | ||
|
|
605a5a1d5c | ||
|
|
2d5d786c8e | ||
|
|
5efe5b7b10 | ||
|
|
3e92bb22fa | ||
|
|
1249b9d410 | ||
|
|
197aafe092 | ||
|
|
79829ca128 | ||
|
|
adaa1d9a3e | ||
|
|
3e6691dbbb | ||
|
|
046761b9aa | ||
|
|
0b0466b960 | ||
|
|
f8d4a2bd6b | ||
|
|
1af59a3770 | ||
|
|
bbcb13c91b | ||
|
|
d27dc46c50 | ||
|
|
679459b83b | ||
|
|
5a619773d5 | ||
|
|
ad76ab1315 | ||
|
|
15eb8db925 | ||
|
|
a0e92c5605 | ||
|
|
eac3e36c07 | ||
|
|
0b8def555b | ||
|
|
5f5fa5c2eb | ||
|
|
1eac8bbbbe | ||
|
|
49c7580cd4 | ||
|
|
dca958f2e2 | ||
|
|
eae5f0d605 | ||
|
|
6ab091645c | ||
|
|
d66041c280 | ||
|
|
de070a450a | ||
|
|
eaaab35f31 | ||
|
|
6a63f080ad | ||
|
|
c42f81b629 | ||
|
|
9a78a7b896 | ||
|
|
8b70721137 | ||
|
|
44ffbd716d | ||
|
|
5bfc3a5ff4 | ||
|
|
0ba5df4164 | ||
|
|
52a59c8192 | ||
|
|
c1a860494d | ||
|
|
395dafa127 | ||
|
|
bab41647f5 | ||
|
|
8582a67308 | ||
|
|
0d58fb0fd3 | ||
|
|
1d2482f8bc | ||
|
|
f4cf906127 | ||
|
|
559f9b1825 | ||
|
|
c7039bc9ea | ||
|
|
f929d36e1a | ||
|
|
254d1a3465 | ||
|
|
442aae8a35 | ||
|
|
bcb0ed0a5c | ||
|
|
a48b8f0e34 | ||
|
|
7eeeda2aae | ||
|
|
a5ad9c30f0 | ||
|
|
ac732b2c85 | ||
|
|
a56b2d68fb | ||
|
|
25b0ff38c4 | ||
|
|
dcc06931cc | ||
|
|
bc3ebbd095 | ||
|
|
e5b9581743 | ||
|
|
4b9fe49e3a | ||
|
|
ab4c8b0a46 | ||
|
|
f6c26fe869 | ||
|
|
80e9735b28 | ||
|
|
aa6f188bd9 | ||
|
|
e8b7660376 | ||
|
|
7d76622410 | ||
|
|
928e5f66c4 | ||
|
|
6a343535c0 | ||
|
|
f58f6acb44 | ||
|
|
64754777ed | ||
|
|
3b5e4b6d84 | ||
|
|
9383578cb8 | ||
|
|
474672ec64 | ||
|
|
25c6691793 | ||
|
|
3ea7b72024 | ||
|
|
2d6f05a9a4 | ||
|
|
28637286d6 | ||
|
|
874887b790 | ||
|
|
c977e5ebb5 | ||
|
|
f040e668f3 | ||
|
|
55a15f938c | ||
|
|
8c4f35daf4 | ||
|
|
8f38ce3424 | ||
|
|
b8f57a74d0 | ||
|
|
7ed26c0dbe | ||
|
|
e8f5b26d4d | ||
|
|
0273648b6b | ||
|
|
b6fdac8885 | ||
|
|
00e6389672 | ||
|
|
e02c669b61 | ||
|
|
f0cb7c6bf3 | ||
|
|
571ef0b309 | ||
|
|
74328d1bcc | ||
|
|
d34a9d828c | ||
|
|
2fd35b3a40 | ||
|
|
e27512f626 | ||
|
|
dbf9cb3b4e | ||
|
|
34c1245519 | ||
|
|
f602bfe438 | ||
|
|
9aa4b8aa64 | ||
|
|
5a150ebc5b | ||
|
|
cbe1892b50 | ||
|
|
13df60e0dd | ||
|
|
3ff7692528 | ||
|
|
111bba84dc | ||
|
|
b0d2b72b88 | ||
|
|
696317ea8a | ||
|
|
593178a46a | ||
|
|
f8fe16482d | ||
|
|
5108480ec5 | ||
|
|
95968b1b1c | ||
|
|
566569af98 | ||
|
|
6693e9fca9 | ||
|
|
431bde56d2 | ||
|
|
7cf17c0e63 | ||
|
|
49561bfc8c | ||
|
|
8cbbb58e78 | ||
|
|
905549e379 | ||
|
|
5d45c7209a | ||
|
|
371cddfe17 | ||
|
|
fcfac30caa | ||
|
|
b094fb1e52 | ||
|
|
a2dd82b6db | ||
|
|
e6071610e4 | ||
|
|
bdd0e2bb79 | ||
|
|
054a9a6f2b | ||
|
|
35b9ed6273 | ||
|
|
e65277baa5 | ||
|
|
421bd8624c | ||
|
|
4562c6422a | ||
|
|
a5cd9f2473 | ||
|
|
18bbdfa84b | ||
|
|
d8c37f6e2d | ||
|
|
6801dae75d | ||
|
|
59e1de6771 | ||
|
|
5b240a1950 | ||
|
|
3ec3722038 | ||
|
|
d798ebadfe | ||
|
|
6cbddef627 | ||
|
|
016de411c9 | ||
|
|
2173f53883 | ||
|
|
f2e5bc52e5 | ||
|
|
393a9290e9 | ||
|
|
ad5045bc09 | ||
|
|
9b515ebdd1 | ||
|
|
97bf9ee8e8 |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.git
|
||||
website
|
||||
@@ -14,7 +14,7 @@ files:
|
||||
owner: root
|
||||
group: users
|
||||
content: |
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@3
|
||||
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
|
||||
container_commands:
|
||||
01_makeBabel:
|
||||
command: "touch /tmp/.babel.json"
|
||||
|
||||
@@ -20,8 +20,4 @@ website/common/browserify.js
|
||||
test/content/**/*
|
||||
Gruntfile.js
|
||||
gulpfile.js
|
||||
gulp
|
||||
webpack
|
||||
test/client/e2e
|
||||
test/client/unit/index.js
|
||||
test/client/unit/karma.conf.js
|
||||
gulp
|
||||
@@ -4,7 +4,7 @@
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg/es6",
|
||||
"habitrpg"
|
||||
"habitrpg",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
}
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -9,5 +9,6 @@ Fixes put_issue_url_here
|
||||
|
||||
|
||||
[//]: # (Put User ID in here - found in Settings -> API)
|
||||
|
||||
----
|
||||
UUID:
|
||||
|
||||
15
.travis.yml
15
.travis.yml
@@ -1,8 +1,16 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4.3.1'
|
||||
- '6'
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- npm install -g npm@3
|
||||
- $CXX --version
|
||||
- npm install -g npm@4
|
||||
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
|
||||
before_script:
|
||||
- npm run test:build
|
||||
@@ -12,6 +20,9 @@ after_script:
|
||||
- ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js
|
||||
script: npm run $TEST
|
||||
env:
|
||||
global:
|
||||
- CXX=g++-4.8
|
||||
- DISABLE_REQUEST_LOGGING=true
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api-v3" REQUIRES_SERVER=true
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -17,20 +17,22 @@ RUN apt-get install -y \
|
||||
python
|
||||
|
||||
# Install NodeJS
|
||||
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
|
||||
RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
# Install npm@latest
|
||||
RUN curl -sL https://www.npmjs.org/install.sh | sh
|
||||
|
||||
# Clean up package management
|
||||
RUN apt-get clean
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install global packages
|
||||
RUN npm install -g npm@3
|
||||
RUN npm install -g gulp grunt-cli bower
|
||||
RUN npm install -g gulp grunt-cli bower mocha
|
||||
|
||||
# Clone Habitica repo and install dependencies
|
||||
WORKDIR /habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitrpg.git /habitrpg
|
||||
RUN git clone https://github.com/HabitRPG/habitica.git /habitrpg
|
||||
RUN npm install
|
||||
RUN bower install --allow-root
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ module.exports = function(grunt) {
|
||||
files: [
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'},
|
||||
{expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'},
|
||||
@@ -78,6 +78,7 @@ module.exports = function(grunt) {
|
||||
'website/build/favicon.ico',
|
||||
'website/build/favicon_192x192.png',
|
||||
'website/build/*.png',
|
||||
'website/build/static/sprites/*.png',
|
||||
'website/build/*.gif',
|
||||
'website/build/bower_components/bootstrap/dist/fonts/*'
|
||||
],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/r/HabitRPG/habitrpg?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://coveralls.io/github/HabitRPG/habitica?branch=develop) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
||||
===============
|
||||
|
||||
[Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
"PORT":3000,
|
||||
"ENABLE_CONSOLE_LOGS_IN_PROD":"false",
|
||||
"IP":"0.0.0.0",
|
||||
"CORES":1,
|
||||
"WEB_CONCURRENCY":1,
|
||||
"BASE_URL":"http://localhost:3000",
|
||||
"FACEBOOK_ANALYTICS":"1234567890123456",
|
||||
"FACEBOOK_KEY":"123456789012345",
|
||||
"FACEBOOK_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"GOOGLE_CLIENT_ID":"123456789012345",
|
||||
"GOOGLE_CLIENT_SECRET":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"PLAY_API": {
|
||||
"CLIENT_ID": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"CLIENT_SECRET": "aaaabbbbccccddddeeeeffff00001111",
|
||||
"ACCESS_TOKEN":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"REFRESH_TOKEN":"aaaabbbbccccddddeeeeffff00001111"
|
||||
},
|
||||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
||||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
||||
"NODE_ENV":"development",
|
||||
@@ -79,6 +84,7 @@
|
||||
},
|
||||
"SLACK": {
|
||||
"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/",
|
||||
"SUBSCRIPTIONS_URL": "https://hooks.slack.com/services/id/id/id"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
web:
|
||||
volumes:
|
||||
- '.:/habitrpg'
|
||||
- '.:/habitrpg'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "3000:3000"
|
||||
links:
|
||||
- mongo
|
||||
- mongo
|
||||
environment:
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
- NODE_DB_URI=mongodb://mongo/habitrpg
|
||||
|
||||
mongo:
|
||||
image: mongo
|
||||
ports:
|
||||
- "27017:27017"
|
||||
- "27017:27017"
|
||||
|
||||
@@ -12,6 +12,9 @@ import {each} from 'lodash';
|
||||
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
|
||||
const DIST_PATH = 'website/assets/sprites/dist/';
|
||||
|
||||
const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/';
|
||||
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
|
||||
|
||||
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
@@ -25,7 +28,7 @@ gulp.task('sprites:largeSprites', () => {
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (done) => {
|
||||
clean(`${DIST_PATH}spritesmith*`, done);
|
||||
clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
|
||||
@@ -66,14 +69,16 @@ function createSpritesStream (name, src) {
|
||||
algorithm: 'binary-tree',
|
||||
padding: 1,
|
||||
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
|
||||
cssVarMap: cssVarMap
|
||||
cssVarMap: cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
let cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
|
||||
.pipe(gulp.dest(DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
@@ -148,4 +153,9 @@ function cssVarMap (sprite) {
|
||||
}
|
||||
if (~sprite.name.indexOf('shirt'))
|
||||
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
|
||||
if (~sprite.name.indexOf('hair_base')) {
|
||||
let styleArray = sprite.name.split('_').slice(2,3);
|
||||
if (Number(styleArray[0]) > 14)
|
||||
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ gulp.task('test:api-v3:integration:watch', () => {
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'),
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err, stdout, stderr) => done(err)
|
||||
);
|
||||
|
||||
@@ -84,8 +84,8 @@ gulp.task('transifex:malformedStrings', () => {
|
||||
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||
let missingInterploationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterploationString);
|
||||
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
75
migrations/20161102_takeThis.js
Normal file
75
migrations/20161102_takeThis.js
Normal file
@@ -0,0 +1,75 @@
|
||||
var migrationName = '20161102_takeThis.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['d1be0965-e909-4d30-82fa-9a0011f885b2']}
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
74
migrations/20161122_turkey_ladder.js
Normal file
74
migrations/20161122_turkey_ladder.js
Normal file
@@ -0,0 +1,74 @@
|
||||
var migrationName = '20161122_turkey_ladder.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Yearly Turkey Day award. Turkey pet, Turkey mount, Gilded Turkey pet, Gilded Turkey mount
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-10-31')} // Extend timeframe each run of migration
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'migration': 1,
|
||||
'items.mounts': 1,
|
||||
'items.pets': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (user.items.pets['Turkey-Gilded']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true};
|
||||
} else if (user.items.mounts['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5};
|
||||
} else if (user.items.pets['Turkey-Base']) {
|
||||
set = {'migration':migrationName, 'items.mounts.Turkey-Base':true};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.pets.Turkey-Base':5};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
73
migrations/20161230_nye_hats.js
Normal file
73
migrations/20161230_nye_hats.js
Normal file
@@ -0,0 +1,73 @@
|
||||
var migrationName = '20161230_nye_hats.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Yearly New Year's party hat award
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2016-11-30')} // Remove after first run
|
||||
};
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'items.gear.owned': 1,
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
113
migrations/20170120_missing_incentive.js
Normal file
113
migrations/20170120_missing_incentive.js
Normal file
@@ -0,0 +1,113 @@
|
||||
var migrationName = '20170120_missing_incentive.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award missing Royal Purple Hatching Potion to users with 55+ check-ins
|
||||
* Reduce users with impossible check-in counts to a reasonable number
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
import common from '../website/common';
|
||||
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'loginIncentives': {$gt:54},
|
||||
'migration': {$ne: migrationName},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var language = user.preferences.language || 'en';
|
||||
var set = {'migration': migrationName};
|
||||
var inc = {'items.hatchingPotions.RoyalPurple': 1};
|
||||
if (user.loginIncentives > 58) {
|
||||
set = {'migration': migrationName, 'loginIncentives': 58};
|
||||
}
|
||||
var push = {
|
||||
'notifications': {
|
||||
'type': 'LOGIN_INCENTIVE',
|
||||
'data': {
|
||||
'nextRewardAt': 60,
|
||||
'rewardKey': [
|
||||
'Pet_HatchingPotion_Purple',
|
||||
],
|
||||
'rewardText': common.i18n.t('potion', {potionType: common.i18n.t('hatchingPotionRoyalPurple', language)}, language),
|
||||
'reward': [
|
||||
{
|
||||
'premium': true,
|
||||
'key': 'RoyalPurple',
|
||||
'limited': true,
|
||||
'value': 2,
|
||||
}
|
||||
],
|
||||
'message': common.i18n.t('unlockedCheckInReward', language),
|
||||
},
|
||||
'id': common.uuid(),
|
||||
}
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set, $push:push, $inc:inc});
|
||||
|
||||
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;
|
||||
109
migrations/20170131_habit_birthday.js
Normal file
109
migrations/20170131_habit_birthday.js
Normal file
@@ -0,0 +1,109 @@
|
||||
var migrationName = '20170131_habit_birthday.js';
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes,
|
||||
* 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake!
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'auth.timestamps.loggedin':{$gt:new Date('2017-01-24')}, // remove after first run to cover remaining users
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data)
|
||||
'items.gear.owned'
|
||||
],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {'migration':migrationName};
|
||||
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) {
|
||||
set['items.gear.owned.armor_special_birthday2017'] = false;
|
||||
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) {
|
||||
set['items.gear.owned.armor_special_birthday2016'] = false;
|
||||
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) {
|
||||
set['items.gear.owned.armor_special_birthday2015'] = false;
|
||||
} else {
|
||||
set['items.gear.owned.armor_special_birthday'] = false;
|
||||
}
|
||||
|
||||
var inc = {
|
||||
'items.food.Cake_Skeleton':1,
|
||||
'items.food.Cake_Base':1,
|
||||
'items.food.Cake_CottonCandyBlue':1,
|
||||
'items.food.Cake_CottonCandyPink':1,
|
||||
'items.food.Cake_Shade':1,
|
||||
'items.food.Cake_White':1,
|
||||
'items.food.Cake_Golden':1,
|
||||
'items.food.Cake_Zombie':1,
|
||||
'items.food.Cake_Desert':1,
|
||||
'items.food.Cake_Red':1,
|
||||
'achievements.habitBirthdays':1
|
||||
};
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
|
||||
|
||||
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;
|
||||
21
migrations/migration-runner.js
Normal file
21
migrations/migration-runner.js
Normal file
@@ -0,0 +1,21 @@
|
||||
require("babel-register");
|
||||
require("babel-polyfill");
|
||||
|
||||
// This file must use ES5, everything required can be in ES6
|
||||
|
||||
function setUpServer () {
|
||||
var nconf = require('nconf');
|
||||
var mongoose = require('mongoose');
|
||||
var Bluebird = require('bluebird');
|
||||
var setupNconf = require('../website/server/libs/setupNconf');
|
||||
setupNconf();
|
||||
// We require src/server and npt src/index because
|
||||
// 1. nconf is already setup
|
||||
// 2. we don't need clustering
|
||||
require('../website/server/server'); // eslint-disable-line global-require
|
||||
}
|
||||
setUpServer();
|
||||
|
||||
// Replace this with your migration
|
||||
var processUsers = require('./new_stuff');
|
||||
processUsers();
|
||||
@@ -2,7 +2,7 @@ var _id = '';
|
||||
var update = {
|
||||
$addToSet: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['head_mystery_201610','armor_mystery_201610']
|
||||
$each:['shield_mystery_201701','eyewear_mystery_201701']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,49 +6,69 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||
*/
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
var dbUsers = mongo.db(connectionString).collection('users');
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.newStuff': {$ne:true},
|
||||
};
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.newStuff':{$ne:true}
|
||||
};
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
};
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {'flags.newStuff':true};
|
||||
var set = {'flags.newStuff': true};
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
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!'; }
|
||||
@@ -58,3 +78,5 @@ function exiting(code, msg) {
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
|
||||
@@ -6,52 +6,69 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
* Remove flag stating that the Enchanted Armoire is empty, for when new equipment is added
|
||||
*/
|
||||
|
||||
var dbserver = 'localhost:27017'; // FOR TEST DATABASE
|
||||
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE
|
||||
var dbname = 'habitrpg';
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
var mongo = require('mongoskin');
|
||||
var _ = require('lodash');
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.armoireEmpty': true,
|
||||
};
|
||||
|
||||
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'flags.armoireEmpty':true
|
||||
};
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
var fields = {
|
||||
'flags.armoireEmpty':1
|
||||
};
|
||||
|
||||
console.warn('Updating users...');
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
|
||||
if (err) { return exiting(1, 'ERROR! ' + err); }
|
||||
if (!user) {
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
return displayData();
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
// specify user data to change:
|
||||
var set = {'migration':migrationName, 'flags.armoireEmpty':false};
|
||||
var set = {'migration': migrationName, 'flags.armoireEmpty': false};
|
||||
|
||||
dbUsers.update({_id:user._id}, {$set:set});
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
|
||||
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!'; }
|
||||
@@ -61,3 +78,5 @@ function exiting(code, msg) {
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
|
||||
115
migrations/restore-profile-data.js
Normal file
115
migrations/restore-profile-data.js
Normal file
@@ -0,0 +1,115 @@
|
||||
var migrationName = 'restore_profile_data.js';
|
||||
var authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
|
||||
var authorUuid = ''; //... own data is done
|
||||
|
||||
/*
|
||||
* Check if users have empty profile data in new database and update it with old database info
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = ''; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
var monk2 = require('monk');
|
||||
var oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId)
|
||||
{
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
// 'profile.name': 'profile name not found',
|
||||
'profile.blurb': null,
|
||||
// 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')},
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: ['_id', 'profile', 'auth.timestamps.loggedin'] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
}
|
||||
|
||||
var userPaymentPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPaymentPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
if (!user.profile.name || user.profile.name === 'profile name not found' || !user.profile.imageUrl || !user.profile.blurb) {
|
||||
return olDbUsers.findOne({_id: user._id}, '_id profile')
|
||||
.then((oldUserData) => {
|
||||
if (!oldUserData) return;
|
||||
// specify user data to change:
|
||||
var set = {};
|
||||
|
||||
if (oldUserData.profile.name === 'profile name not found') return;
|
||||
|
||||
var userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
|
||||
if (userNeedsProfileName && oldUserData.profile.name) {
|
||||
set['profile.name'] = oldUserData.profile.name;
|
||||
}
|
||||
|
||||
if (!user.profile.imageUrl && oldUserData.profile.imageUrl) {
|
||||
set['profile.imageUrl'] = oldUserData.profile.imageUrl;
|
||||
}
|
||||
|
||||
if (!user.profile.blurb && oldUserData.profile.blurb) {
|
||||
set['profile.blurb'] = oldUserData.profile.blurb;
|
||||
}
|
||||
|
||||
if (Object.keys(set).length !== 0 && set.constructor === Object) {
|
||||
console.log(set)
|
||||
return dbUsers.update({_id: user._id}, {$set:set});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
processUsers()
|
||||
101
migrations/takeThis.js
Normal file
101
migrations/takeThis.js
Normal file
@@ -0,0 +1,101 @@
|
||||
var migrationName = '20170201_takeThis.js'; // Update per month
|
||||
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||
|
||||
/*
|
||||
* Award Take This ladder items to participants in this month's challenge
|
||||
*/
|
||||
|
||||
var monk = require('monk');
|
||||
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers(lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
var query = {
|
||||
'migration':{$ne:migrationName},
|
||||
'challenges':{$in:['b1d436b5-c784-42e3-9b07-7072479a6f8e']} // Update per month
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
$gt: lastId
|
||||
}
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
limit: 250,
|
||||
fields: [
|
||||
'items.gear.owned',
|
||||
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch(function (err) {
|
||||
console.log(err);
|
||||
return exiting(1, 'ERROR! ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
var progressCount = 1000;
|
||||
var count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
}
|
||||
|
||||
var userPromises = users.map(updateUser);
|
||||
var lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(function () {
|
||||
processUsers(lastUser._id);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
|
||||
var set = {};
|
||||
|
||||
if (typeof user.items.gear.owned.back_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName};
|
||||
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
|
||||
} else {
|
||||
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
|
||||
}
|
||||
|
||||
dbUsers.update({_id: user._id}, {$set:set});
|
||||
|
||||
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;
|
||||
5298
npm-shrinkwrap.json
generated
5298
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "3.52.0",
|
||||
"version": "3.75.3",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@slack/client": "3.6.0",
|
||||
@@ -13,15 +13,19 @@
|
||||
"async": "^1.5.0",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"aws-sdk": "^2.0.25",
|
||||
"axios": "^0.15.3",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
||||
"babel-plugin-transform-regenerator": "^6.16.1",
|
||||
"babel-polyfill": "^6.6.1",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-register": "^6.6.0",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"babelify": "^7.2.0",
|
||||
"bcrypt": "^1.0.2",
|
||||
"bluebird": "^3.3.5",
|
||||
"body-parser": "^1.15.0",
|
||||
"bower": "~1.3.12",
|
||||
@@ -29,7 +33,7 @@
|
||||
"compression": "^1.6.1",
|
||||
"connect-ratelimit": "0.0.7",
|
||||
"cookie-session": "^1.2.0",
|
||||
"coupon-code": "^0.4.3",
|
||||
"coupon-code": "^0.4.5",
|
||||
"css-loader": "^0.23.1",
|
||||
"csv-stringify": "^1.0.2",
|
||||
"cwait": "^1.0.0",
|
||||
@@ -64,32 +68,33 @@
|
||||
"image-size": "~0.3.2",
|
||||
"in-app-purchase": "^1.1.6",
|
||||
"jade": "~1.11.0",
|
||||
"jquery": "https://registry.npmjs.org/jquery/-/jquery-3.1.1.tgz",
|
||||
"js2xmlparser": "~1.0.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^2.7.1",
|
||||
"less-loader": "^2.2.3",
|
||||
"lodash": "^3.10.1",
|
||||
"lodash.setwith": "^4.2.0",
|
||||
"lodash.pickby": "^4.2.0",
|
||||
"lodash.setwith": "^4.2.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"method-override": "^2.3.5",
|
||||
"moment": "^2.13.0",
|
||||
"mongoose": "^4.4.16",
|
||||
"mongoose": "^4.7.1",
|
||||
"mongoose-id-autoinc": "~2013.7.14-4",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "~0.8.2",
|
||||
"newrelic": "^1.27.2",
|
||||
"nib": "^1.1.0",
|
||||
"node-gcm": "^0.14.4",
|
||||
"nodemailer": "^2.3.2",
|
||||
"object-path": "^0.9.2",
|
||||
"ora": "^0.2.0",
|
||||
"pageres": "^4.1.1",
|
||||
"passport": "~0.2.1",
|
||||
"passport-facebook": "2.0.0",
|
||||
"passport": "^0.3.2",
|
||||
"passport-facebook": "^2.0.0",
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.2.1",
|
||||
"postcss-easy-import": "^1.0.1",
|
||||
"pretty-data": "^0.40.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.0-beta6",
|
||||
@@ -111,11 +116,11 @@
|
||||
"validator": "^4.9.0",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"vue": "^2.0.0-rc.6",
|
||||
"vue": "^2.1.0",
|
||||
"vue-hot-reload-api": "^1.2.0",
|
||||
"vue-loader": "^9.4.0",
|
||||
"vue-resource": "^1.0.2",
|
||||
"vue-loader": "^10.0.0",
|
||||
"vue-router": "^2.0.0-rc.5",
|
||||
"vue-template-compiler": "^2.1.0",
|
||||
"webpack": "^1.12.2",
|
||||
"webpack-merge": "^0.8.3",
|
||||
"winston": "^2.1.0",
|
||||
@@ -123,8 +128,8 @@
|
||||
},
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^4.3.1",
|
||||
"npm": "^3.8.9"
|
||||
"node": "^6.9.1",
|
||||
"npm": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
@@ -155,7 +160,6 @@
|
||||
"postinstall": "bower --config.interactive=false install -f; gulp build; npm run client:build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.0.0",
|
||||
"chai": "^3.4.0",
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"chalk": "^1.1.3",
|
||||
@@ -165,13 +169,12 @@
|
||||
"cross-spawn": "^2.1.5",
|
||||
"csv": "~0.3.6",
|
||||
"deep-diff": "~0.1.4",
|
||||
"eslint": "~2.12.0",
|
||||
"eslint-config-habitrpg": "^1.0.0",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-config-habitrpg": "^2.0.0",
|
||||
"eslint-friendly-formatter": "^2.0.5",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-babel": "^3.0.0",
|
||||
"eslint-plugin-html": "^1.3.0",
|
||||
"eslint-plugin-mocha": "^2.1.0",
|
||||
"eslint-plugin-mocha": "^4.7.0",
|
||||
"event-stream": "^3.2.2",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"expect.js": "~0.2.0",
|
||||
@@ -196,6 +199,7 @@
|
||||
"mocha": "^2.3.3",
|
||||
"mongodb": "^2.0.46",
|
||||
"mongoskin": "~2.1.0",
|
||||
"monk": "^3.1.3",
|
||||
"nightwatch": "^0.8.18",
|
||||
"phantomjs-prebuilt": "^2.1.12",
|
||||
"protractor": "^3.1.1",
|
||||
@@ -204,6 +208,7 @@
|
||||
"selenium-server": "2.53.0",
|
||||
"sinon": "^1.17.2",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"sinon-stub-promise": "^4.0.0",
|
||||
"superagent-defaults": "^0.1.13",
|
||||
"vinyl-transform": "^1.0.0",
|
||||
"webpack-dev-middleware": "^1.4.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": [
|
||||
"habitrpg/mocha",
|
||||
"habitrpg/babel"
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('GET challenges/group/:groupId', () => {
|
||||
describe('GET challenges/groups/:groupId', () => {
|
||||
context('Public Guild', () => {
|
||||
let publicGuild, user, nonMember, challenge, challenge2;
|
||||
|
||||
|
||||
@@ -35,6 +35,24 @@ describe('POST /chat', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when an empty message is provided', async () => {
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: ' '}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when an message containing only newlines is provided', async () => {
|
||||
await expect(user.post(`/groups/${groupWithChat._id}/chat`, { message: '\n\n'}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns an error when group is not found', async () => {
|
||||
await expect(user.post('/groups/invalidID/chat', { message: testMessage})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
|
||||
@@ -7,15 +7,21 @@ import {
|
||||
import moment from 'moment';
|
||||
|
||||
describe('GET /export/history.csv', () => {
|
||||
it('should return a valid CSV file with tasks history data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid CSV file with tasks history data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'daily', text: 'daily 1'},
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'habit', text: 'habit 2'},
|
||||
{type: 'todo', text: 'todo 1'},
|
||||
]);
|
||||
|
||||
// to handle occasional inconsistency in task creation order
|
||||
tasks.sort(function (a, b) {
|
||||
return a.text.localeCompare(b.text);
|
||||
});
|
||||
|
||||
// score all the tasks twice
|
||||
await user.post(`/tasks/${tasks[0]._id}/score/up`);
|
||||
await user.post(`/tasks/${tasks[1]._id}/score/up`);
|
||||
@@ -28,7 +34,7 @@ describe('GET /export/history.csv', () => {
|
||||
await user.post(`/tasks/${tasks[3]._id}/score/up`);
|
||||
|
||||
// adding an history entry to daily 1 manually because cron didn't run yet
|
||||
await updateDocument('tasks', tasks[1], {
|
||||
await updateDocument('tasks', tasks[0], {
|
||||
history: [{value: 3.2, date: Number(new Date())}],
|
||||
});
|
||||
|
||||
@@ -41,11 +47,11 @@ describe('GET /export/history.csv', () => {
|
||||
let splitRes = res.split('\n');
|
||||
|
||||
expect(splitRes[0]).to.equal('Task Name,Task ID,Task Type,Date,Value');
|
||||
expect(splitRes[1]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[0]._id},habit,${moment(tasks[0].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[1].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[5]).to.equal(`daily 1,${tasks[1]._id},daily,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[1]).to.equal(`daily 1,${tasks[0]._id},daily,${moment(tasks[0].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[0].history[0].value}`);
|
||||
expect(splitRes[2]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[0].value}`);
|
||||
expect(splitRes[3]).to.equal(`habit 1,${tasks[1]._id},habit,${moment(tasks[1].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[1].history[1].value}`);
|
||||
expect(splitRes[4]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[0].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[0].value}`);
|
||||
expect(splitRes[5]).to.equal(`habit 2,${tasks[2]._id},habit,${moment(tasks[2].history[1].date).format('YYYY-MM-DD HH:mm:ss')},${tasks[2].history[1].value}`);
|
||||
expect(splitRes[6]).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,8 @@ import Bluebird from 'bluebird';
|
||||
let parseStringAsync = Bluebird.promisify(xml2js.parseString, {context: xml2js});
|
||||
|
||||
describe('GET /export/userdata.xml', () => {
|
||||
it('should return a valid XML file with user data', async () => {
|
||||
// TODO disabled because it randomly causes the build to fail
|
||||
xit('should return a valid XML file with user data', async () => {
|
||||
let user = await generateUser();
|
||||
let tasks = await user.post('/tasks/user', [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
|
||||
@@ -82,8 +82,10 @@ describe('GET /groups/:groupId/members', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -134,6 +134,22 @@ describe('POST /group/:groupId/join', () => {
|
||||
|
||||
await expect(user.get('/user')).to.eventually.have.deep.property('items.quests.basilist', 1);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${guild._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: guild.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,6 +188,23 @@ describe('POST /group/:groupId/join', () => {
|
||||
await expect(invitedUser.get('/user')).to.eventually.have.deep.property('party._id', party._id);
|
||||
});
|
||||
|
||||
it('notifies inviting user that their invitation was accepted', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
let inviter = await user.get('/user');
|
||||
|
||||
let expectedData = {
|
||||
headerText: t('invitationAcceptedHeader'),
|
||||
bodyText: t('invitationAcceptedBody', {
|
||||
username: invitedUser.auth.local.username,
|
||||
groupName: party.name,
|
||||
}),
|
||||
};
|
||||
|
||||
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
|
||||
expect(inviter.notifications[0].data).to.eql(expectedData);
|
||||
});
|
||||
|
||||
it('clears invitation from user when joining party', async () => {
|
||||
await invitedUser.post(`/groups/${party._id}/join`);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import * as email from '../../../../../website/server/libs/email';
|
||||
|
||||
describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
let leader;
|
||||
@@ -60,6 +61,14 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
});
|
||||
|
||||
context('Guilds', () => {
|
||||
beforeEach(() => {
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
let memberRemoved = await member.get('/user');
|
||||
@@ -80,6 +89,22 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
|
||||
expect(_.findIndex(invitedUserWithoutInvite.invitations.guilds, {id: guild._id})).eql(-1);
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${invitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(invitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('guild-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await leader.post(`/groups/${guild._id}/removeMember/${member._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(member._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-guild');
|
||||
});
|
||||
});
|
||||
|
||||
context('Party', () => {
|
||||
@@ -105,6 +130,11 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
partyInvitedUser = invitees[0];
|
||||
partyMember = members[0];
|
||||
removedMember = members[1];
|
||||
sandbox.spy(email, 'sendTxn');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('can remove other members', async () => {
|
||||
@@ -187,5 +217,21 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
|
||||
expect(party.quest.members[partyLeader._id]).to.be.true;
|
||||
expect(party.quest.members[partyMember._id]).to.not.exist;
|
||||
});
|
||||
|
||||
it('sends email to user with rescinded invite', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyInvitedUser._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyInvitedUser._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('party-invite-rescinded');
|
||||
});
|
||||
|
||||
it('sends email to removed user', async () => {
|
||||
await partyLeader.post(`/groups/${party._id}/removeMember/${partyMember._id}`);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
expect(email.sendTxn.args[0][0]._id).to.be.eql(partyMember._id);
|
||||
expect(email.sendTxn.args[0][1]).to.be.eql('kicked-from-party');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -300,6 +300,26 @@ describe('Post /groups/:groupId/invite', () => {
|
||||
message: t('userAlreadyInGroup'),
|
||||
});
|
||||
});
|
||||
|
||||
// @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 () => {
|
||||
let userToInvite = await generateUser();
|
||||
|
||||
let nonGroupLeader = await generateUser();
|
||||
await inviter.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [nonGroupLeader._id],
|
||||
});
|
||||
await nonGroupLeader.post(`/groups/${group._id}/join`);
|
||||
|
||||
await expect(nonGroupLeader.post(`/groups/${group._id}/invite`, {
|
||||
uuids: [userToInvite._id],
|
||||
}))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanInviteToGroupPlan'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('party invites', () => {
|
||||
|
||||
@@ -40,13 +40,14 @@ describe('PUT /heroes/:heroId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updates contributor level, balance, ads, blocked', async () => {
|
||||
it('change contributor level, balance, ads', async () => {
|
||||
let hero = await generateUser();
|
||||
let prevBlockState = hero.auth.blocked;
|
||||
let prevSleepState = hero.preferences.sleep;
|
||||
let heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
balance: 3,
|
||||
contributor: {level: 1},
|
||||
purchased: {ads: true},
|
||||
auth: {blocked: true},
|
||||
});
|
||||
|
||||
// test response
|
||||
@@ -61,18 +62,47 @@ describe('PUT /heroes/:heroId', () => {
|
||||
expect(heroRes.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
|
||||
expect(heroRes.contributor.level).to.equal(1);
|
||||
expect(heroRes.purchased.ads).to.equal(true);
|
||||
expect(heroRes.auth.blocked).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.balance).to.equal(3 + 0.75); // 3+0.75 for first contrib level
|
||||
expect(hero.contributor.level).to.equal(1);
|
||||
expect(hero.purchased.ads).to.equal(true);
|
||||
expect(hero.auth.blocked).to.equal(true);
|
||||
expect(hero.preferences.sleep).to.equal(true);
|
||||
expect(hero.auth.blocked).to.equal(prevBlockState);
|
||||
expect(hero.preferences.sleep).to.equal(prevSleepState);
|
||||
expect(hero.notifications.length).to.equal(1);
|
||||
expect(hero.notifications[0].type).to.equal('NEW_CONTRIBUTOR_LEVEL');
|
||||
});
|
||||
|
||||
it('block a user', async () => {
|
||||
let hero = await generateUser();
|
||||
let heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
auth: {blocked: true},
|
||||
preferences: {sleep: true},
|
||||
});
|
||||
|
||||
// test response values
|
||||
expect(heroRes.auth.blocked).to.equal(true);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.auth.blocked).to.equal(true);
|
||||
expect(hero.preferences.sleep).to.equal(true);
|
||||
});
|
||||
|
||||
it('unblock a user', async () => {
|
||||
let hero = await generateUser();
|
||||
let prevSleepState = hero.preferences.sleep;
|
||||
let heroRes = await user.put(`/hall/heroes/${hero._id}`, {
|
||||
auth: {blocked: false},
|
||||
});
|
||||
|
||||
// test response values
|
||||
expect(heroRes.auth.blocked).to.equal(false);
|
||||
// test hero values
|
||||
await hero.sync();
|
||||
expect(hero.auth.blocked).to.equal(false);
|
||||
expect(hero.preferences.sleep).to.equal(prevSleepState);
|
||||
});
|
||||
|
||||
it('updates chatRevoked flag', async () => {
|
||||
let hero = await generateUser();
|
||||
|
||||
|
||||
44
test/api/v3/integration/members/GET-achievements.test.js
Normal file
44
test/api/v3/integration/members/GET-achievements.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('GET /members/:memberId/achievements', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('validates req.params.memberId', async () => {
|
||||
await expect(user.get('/members/invalidUUID/achievements')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns achievements based on given user', async () => {
|
||||
let member = await generateUser({
|
||||
contributor: {level: 1},
|
||||
backer: {tier: 3},
|
||||
});
|
||||
let achievementsRes = await user.get(`/members/${member._id}/achievements`);
|
||||
|
||||
expect(achievementsRes.special.achievements.contributor.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.contributor.value).to.equal(1);
|
||||
|
||||
expect(achievementsRes.special.achievements.kickstarter.earned).to.equal(true);
|
||||
expect(achievementsRes.special.achievements.kickstarter.value).to.equal(3);
|
||||
});
|
||||
|
||||
it('handles non-existing members', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.get(`/members/${dummyId}/achievements`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('userWithIDNotFound', {userId: dummyId}),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -35,8 +35,10 @@ describe('GET /members/:memberId', () => {
|
||||
'backer', 'contributor', 'auth', 'items', 'inbox',
|
||||
]);
|
||||
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql(['size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background'].sort());
|
||||
expect(Object.keys(memberRes.preferences).sort()).to.eql([
|
||||
'size', 'hair', 'skin', 'shirt',
|
||||
'chair', 'costume', 'sleep', 'background',
|
||||
].sort());
|
||||
|
||||
expect(memberRes.stats.maxMP).to.exist;
|
||||
expect(memberRes.stats.maxHealth).to.equal(common.maxHealth);
|
||||
|
||||
@@ -4,6 +4,14 @@ import {
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
function findMessage (messages, receiverId) {
|
||||
let message = _.find(messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiverId;
|
||||
});
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
describe('POST /members/transfer-gems', () => {
|
||||
let userToSendMessage;
|
||||
let receiver;
|
||||
@@ -116,19 +124,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
messageSentContent += message;
|
||||
|
||||
@@ -150,19 +153,14 @@ describe('POST /members/transfer-gems', () => {
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === userToSendMessage._id;
|
||||
});
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (inboxMessage) => {
|
||||
return inboxMessage.uuid === receiver._id;
|
||||
});
|
||||
|
||||
let messageSentContent = t('privateMessageGiftIntro', {
|
||||
let messageSentContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
});
|
||||
messageSentContent += t('privateMessageGiftGemsMessage', {gemAmount});
|
||||
messageSentContent = `\`${messageSentContent}\` `;
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
@@ -173,4 +171,40 @@ describe('POST /members/transfer-gems', () => {
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(messageSentContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
|
||||
it('sends transfer gems message in each participant\'s language', async () => {
|
||||
await receiver.update({
|
||||
'preferences.language': 'es',
|
||||
});
|
||||
await userToSendMessage.update({
|
||||
'preferences.language': 'cs',
|
||||
});
|
||||
await userToSendMessage.post('/members/transfer-gems', {
|
||||
gemAmount,
|
||||
toUserId: receiver._id,
|
||||
});
|
||||
|
||||
let updatedReceiver = await receiver.get('/user');
|
||||
let updatedSender = await userToSendMessage.get('/user');
|
||||
|
||||
let sendersMessageInReceiversInbox = findMessage(updatedReceiver.inbox.messages, userToSendMessage._id);
|
||||
let sendersMessageInSendersInbox = findMessage(updatedSender.inbox.messages, receiver._id);
|
||||
|
||||
let [receieversMessageContent, sendersMessageContent] = ['es', 'cs'].map((lang) => {
|
||||
let messageContent = t('privateMessageGiftGemsMessage', {
|
||||
receiverName: receiver.profile.name,
|
||||
senderName: userToSendMessage.profile.name,
|
||||
gemAmount,
|
||||
}, lang);
|
||||
|
||||
return `\`${messageContent}\` `;
|
||||
});
|
||||
|
||||
expect(sendersMessageInReceiversInbox).to.exist;
|
||||
expect(sendersMessageInReceiversInbox.text).to.equal(receieversMessageContent);
|
||||
|
||||
expect(sendersMessageInSendersInbox).to.exist;
|
||||
expect(sendersMessageInSendersInbox.text).to.equal(sendersMessageContent);
|
||||
expect(updatedSender.balance).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('GET /models/:model/paths', () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('returns an error when model is not accessible or doesn\'t exists', async () => {
|
||||
it('returns an error when model is not accessible or doesn\'t exist', async () => {
|
||||
await expect(user.get('/models/1234/paths')).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('POST /notifications/:notificationId/read', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('errors when notification is not found', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.post(`/notifications/${dummyId}/read`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('messageNotificationNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
xit('removes a notification', async () => {
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/amazon/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/paypal/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/paypal/checkout/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #subscribe', () => {
|
||||
let endpoint = '/paypal/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/paypal/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
xdescribe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/paypal/subscribe/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/stripe/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #checkout', () => {
|
||||
let endpoint = '/amazon/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.orderReferenceId',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #createOrderReferenceId', () => {
|
||||
let endpoint = '/amazon/createOrderReferenceId';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies billingAgreementId', async (done) => {
|
||||
try {
|
||||
await user.post(endpoint);
|
||||
} catch (e) {
|
||||
// Parameter AWSAccessKeyId cannot be empty.
|
||||
expect(e.error).to.eql('BadRequest');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #subscribe', () => {
|
||||
let endpoint = '/amazon/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription code', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/paypal/ipn';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
let result = await user.post(endpoint);
|
||||
expect(result).to.eql('OK');
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/stripe/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
message: 'Invalid API Key provided: ****************************1111',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/stripe/subscribe/edit';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments : amazon #subscribeCancel', () => {
|
||||
let endpoint = '/amazon/subscribe/cancel?noRedirect=true';
|
||||
let user, group, amazonSubscribeCancelStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('throws error when there users has no subscription', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(() => {
|
||||
amazonSubscribeCancelStub = sinon.stub(amzLib, 'cancelSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}&groupId=${group._id}`);
|
||||
|
||||
expect(amazonSubscribeCancelStub).to.be.calledOnce;
|
||||
expect(amazonSubscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].groupId).to.eql(group._id);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonSubscribeCancelStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments - amazon - #checkout', () => {
|
||||
let endpoint = '/amazon/checkout';
|
||||
let user, amazonCheckoutStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.orderReferenceId',
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
amazonCheckoutStub = sinon.stub(amzLib, 'checkout').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.checkout.restore();
|
||||
});
|
||||
|
||||
it('makes a purcahse with amazon checkout', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
let gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 16,
|
||||
uuid: user._id,
|
||||
},
|
||||
};
|
||||
|
||||
let orderReferenceId = 'orderReferenceId-example';
|
||||
|
||||
await user.post(endpoint, {
|
||||
gift,
|
||||
orderReferenceId,
|
||||
});
|
||||
|
||||
expect(amazonCheckoutStub).to.be.calledOnce;
|
||||
expect(amazonCheckoutStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(amazonCheckoutStub.args[0][0].gift).to.eql(gift);
|
||||
expect(amazonCheckoutStub.args[0][0].orderReferenceId).to.eql(orderReferenceId);
|
||||
expect(amazonCheckoutStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(amazonCheckoutStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments - amazon - #createOrderReferenceId', () => {
|
||||
let endpoint = '/amazon/createOrderReferenceId';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies billingAgreementId', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Missing req.body.billingAgreementId',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import amzLib from '../../../../../../website/server/libs/amazonPayments';
|
||||
|
||||
describe('payments - amazon - #subscribe', () => {
|
||||
let endpoint = '/amazon/subscribe';
|
||||
let user, group, subscribeWithAmazonStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies subscription code', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let billingAgreementId = 'billingAgreementId-example';
|
||||
let subscription = 'basic_3mo';
|
||||
let coupon;
|
||||
|
||||
beforeEach(() => {
|
||||
subscribeWithAmazonStub = sinon.stub(amzLib, 'subscribe').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
amzLib.subscribe.restore();
|
||||
});
|
||||
|
||||
it('creates a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
billingAgreementId,
|
||||
subscription,
|
||||
coupon,
|
||||
});
|
||||
|
||||
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||
expect(subscribeWithAmazonStub.args[0][0].groupId).not.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
|
||||
it('creates a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
billingAgreementId,
|
||||
subscription,
|
||||
coupon,
|
||||
groupId: group._id,
|
||||
});
|
||||
|
||||
expect(subscribeWithAmazonStub).to.be.calledOnce;
|
||||
expect(subscribeWithAmazonStub.args[0][0].billingAgreementId).to.eql(billingAgreementId);
|
||||
expect(subscribeWithAmazonStub.args[0][0].sub).to.exist;
|
||||
expect(subscribeWithAmazonStub.args[0][0].coupon).to.eql(coupon);
|
||||
expect(subscribeWithAmazonStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeWithAmazonStub.args[0][0].groupId).to.eql(group._id);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-key']).to.eql(user.apiToken);
|
||||
expect(subscribeWithAmazonStub.args[0][0].headers['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('payments : amazon', () => {
|
||||
let endpoint = '/amazon/verifyAccessToken';
|
||||
@@ -0,0 +1,40 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
|
||||
describe('payments : google #cancelSubscribe', () => {
|
||||
let endpoint = '/iap/android/subscribe/cancel?noRedirect=true';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let cancelStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
cancelStub = sinon.stub(googlePayments, 'cancelSubscribe').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
googlePayments.cancelSubscribe.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(cancelStub).to.be.calledOnce;
|
||||
expect(cancelStub.args[0][0]._id).to.eql(user._id);
|
||||
expect(cancelStub.args[0][1]['x-api-key']).to.eql(user.apiToken);
|
||||
expect(cancelStub.args[0][1]['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import {generateUser, translate as t} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
|
||||
describe('payments : google #subscribe', () => {
|
||||
let endpoint = '/iap/android/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies sub key', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubscriptionCode'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let subscribeStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeStub = sinon.stub(googlePayments, 'subscribe').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
googlePayments.subscribe.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
let sku = 'com.habitrpg.android.habitica.subscription.3month';
|
||||
|
||||
await user.post(endpoint, {
|
||||
sku,
|
||||
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||
});
|
||||
|
||||
expect(subscribeStub).to.be.calledOnce;
|
||||
expect(subscribeStub.args[0][0]).to.eql(sku);
|
||||
expect(subscribeStub.args[0][1]._id).to.eql(user._id);
|
||||
expect(subscribeStub.args[0][2]).to.eql('receipt');
|
||||
expect(subscribeStub.args[0][3]).to.eql('signature');
|
||||
expect(subscribeStub.args[0][4]['x-api-key']).to.eql(user.apiToken);
|
||||
expect(subscribeStub.args[0][4]['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import {generateUser} from '../../../../../helpers/api-integration/v3';
|
||||
import googlePayments from '../../../../../../website/server/libs/googlePayments';
|
||||
|
||||
describe('payments : google #verify', () => {
|
||||
let endpoint = '/iap/android/verify';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let verifyStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
verifyStub = sinon.stub(googlePayments, 'verifyGemPurchase').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
googlePayments.verifyGemPurchase.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
user = await generateUser({
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
transaction: {receipt: 'receipt', signature: 'signature'},
|
||||
});
|
||||
|
||||
expect(verifyStub).to.be.calledOnce;
|
||||
expect(verifyStub.args[0][0]._id).to.eql(user._id);
|
||||
expect(verifyStub.args[0][1]).to.eql('receipt');
|
||||
expect(verifyStub.args[0][2]).to.eql('signature');
|
||||
expect(verifyStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
|
||||
expect(verifyStub.args[0][3]['x-api-user']).to.eql(user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #checkout', () => {
|
||||
let endpoint = '/paypal/checkout';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let checkoutStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
checkoutStub = sinon.stub(paypalPayments, 'checkout').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.checkout.restore();
|
||||
});
|
||||
|
||||
it('creates a purchase link', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(checkoutStub).to.be.calledOnce;
|
||||
expect(checkoutStub.args[0][0].gift).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #checkoutSuccess', () => {
|
||||
let endpoint = '/paypal/checkout/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies paymentId', async () => {
|
||||
await expect(user.get(endpoint))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingPaymentId'),
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies customerId', async () => {
|
||||
await expect(user.get(`${endpoint}?paymentId=test-paymentid`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingCustomerId'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let checkoutSuccessStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
checkoutSuccessStub = sinon.stub(paypalPayments, 'checkoutSuccess').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.checkoutSuccess.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
let paymentId = 'test-paymentid';
|
||||
let customerId = 'test-customerId';
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?PayerID=${customerId}&paymentId=${paymentId}`);
|
||||
|
||||
expect(checkoutSuccessStub).to.be.calledOnce;
|
||||
|
||||
expect(checkoutSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(checkoutSuccessStub.args[0][0].gift).to.eql(undefined);
|
||||
expect(checkoutSuccessStub.args[0][0].paymentId).to.eql(paymentId);
|
||||
expect(checkoutSuccessStub.args[0][0].customerId).to.eql(customerId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
import shared from '../../../../../../website/common';
|
||||
|
||||
describe('payments : paypal #subscribe', () => {
|
||||
let endpoint = '/paypal/subscribe';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies sub key', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingSubKey'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let subscribeStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeStub = sinon.stub(paypalPayments, 'subscribe').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribe.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
let subKey = 'basic_3mo';
|
||||
let sub = shared.content.subscriptionBlocks[subKey];
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?sub=${subKey}`);
|
||||
|
||||
expect(subscribeStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeStub.args[0][0].sub).to.eql(sub);
|
||||
expect(subscribeStub.args[0][0].coupon).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #subscribeCancel', () => {
|
||||
let endpoint = '/paypal/subscribe/cancel';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let subscribeCancelStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeCancelStub = sinon.stub(paypalPayments, 'subscribeCancel').returnsPromise().resolves('/');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribeCancel.restore();
|
||||
});
|
||||
|
||||
it('cancels a subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(endpoint);
|
||||
|
||||
expect(subscribeCancelStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeCancelStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeCancelStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments : paypal #subscribeSuccess', () => {
|
||||
let endpoint = '/paypal/subscribe/success';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies Paypal Block', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('missingPaypalBlock'),
|
||||
});
|
||||
});
|
||||
|
||||
xdescribe('success', () => {
|
||||
let subscribeSuccessStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
subscribeSuccessStub = sinon.stub(paypalPayments, 'subscribeSuccess').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.subscribeSuccess.restore();
|
||||
});
|
||||
|
||||
it('creates a subscription', async () => {
|
||||
let token = 'test-token';
|
||||
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}?token=${token}`);
|
||||
|
||||
expect(subscribeSuccessStub).to.be.calledOnce;
|
||||
|
||||
expect(subscribeSuccessStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(subscribeSuccessStub.args[0][0].block).to.eql(undefined);
|
||||
expect(subscribeSuccessStub.args[0][0].groupId).to.eql(undefined);
|
||||
expect(subscribeSuccessStub.args[0][0].token).to.eql(token);
|
||||
expect(subscribeSuccessStub.args[0][0].headers).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import paypalPayments from '../../../../../../website/server/libs/paypalPayments';
|
||||
|
||||
describe('payments - paypal - #ipn', () => {
|
||||
let endpoint = '/paypal/ipn';
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
let result = await user.post(endpoint);
|
||||
expect(result).to.eql('OK');
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let ipnStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
ipnStub = sinon.stub(paypalPayments, 'ipn').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
paypalPayments.ipn.restore();
|
||||
});
|
||||
|
||||
it('makes a purchase', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(ipnStub).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #subscribeCancel', () => {
|
||||
let endpoint = '/stripe/subscribe/cancel?redirect=none';
|
||||
let user, group, stripeCancelSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.get(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach(async () => {
|
||||
stripeCancelSubscriptionStub = sinon.stub(stripePayments, 'cancelSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}`);
|
||||
|
||||
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.get(`${endpoint}&groupId=${group._id}`);
|
||||
|
||||
expect(stripeCancelSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCancelSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #checkout', () => {
|
||||
let endpoint = '/stripe/checkout';
|
||||
let user, group;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint, {id: 123})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'Error',
|
||||
message: 'Invalid API Key provided: ****************************1111',
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let stripeCheckoutSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
stripeCheckoutSubscriptionStub = sinon.stub(stripePayments, 'checkout').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.checkout.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(`${endpoint}?groupId=${group._id}`);
|
||||
|
||||
expect(stripeCheckoutSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeCheckoutSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import stripePayments from '../../../../../../website/server/libs/stripePayments';
|
||||
|
||||
describe('payments - stripe - #subscribeEdit', () => {
|
||||
let endpoint = '/stripe/subscribe/edit';
|
||||
let user, group;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('verifies credentials', async () => {
|
||||
await expect(user.post(endpoint)).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('missingSubscription'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
let stripeEditSubscriptionStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
stripeEditSubscriptionStub = sinon.stub(stripePayments, 'editSubscription').returnsPromise().resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
stripePayments.editSubscription.restore();
|
||||
});
|
||||
|
||||
it('cancels a user subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
await user.post(endpoint);
|
||||
|
||||
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('cancels a group subscription', async () => {
|
||||
user = await generateUser({
|
||||
'profile.name': 'sender',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
balance: 2,
|
||||
});
|
||||
|
||||
group = await generateGroup(user, {
|
||||
name: 'test group',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
'purchased.plan.customerId': 'customer-id',
|
||||
'purchased.plan.planId': 'basic_3mo',
|
||||
'purchased.plan.lastBillingDate': new Date(),
|
||||
});
|
||||
|
||||
await user.post(endpoint, {
|
||||
groupId: group._id,
|
||||
});
|
||||
|
||||
expect(stripeEditSubscriptionStub).to.be.calledOnce;
|
||||
expect(stripeEditSubscriptionStub.args[0][0].user._id).to.eql(user._id);
|
||||
expect(stripeEditSubscriptionStub.args[0][0].groupId).to.eql(group._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -148,5 +148,20 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||
});
|
||||
|
||||
it('begins the quest if accepting the last pending invite and verifies chat', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
// quest will start after everyone has accepted
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
await questingGroup.sync();
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -231,5 +231,22 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
||||
expect(questingGroup.quest.members[partyMembers[0]._id]).to.exist;
|
||||
expect(questingGroup.quest.members[leader._id]).to.exist;
|
||||
});
|
||||
|
||||
it('allows group leader to force start quest and verifies chat', async () => {
|
||||
let questLeader = partyMembers[0];
|
||||
await questLeader.update({[`items.quests.${PET_QUEST}`]: 1});
|
||||
await questLeader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
|
||||
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,5 +188,25 @@ describe('POST /groups/:groupId/quests/invite/:questKey', () => {
|
||||
|
||||
expect(group.quest.active).to.eql(true);
|
||||
});
|
||||
|
||||
it('starts quest automatically if user is in a solo party and verifies chat', async () => {
|
||||
let leaderDetails = { balance: 10 };
|
||||
leaderDetails[`items.quests.${PET_QUEST}`] = 1;
|
||||
let { group, groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: { type: 'party', privacy: 'private' },
|
||||
leaderDetails,
|
||||
});
|
||||
|
||||
await groupLeader.post(`/groups/${group._id}/quests/invite/${PET_QUEST}`);
|
||||
|
||||
await group.sync();
|
||||
|
||||
expect(group.chat[0].text).to.exist;
|
||||
expect(group.chat[0]._meta).to.exist;
|
||||
expect(group.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await groupLeader.get(`/groups/${group._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
|
||||
describe('POST /groups/:groupId/quests/abort', () => {
|
||||
let questingGroup;
|
||||
@@ -89,6 +90,8 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
|
||||
let stub = sandbox.stub(Group.prototype, 'sendChat');
|
||||
|
||||
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
|
||||
await Promise.all([
|
||||
leader.sync(),
|
||||
@@ -123,5 +126,9 @@ describe('POST /groups/:groupId/quests/abort', () => {
|
||||
},
|
||||
members: {},
|
||||
});
|
||||
expect(Group.prototype.sendChat).to.be.calledOnce;
|
||||
expect(Group.prototype.sendChat).to.be.calledWithMatch(/aborted the party quest Wail of the Whale.`/);
|
||||
|
||||
stub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,5 +180,19 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
||||
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||
});
|
||||
|
||||
it('starts the quest when the last user reject and verifies chat', async () => {
|
||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
|
||||
await questingGroup.sync();
|
||||
|
||||
expect(questingGroup.chat[0].text).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.exist;
|
||||
expect(questingGroup.chat[0]._meta).to.have.all.keys(['participatingMembers']);
|
||||
|
||||
let returnedGroup = await leader.get(`/groups/${questingGroup._id}`);
|
||||
expect(returnedGroup.chat[0]._meta).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('GET /shops/seasonal', () => {
|
||||
|
||||
expect(shop.identifier).to.equal('seasonalShop');
|
||||
expect(shop.text).to.eql(t('seasonalShop'));
|
||||
expect(shop.notes).to.eql(t('seasonalShopFallText'));
|
||||
expect(shop.notes).to.be.a('string');
|
||||
expect(shop.imageName).to.be.a('string');
|
||||
expect(shop.categories).to.be.an('array');
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('GET /tasks/user', () => {
|
||||
for (let i = 0; i < numberOfTodos; i++) {
|
||||
let id = todos[i]._id;
|
||||
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
await user.sync();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /tasks/clearCompletedTodos', () => {
|
||||
it('deletes all completed todos except the ones from a challenge', async () => {
|
||||
it('deletes all completed todos except the ones from a challenge and group', async () => {
|
||||
let user = await generateUser({balance: 1});
|
||||
let guild = await generateGroup(user);
|
||||
let challenge = await generateChallenge(user, guild);
|
||||
@@ -24,12 +24,18 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
||||
type: 'todo',
|
||||
});
|
||||
|
||||
let groupTask = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'todo 7',
|
||||
type: 'todo',
|
||||
});
|
||||
await user.post(`/tasks/${groupTask._id}/assign/${user._id}`);
|
||||
|
||||
let tasks = await user.get('/tasks/user?type=todos');
|
||||
expect(tasks.length).to.equal(initialTodoCount + 6);
|
||||
expect(tasks.length).to.equal(initialTodoCount + 7);
|
||||
|
||||
for (let task of tasks) {
|
||||
if (['todo 2', 'todo 3', 'todo 6'].indexOf(task.text) !== -1) {
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line babel/no-await-in-loop
|
||||
await user.post(`/tasks/${task._id}/score/up`); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +43,7 @@ describe('POST /tasks/clearCompletedTodos', () => {
|
||||
let completedTodos = await user.get('/tasks/user?type=completedTodos');
|
||||
let todos = await user.get('/tasks/user?type=todos');
|
||||
let allTodos = todos.concat(completedTodos);
|
||||
expect(allTodos.length).to.equal(initialTodoCount + 4); // + 6 - 3 completed (but one is from challenge)
|
||||
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6');
|
||||
expect(allTodos.length).to.equal(initialTodoCount + 5); // + 7 - 3 completed (but one is from challenge)
|
||||
expect(allTodos[allTodos.length - 1].text).to.equal('todo 6'); // last completed todo
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,12 +5,17 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/challenge/:challengeId', () => {
|
||||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
|
||||
function findUserChallengeTask (memberTask) {
|
||||
return memberTask.challenge.id === challenge._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({balance: 1});
|
||||
guild = await generateGroup(user);
|
||||
@@ -88,6 +93,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.habits.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test habit');
|
||||
@@ -95,6 +103,8 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.type).to.eql('habit');
|
||||
expect(task.up).to.eql(false);
|
||||
expect(task.down).to.eql(true);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a todo', async () => {
|
||||
@@ -105,11 +115,16 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.todos.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test todo');
|
||||
expect(task.notes).to.eql('1976');
|
||||
expect(task.type).to.eql('todo');
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
||||
it('creates a daily', async () => {
|
||||
@@ -124,6 +139,9 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
});
|
||||
let challengeWithTask = await user.get(`/challenges/${challenge._id}`);
|
||||
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
let userChallengeTask = find(memberTasks, findUserChallengeTask);
|
||||
|
||||
expect(challengeWithTask.tasksOrder.dailys.indexOf(task._id)).to.be.above(-1);
|
||||
expect(task.challenge.id).to.equal(challenge._id);
|
||||
expect(task.text).to.eql('test daily');
|
||||
@@ -132,5 +150,7 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
||||
expect(task.frequency).to.eql('daily');
|
||||
expect(task.everyX).to.eql(5);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,4 +69,48 @@ describe('DELETE /tasks/:id', () => {
|
||||
expect(syncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
expect(member2SyncedTask.group.broken).to.equal('TASK_DELETED');
|
||||
});
|
||||
|
||||
it('prevents a user from deleting a task they are assigned to', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.del(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('cantDeleteAssignedGroupTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to delete a broken task', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await user.del(`/tasks/${task._id}`);
|
||||
|
||||
await member.del(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Task not found.',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to delete a task after leaving a group', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await member.post(`/groups/${guild._id}/leave`);
|
||||
|
||||
await member.del(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await expect(member.get(`/tasks/${syncedTask._id}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: 'Task not found.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('GET /approvals/group/:groupId', () => {
|
||||
let user, guild, member, task, syncedTask;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
try {
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-empty
|
||||
}
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await expect(member.get(`/approvals/group/${guild._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a list of task that need approval', async () => {
|
||||
let approvals = await user.get(`/approvals/group/${guild._id}`);
|
||||
expect(approvals[0]._id).to.equal(syncedTask._id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/approve/:userId', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not assigned', async () => {
|
||||
await expect(user.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('taskNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('errors when user is not the group leader', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await expect(member.post(`/tasks/${task._id}/approve/${member._id}`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('onlyGroupLeaderCanEditTasks'),
|
||||
});
|
||||
});
|
||||
|
||||
it('approves an assigned user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await member.sync();
|
||||
|
||||
expect(member.notifications.length).to.equal(2);
|
||||
expect(member.notifications[0].type).to.equal('GROUP_TASK_APPROVED');
|
||||
expect(member.notifications[0].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
expect(member.notifications[1].type).to.equal('SCORED_TASK');
|
||||
expect(member.notifications[1].data.message).to.equal(t('yourTaskHasBeenApproved', {taskText: task.text}));
|
||||
|
||||
expect(syncedTask.group.approval.approved).to.be.true;
|
||||
expect(syncedTask.group.approval.approvingUser).to.equal(user._id);
|
||||
expect(syncedTask.group.approval.dateApproved).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /tasks/:id/score/:direction', () => {
|
||||
let user, guild, member, task;
|
||||
|
||||
function findAssignedTask (memberTask) {
|
||||
return memberTask.group.id === guild._id;
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
let {group, members, groupLeader} = await createAndPopulateGroup({
|
||||
groupDetails: {
|
||||
name: 'Test Guild',
|
||||
type: 'guild',
|
||||
},
|
||||
members: 1,
|
||||
});
|
||||
|
||||
guild = group;
|
||||
user = groupLeader;
|
||||
member = members[0];
|
||||
|
||||
task = await user.post(`/tasks/group/${guild._id}`, {
|
||||
text: 'test todo',
|
||||
type: 'todo',
|
||||
requiresApproval: true,
|
||||
});
|
||||
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
});
|
||||
|
||||
it('prevents user from scoring a task that needs to be approved', async () => {
|
||||
await user.update({
|
||||
'preferences.language': 'cs',
|
||||
});
|
||||
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
expect(user.notifications[0].type).to.equal('GROUP_TASK_APPROVAL');
|
||||
expect(user.notifications[0].data.message).to.equal(t('userHasRequestedTaskApproval', {
|
||||
user: member.auth.local.username,
|
||||
taskName: updatedTask.text,
|
||||
}, 'cs')); // This test only works if we have the notification translated
|
||||
expect(user.notifications[0].data.groupId).to.equal(guild._id);
|
||||
|
||||
expect(updatedTask.group.approval.requested).to.equal(true);
|
||||
expect(updatedTask.group.approval.requestedDate).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
|
||||
it('errors when approval has already been requested', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskApprovalHasBeenRequested'),
|
||||
});
|
||||
|
||||
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('taskRequiresApproval'),
|
||||
});
|
||||
});
|
||||
|
||||
it('allows a user to score an apporoved task', async () => {
|
||||
let memberTasks = await member.get('/tasks/user');
|
||||
let syncedTask = find(memberTasks, findAssignedTask);
|
||||
|
||||
await user.post(`/tasks/${task._id}/approve/${member._id}`);
|
||||
|
||||
await member.post(`/tasks/${syncedTask._id}/score/up`);
|
||||
let updatedTask = await member.get(`/tasks/${syncedTask._id}`);
|
||||
|
||||
expect(updatedTask.completed).to.equal(true);
|
||||
expect(updatedTask.dateCompleted).to.be.a('string'); // date gets converted to a string as json doesn't have a Date type
|
||||
});
|
||||
});
|
||||
@@ -82,7 +82,7 @@ describe('POST /tasks/:taskId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('allows user to assign themselves', async () => {
|
||||
it('allows user to assign themselves (claim)', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let groupTask = await user.get(`/tasks/group/${guild._id}`);
|
||||
@@ -93,6 +93,15 @@ describe('POST /tasks/:taskId', () => {
|
||||
expect(syncedTask).to.exist;
|
||||
});
|
||||
|
||||
it('sends a message to the group when a user claims a task', async () => {
|
||||
await member.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
let updateGroup = await user.get(`/groups/${guild._id}`);
|
||||
|
||||
expect(updateGroup.chat[0].text).to.equal(t('userIsClamingTask', {username: member.profile.name, task: task.text}));
|
||||
expect(updateGroup.chat[0].uuid).to.equal('system');
|
||||
});
|
||||
|
||||
it('assigns a task to a user', async () => {
|
||||
await user.post(`/tasks/${task._id}/assign/${member._id}`);
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
generateUser,
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
describe('POST group-tasks/:taskId/move/to/:position', () => {
|
||||
let user, guild;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({balance: 1});
|
||||
guild = await generateGroup(user, {type: 'guild'});
|
||||
});
|
||||
|
||||
it('can move task to new position', async () => {
|
||||
let tasks = await user.post(`/tasks/group/${guild._id}`, [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'habit', text: 'habit 2'},
|
||||
{type: 'daily', text: 'daily 1'},
|
||||
{type: 'habit', text: 'habit 3'},
|
||||
{type: 'habit', text: 'habit 4'},
|
||||
{type: 'todo', text: 'todo 1'},
|
||||
{type: 'habit', text: 'habit 5'},
|
||||
]);
|
||||
|
||||
let taskToMove = tasks[1];
|
||||
expect(taskToMove.text).to.equal('habit 2');
|
||||
let newOrder = await user.post(`/group-tasks/${tasks[1]._id}/move/to/3`);
|
||||
expect(newOrder[3]).to.equal(taskToMove._id);
|
||||
expect(newOrder.length).to.equal(5);
|
||||
});
|
||||
|
||||
it('can push to bottom', async () => {
|
||||
let tasks = await user.post(`/tasks/group/${guild._id}`, [
|
||||
{type: 'habit', text: 'habit 1'},
|
||||
{type: 'habit', text: 'habit 2'},
|
||||
{type: 'daily', text: 'daily 1'},
|
||||
{type: 'habit', text: 'habit 3'},
|
||||
{type: 'habit', text: 'habit 4'},
|
||||
{type: 'todo', text: 'todo 1'},
|
||||
{type: 'habit', text: 'habit 5'},
|
||||
]);
|
||||
|
||||
let taskToMove = tasks[1];
|
||||
expect(taskToMove.text).to.equal('habit 2');
|
||||
let newOrder = await user.post(`/group-tasks/${tasks[1]._id}/move/to/-1`);
|
||||
expect(newOrder[4]).to.equal(taskToMove._id);
|
||||
expect(newOrder.length).to.equal(5);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,10 @@ import {
|
||||
map,
|
||||
} from 'lodash';
|
||||
import Bluebird from 'bluebird';
|
||||
import {
|
||||
sha1MakeSalt,
|
||||
sha1Encrypt as sha1EncryptPassword,
|
||||
} from '../../../../../website/server/libs/password';
|
||||
|
||||
describe('DELETE /user', () => {
|
||||
let user;
|
||||
@@ -67,6 +71,30 @@ describe('DELETE /user', () => {
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
});
|
||||
|
||||
it('deletes the user with a legacy sha1 password', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
await user.update({
|
||||
'auth.local.hashed_password': sha1HashedPassword,
|
||||
'auth.local.passwordHashMethod': 'sha1',
|
||||
'auth.local.salt': salt,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||
expect(user.auth.local.salt).to.equal(salt);
|
||||
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||
|
||||
// delete the user
|
||||
await user.del('/user', {
|
||||
password: textPassword,
|
||||
});
|
||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
||||
});
|
||||
|
||||
context('last member of a party', () => {
|
||||
let party;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ describe('GET /user', () => {
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -62,15 +62,6 @@ describe('POST /user/buy/:key', () => {
|
||||
await user.post(`/user/buy/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
armor_warrior_1: true,
|
||||
eyewear_special_blackTopFrame: true,
|
||||
eyewear_special_blueTopFrame: true,
|
||||
eyewear_special_greenTopFrame: true,
|
||||
eyewear_special_pinkTopFrame: true,
|
||||
eyewear_special_redTopFrame: true,
|
||||
eyewear_special_whiteTopFrame: true,
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
});
|
||||
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,15 +31,6 @@ describe('POST /user/buy-gear/:key', () => {
|
||||
await user.post(`/user/buy-gear/${key}`);
|
||||
await user.sync();
|
||||
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
armor_warrior_1: true,
|
||||
eyewear_special_blackTopFrame: true,
|
||||
eyewear_special_blueTopFrame: true,
|
||||
eyewear_special_greenTopFrame: true,
|
||||
eyewear_special_pinkTopFrame: true,
|
||||
eyewear_special_redTopFrame: true,
|
||||
eyewear_special_whiteTopFrame: true,
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
});
|
||||
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,13 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
createAndPopulateGroup,
|
||||
generateGroup,
|
||||
generateChallenge,
|
||||
sleep,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /user/class/cast/:spellId', () => {
|
||||
let user;
|
||||
@@ -120,6 +122,31 @@ describe('POST /user/class/cast/:spellId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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' },
|
||||
@@ -160,6 +187,40 @@ describe('POST /user/class/cast/:spellId', () => {
|
||||
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(`/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);
|
||||
});
|
||||
|
||||
// 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 === \'task\'');
|
||||
|
||||
@@ -20,6 +20,6 @@ describe('POST /user/purchase-hourglass/:type/:key', () => {
|
||||
|
||||
expect(response.message).to.eql(t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({'MantisShrimp-Base': 5});
|
||||
expect(user.items.pets['MantisShrimp-Base']).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
generateChallenge,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { find } from 'lodash';
|
||||
|
||||
describe('POST /user/reset', () => {
|
||||
let user;
|
||||
@@ -86,19 +87,34 @@ describe('POST /user/reset', () => {
|
||||
expect(user.tasksOrder.rewards).to.be.empty;
|
||||
});
|
||||
|
||||
it('does not delete challenge tasks', async () => {
|
||||
it('does not delete challenge or group tasks', async () => {
|
||||
let guild = await generateGroup(user);
|
||||
let challenge = await generateChallenge(user, guild);
|
||||
let task = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||
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 userChallengeTask = await user.get(`/tasks/${task._id}`);
|
||||
let memberTasks = await user.get('/tasks/user');
|
||||
|
||||
expect(userChallengeTask).to.eql(task);
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,32 @@ describe('PUT /user', () => {
|
||||
expect(user.preferences.costume).to.eql(true);
|
||||
expect(user.stats.hp).to.eql(14);
|
||||
});
|
||||
|
||||
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: 'User validation failed',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Top Level Protected Operations', () => {
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
encrypt,
|
||||
} from '../../../../../../website/server/libs/encryption';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import superagent from 'superagent';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const API_TEST_SERVER_PORT = nconf.get('PORT');
|
||||
|
||||
describe('GET /user/auth/local/reset-password-set-new-one', () => {
|
||||
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
|
||||
|
||||
// Tests to validate the validatePasswordResetCodeAndFindUser function
|
||||
|
||||
it('renders an error page if the code is missing', async () => {
|
||||
try {
|
||||
await superagent.get(endpoint);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code is invalid json', async () => {
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=invalid`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code cannot be decrypted', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
try {
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code is expired', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().subtract({minutes: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the user does not exist', async () => {
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: Date.now().toString(),
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the user has no local auth', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
auth: 'not an object with valid fields',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': 'invalid',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.get(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
it('returns the password reset page if the password reset code is valid', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await superagent.get(`${endpoint}?code=${code}`);
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
import {
|
||||
encrypt,
|
||||
} from '../../../../../../website/server/libs/encryption';
|
||||
import {
|
||||
compare,
|
||||
bcryptCompare,
|
||||
sha1MakeSalt,
|
||||
sha1Encrypt as sha1EncryptPassword,
|
||||
} from '../../../../../../website/server/libs/password';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import superagent from 'superagent';
|
||||
import nconf from 'nconf';
|
||||
|
||||
const API_TEST_SERVER_PORT = nconf.get('PORT');
|
||||
|
||||
describe('POST /user/auth/local/reset-password-set-new-one', () => {
|
||||
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
|
||||
|
||||
// Tests to validate the validatePasswordResetCodeAndFindUser function
|
||||
|
||||
it('renders an error page if the code is missing', async () => {
|
||||
try {
|
||||
await superagent.post(endpoint);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code is invalid json', async () => {
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=invalid`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code cannot be decrypted', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
try {
|
||||
let code = JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
});
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code is expired', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().subtract({minutes: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the user does not exist', async () => {
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: Date.now().toString(),
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the user has no local auth', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
auth: 'not an object with valid fields',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': 'invalid',
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
it('renders the error page if the new password is missing', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent.post(`${endpoint}?code=${code}`);
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders the error page if the password confirmation is missing', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({newPassword: 'my new password'});
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders the error page if the password confirmation does not match', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
try {
|
||||
await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'not matching',
|
||||
});
|
||||
throw new Error('Request should fail.');
|
||||
} catch (err) {
|
||||
expect(err.status).to.equal(401);
|
||||
}
|
||||
});
|
||||
|
||||
it('renders the success page and save the user', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
});
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordResetCode).to.equal(undefined);
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||
expect(user.auth.local.salt).to.be.undefined;
|
||||
let isPassValid = await compare(user, 'my new password');
|
||||
expect(isPassValid).to.equal(true);
|
||||
});
|
||||
|
||||
it('renders the success page and convert the password from sha1 to bcrypt', async () => {
|
||||
let user = await generateUser();
|
||||
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
await user.update({
|
||||
'auth.local.hashed_password': sha1HashedPassword,
|
||||
'auth.local.passwordHashMethod': 'sha1',
|
||||
'auth.local.salt': salt,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||
expect(user.auth.local.salt).to.equal(salt);
|
||||
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
}));
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await superagent
|
||||
.post(`${endpoint}?code=${code}`)
|
||||
.send({
|
||||
newPassword: 'my new password',
|
||||
confirmPassword: 'my new password',
|
||||
});
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordResetCode).to.equal(undefined);
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||
expect(user.auth.local.salt).to.be.undefined;
|
||||
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare('my new password', user.auth.local.hashed_password);
|
||||
expect(isValidPassword).to.equal(true);
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,11 @@ import {
|
||||
requester,
|
||||
translate as t,
|
||||
} from '../../../../../helpers/api-integration/v3';
|
||||
import {
|
||||
bcryptCompare,
|
||||
sha1MakeSalt,
|
||||
sha1Encrypt as sha1EncryptPassword,
|
||||
} from '../../../../../../website/server/libs/password';
|
||||
|
||||
describe('POST /user/auth/local/login', () => {
|
||||
let api;
|
||||
@@ -72,4 +77,35 @@ describe('POST /user/auth/local/login', () => {
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
await user.update({
|
||||
'auth.local.hashed_password': sha1HashedPassword,
|
||||
'auth.local.passwordHashMethod': 'sha1',
|
||||
'auth.local.salt': salt,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
|
||||
expect(user.auth.local.salt).to.equal(salt);
|
||||
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
|
||||
|
||||
// login
|
||||
await api.post(endpoint, {
|
||||
username: user.auth.local.email,
|
||||
password: textPassword,
|
||||
});
|
||||
|
||||
await user.sync();
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||
expect(user.auth.local.salt).to.be.undefined;
|
||||
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
|
||||
expect(isValidPassword).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user