mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-27 11:12:28 +01:00
Compare commits
415 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf3a092f06 | ||
|
|
ae9bca3ac7 | ||
|
|
194f2d512a | ||
|
|
7d78a7b320 | ||
|
|
2362976f14 | ||
|
|
99b63456c8 | ||
|
|
e0b8cfbaa7 | ||
|
|
4ca4fd9ae7 | ||
|
|
bb84b6f6c2 | ||
|
|
451df7d50d | ||
|
|
3a359cc057 | ||
|
|
66b2b47f87 | ||
|
|
8992542125 | ||
|
|
23bd51d486 | ||
|
|
85c979a22d | ||
|
|
ed02913e9d | ||
|
|
84ecb7c701 | ||
|
|
dbd283514c | ||
|
|
12f802709b | ||
|
|
a504f69b8a | ||
|
|
ca8e6c74f6 | ||
|
|
b9dbc0f829 | ||
|
|
789248a8a4 | ||
|
|
eaad244181 | ||
|
|
a859dbd646 | ||
|
|
106290a11e | ||
|
|
3fa0bac36f | ||
|
|
b40dee9e68 | ||
|
|
6a9ffae758 | ||
|
|
f1ce7f23c3 | ||
|
|
f093634cb2 | ||
|
|
5f0672f3a6 | ||
|
|
eca1d8501c | ||
|
|
3fc0fec95e | ||
|
|
f4f1eac92d | ||
|
|
0e679f0fea | ||
|
|
d55d3fc56d | ||
|
|
9a6347afb6 | ||
|
|
20fa676fae | ||
|
|
4e627aad41 | ||
|
|
27d8ad32e6 | ||
|
|
39fd60267c | ||
|
|
645f40f7e0 | ||
|
|
2842087a43 | ||
|
|
3c4d8949de | ||
|
|
2a7f4c0551 | ||
|
|
5212e72e77 | ||
|
|
d49c89eff8 | ||
|
|
dc303fb1de | ||
|
|
1883a0aca3 | ||
|
|
06339a1504 | ||
|
|
71b5db1461 | ||
|
|
8420a2cf23 | ||
|
|
67b19f0658 | ||
|
|
0f34103a58 | ||
|
|
e2e320a3b6 | ||
|
|
2d4516eaa7 | ||
|
|
1c7e1e5aff | ||
|
|
44a7a0159b | ||
|
|
2091c407b0 | ||
|
|
81ce84db31 | ||
|
|
319621d4fe | ||
|
|
45f7be0266 | ||
|
|
035a7f124b | ||
|
|
d0ba8b7d30 | ||
|
|
a32622c81f | ||
|
|
5e4efb71a6 | ||
|
|
55c4e37f68 | ||
|
|
b441cfa9f3 | ||
|
|
99599953f5 | ||
|
|
b4a517f2d3 | ||
|
|
3e2a49103e | ||
|
|
159b4857ba | ||
|
|
aa518862e3 | ||
|
|
a2a2fea40f | ||
|
|
3c1c92dd68 | ||
|
|
8ab50dc316 | ||
|
|
25603634db | ||
|
|
ef5d6d7889 | ||
|
|
f15a32255b | ||
|
|
a7fc1aa7a7 | ||
|
|
84d76a2ebc | ||
|
|
9988b1e82d | ||
|
|
da4dd07de1 | ||
|
|
4d88787ba0 | ||
|
|
6e9ab1dbbb | ||
|
|
0fca0ed147 | ||
|
|
74d3141195 | ||
|
|
a2a251b2f1 | ||
|
|
853b9313f6 | ||
|
|
a7c814262b | ||
|
|
f02992faa5 | ||
|
|
d98614d403 | ||
|
|
0482941934 | ||
|
|
cf00bddd6f | ||
|
|
dfd79c9c1a | ||
|
|
2741721161 | ||
|
|
751d284f14 | ||
|
|
52719ac685 | ||
|
|
e6d20fead7 | ||
|
|
f7c23ccac8 | ||
|
|
819f7433db | ||
|
|
bfc84be690 | ||
|
|
5cf2c592b5 | ||
|
|
d79e0b32d2 | ||
|
|
2ab197745a | ||
|
|
7f29088c29 | ||
|
|
d1afbf4b92 | ||
|
|
e1d30eec98 | ||
|
|
2ca3c225c4 | ||
|
|
9ceb45b058 | ||
|
|
1cf3ba26b7 | ||
|
|
8a57d77550 | ||
|
|
11b7cfe176 | ||
|
|
2432492e81 | ||
|
|
c75fdd6b3a | ||
|
|
d52d79f2b1 | ||
|
|
92d19eff80 | ||
|
|
9c6a0e047c | ||
|
|
33d7391202 | ||
|
|
43acb6da95 | ||
|
|
4870623869 | ||
|
|
ee7e40b5c8 | ||
|
|
a3b1e2b524 | ||
|
|
0a4bff0587 | ||
|
|
936fa28a25 | ||
|
|
c3cff31edf | ||
|
|
8f0a9e978d | ||
|
|
81be2f0c28 | ||
|
|
31a5b420d9 | ||
|
|
d206c39b8d | ||
|
|
ba063ec74e | ||
|
|
f84cdfa3cf | ||
|
|
9149588025 | ||
|
|
61427e7bfc | ||
|
|
bcbcd29da7 | ||
|
|
1c851af612 | ||
|
|
3c4bbd673c | ||
|
|
3643b4d320 | ||
|
|
57b9dc374a | ||
|
|
5dc5b4ac92 | ||
|
|
f7e2f04da6 | ||
|
|
47e4ee222d | ||
|
|
9a4e6a5012 | ||
|
|
1859f43133 | ||
|
|
2bcf00fb54 | ||
|
|
78946bf328 | ||
|
|
cab6b35e49 | ||
|
|
04b18641ff | ||
|
|
bd121d4286 | ||
|
|
c3a8305a40 | ||
|
|
b0349d7297 | ||
|
|
e83ac52f17 | ||
|
|
2aa9a65765 | ||
|
|
9bdeb33f4c | ||
|
|
dd691a84e6 | ||
|
|
cac5e0a10c | ||
|
|
ff7b6c592e | ||
|
|
1a0e266ebb | ||
|
|
b121585c68 | ||
|
|
51726d8d8e | ||
|
|
2b8e25833d | ||
|
|
1d7ec89b47 | ||
|
|
9afcf8d5eb | ||
|
|
93393f4835 | ||
|
|
3031caffe8 | ||
|
|
1492994fa8 | ||
|
|
7533eb9d96 | ||
|
|
7461a5d509 | ||
|
|
960a86888a | ||
|
|
4b98a0bf35 | ||
|
|
0dd8cab2a0 | ||
|
|
7eb75f8587 | ||
|
|
4881d870c7 | ||
|
|
3a0aa95788 | ||
|
|
0133184a83 | ||
|
|
743faa8eca | ||
|
|
dd0634d777 | ||
|
|
102cca36e5 | ||
|
|
a78dc7f40d | ||
|
|
0ebdb75fc8 | ||
|
|
58b955c405 | ||
|
|
99282d1a98 | ||
|
|
61d0f6863e | ||
|
|
8acc001004 | ||
|
|
71f8febec7 | ||
|
|
4ef221bfe8 | ||
|
|
caf5eaeb9b | ||
|
|
ccae63f26d | ||
|
|
b652e1ae1b | ||
|
|
71d78c60ff | ||
|
|
0cbf500277 | ||
|
|
6b3b282b98 | ||
|
|
c0eb147e1a | ||
|
|
f8bab04a0e | ||
|
|
e6d20117c1 | ||
|
|
02d5ad35c6 | ||
|
|
3e12774e40 | ||
|
|
17b1365853 | ||
|
|
360aaa9f0b | ||
|
|
94b34f1f80 | ||
|
|
ec7ded042b | ||
|
|
d17cf14866 | ||
|
|
3cde9ed32f | ||
|
|
2f341c6e83 | ||
|
|
b0a1a23b68 | ||
|
|
7c70729843 | ||
|
|
ca842229c9 | ||
|
|
abfcb4ef44 | ||
|
|
7cd59cef35 | ||
|
|
fe34caa2b8 | ||
|
|
56141b6d5b | ||
|
|
15ed76c8f2 | ||
|
|
ea9d939577 | ||
|
|
e65ed4ee36 | ||
|
|
9297685825 | ||
|
|
86eeb3c849 | ||
|
|
fdcca53ee9 | ||
|
|
223eda501b | ||
|
|
66ecb48cda | ||
|
|
07ee36e94d | ||
|
|
e29d06dcd9 | ||
|
|
e03ebbdf46 | ||
|
|
7cb9a752ec | ||
|
|
0bac3c0b5f | ||
|
|
0d9010572c | ||
|
|
ed7a54dfd6 | ||
|
|
4cccaf33e2 | ||
|
|
3a6c296883 | ||
|
|
38896d3103 | ||
|
|
ed7be562f7 | ||
|
|
a1e3127d36 | ||
|
|
a0e5a56bf2 | ||
|
|
c1ea91803c | ||
|
|
fe4a35d1d1 | ||
|
|
f99e3fab8b | ||
|
|
7b4671fbf9 | ||
|
|
07349c70bc | ||
|
|
88243a32fa | ||
|
|
d62653f0fd | ||
|
|
0ecafb4a2e | ||
|
|
6f6a0e51df | ||
|
|
5b215470f7 | ||
|
|
7e027a1a25 | ||
|
|
477743260a | ||
|
|
08f12ac163 | ||
|
|
d079b52c63 | ||
|
|
3eaf993c1b | ||
|
|
a625e83b53 | ||
|
|
07cffe9e16 | ||
|
|
e09a70570e | ||
|
|
5aa371bf90 | ||
|
|
ce4729f069 | ||
|
|
c96d3c8ec5 | ||
|
|
4b0ce7ffc1 | ||
|
|
8bae0223bb | ||
|
|
01d0c5d407 | ||
|
|
e262da4691 | ||
|
|
ecc1c0bba3 | ||
|
|
00f44b655e | ||
|
|
bcf7bcf03c | ||
|
|
415f28995d | ||
|
|
e0e9811ab6 | ||
|
|
5b7b87e524 | ||
|
|
0c27fb24a5 | ||
|
|
9cd43db401 | ||
|
|
5aac8e46a5 | ||
|
|
676eeaf190 | ||
|
|
481836f035 | ||
|
|
54e088a097 | ||
|
|
7eb5efb571 | ||
|
|
e47c3211b0 | ||
|
|
85fb5f33aa | ||
|
|
e37f4467f8 | ||
|
|
20f2cf7d27 | ||
|
|
0d90a1db4c | ||
|
|
621787915c | ||
|
|
90c917f69e | ||
|
|
7864ba75c7 | ||
|
|
50cae0165c | ||
|
|
5b57d91a9b | ||
|
|
85eab76a71 | ||
|
|
e1246ff99f | ||
|
|
16c8825b2b | ||
|
|
ca7399f6c1 | ||
|
|
20ef27028e | ||
|
|
916ebcacff | ||
|
|
df0a633b09 | ||
|
|
cc8d3a9f36 | ||
|
|
2a4e103812 | ||
|
|
d19b3857ee | ||
|
|
3a6a4092f9 | ||
|
|
17c7a2bad1 | ||
|
|
5b406ba70f | ||
|
|
e021328067 | ||
|
|
db02712c98 | ||
|
|
45f7cf04ab | ||
|
|
e0a2528a4f | ||
|
|
9a2bb981a9 | ||
|
|
823a1034e0 | ||
|
|
03f7adf596 | ||
|
|
9c20c3595a | ||
|
|
228f2379aa | ||
|
|
211da982b3 | ||
|
|
9c93daf542 | ||
|
|
f933cb624c | ||
|
|
8308f88865 | ||
|
|
86dbc23f3c | ||
|
|
cca5b8492b | ||
|
|
4faa06f37d | ||
|
|
33b26a69b8 | ||
|
|
b466d12263 | ||
|
|
e69275663b | ||
|
|
e2781964bb | ||
|
|
2d3f2500e8 | ||
|
|
77b188833e | ||
|
|
b44fbdcb14 | ||
|
|
5b61f65711 | ||
|
|
46e4036c01 | ||
|
|
dadcf5db07 | ||
|
|
b21a07aa66 | ||
|
|
c6be804723 | ||
|
|
a8846b17c0 | ||
|
|
e6ca63cc36 | ||
|
|
dd9f459bbf | ||
|
|
a715ec1a0f | ||
|
|
bbbe0cca09 | ||
|
|
0072a3968d | ||
|
|
f3fb09f4f9 | ||
|
|
c4f44fce4c | ||
|
|
e1abeeb78a | ||
|
|
17e8b0a0fd | ||
|
|
b142225a5a | ||
|
|
1efb21709b | ||
|
|
f184384175 | ||
|
|
6ab6fdf1f0 | ||
|
|
5653030c64 | ||
|
|
ed266adfc2 | ||
|
|
a629ec510f | ||
|
|
6523b6b342 | ||
|
|
2dca2148f4 | ||
|
|
51f66af320 | ||
|
|
4a66e2fbe0 | ||
|
|
5e50e98738 | ||
|
|
601811eac6 | ||
|
|
071dffe8f2 | ||
|
|
1173a9b586 | ||
|
|
af510ce86f | ||
|
|
076e1b851e | ||
|
|
718ea926d8 | ||
|
|
328ecb22d8 | ||
|
|
be5137d14d | ||
|
|
3fec6fca0e | ||
|
|
bb674d1539 | ||
|
|
59dbd12576 | ||
|
|
e9d5123f8a | ||
|
|
82863bd947 | ||
|
|
8618913a75 | ||
|
|
59f08bc0db | ||
|
|
548c68f878 | ||
|
|
c3d9ac6c4e | ||
|
|
8641a98107 | ||
|
|
664a457b47 | ||
|
|
024e93e89b | ||
|
|
cde279e489 | ||
|
|
0e262dca68 | ||
|
|
b355b3d7f3 | ||
|
|
8c1208031d | ||
|
|
aac23f30cf | ||
|
|
03763f46c9 | ||
|
|
a0b1732f7e | ||
|
|
80ef4a3297 | ||
|
|
46a4948f02 | ||
|
|
f9fdab782a | ||
|
|
2066f1288f | ||
|
|
be9203801e | ||
|
|
916d930a83 | ||
|
|
1562c6944b | ||
|
|
21f14d55b7 | ||
|
|
4417787dfe | ||
|
|
bfa3f9a314 | ||
|
|
6f86600b46 | ||
|
|
d3f964c96e | ||
|
|
d661c7260e | ||
|
|
b2095bdb0e | ||
|
|
1ccb700b3f | ||
|
|
42a4bbeab7 | ||
|
|
e0b78a3cf4 | ||
|
|
5028b271fb | ||
|
|
f5b6291abf | ||
|
|
f5a2cf6726 | ||
|
|
15e2e2ab07 | ||
|
|
999477024b | ||
|
|
b49d2e4d2e | ||
|
|
62bcd878a0 | ||
|
|
d0b72d5dd4 | ||
|
|
d031a4c9aa | ||
|
|
793af2a10d | ||
|
|
2598a6001e | ||
|
|
e548074bc1 | ||
|
|
662e642729 | ||
|
|
dd2c2f62b8 | ||
|
|
577ff4cab9 | ||
|
|
e8b7dbb4d2 | ||
|
|
8d93d6bd16 | ||
|
|
0b7a60e3de | ||
|
|
a450ac3652 | ||
|
|
30ebdcaa4f | ||
|
|
ffa2ea4bb4 | ||
|
|
f65240c217 | ||
|
|
0271d98b91 | ||
|
|
e9c86622c5 | ||
|
|
aae2fb1cb9 | ||
|
|
39761bdc04 | ||
|
|
7d29ae44ba |
14
.babelrc
14
.babelrc
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"plugins": [
|
||||
"transform-es2015-modules-commonjs",
|
||||
"syntax-object-rest-spread",
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ coverage/
|
||||
database_reports/
|
||||
website/build/
|
||||
website/transpiled-babel/
|
||||
# Has its own linter
|
||||
website/client/
|
||||
website/common/transpiled-babel/
|
||||
dist/
|
||||
dist-client/
|
||||
|
||||
10
.eslintrc
10
.eslintrc
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
"extends": [
|
||||
"habitrpg",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
}
|
||||
6
.eslintrc.js
Normal file
6
.eslintrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'habitrpg/lib/node'
|
||||
],
|
||||
}
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,7 +1,7 @@
|
||||
[//]: # (Note: See http://habitica.fandom.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
|
||||
|
||||
[//]: # (Put Issue # here, if applicable. This will automatically close the issue if your PR is merged in)
|
||||
Fixes put_#_and_issue_numer_here
|
||||
Fixes put_#_and_issue_number_here
|
||||
|
||||
### Changes
|
||||
[//]: # (Describe the changes that were made in detail here. Include pictures if necessary)
|
||||
|
||||
198
.github/workflows/test.yml
vendored
Normal file
198
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run lint-no-fix
|
||||
apidoc:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run apidoc
|
||||
sanity:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:sanity
|
||||
|
||||
common:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:common
|
||||
content:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:content
|
||||
|
||||
api-unit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:api:unit
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
api-v3-integration:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:api-v3:integration
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
api-v4-integration:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo docker run --name mongo -d -p 27017:27017 mongo
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:api-v4:integration
|
||||
env:
|
||||
REQUIRES_SERVER=true: true
|
||||
|
||||
client-unit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cp config.json.example config.json
|
||||
- name: npm install
|
||||
run: |
|
||||
npm ci
|
||||
env:
|
||||
CI: true
|
||||
- run: npm run test:unit
|
||||
working-directory: ./website/client
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,10 +1,5 @@
|
||||
.DS_Store
|
||||
website/client-old/gen
|
||||
website/client-old/common
|
||||
website/client-old/apidoc
|
||||
website/build
|
||||
website/client-old/js/habitrpg-shared.js*
|
||||
website/client-old/css/habitrpg-shared.css
|
||||
website/transpiled-babel/
|
||||
website/common/transpiled-babel/
|
||||
node_modules
|
||||
@@ -15,8 +10,6 @@ apidoc_build
|
||||
config.json
|
||||
npm-debug.log*
|
||||
lib
|
||||
website/client-old/bower_components
|
||||
website/client-old/new-stuff.html
|
||||
newrelic_agent.log
|
||||
.bower-tmp
|
||||
.bower-registry
|
||||
@@ -27,15 +20,13 @@ TODO
|
||||
*.log
|
||||
src/*/*.map
|
||||
src/*/*/*.map
|
||||
test/*.js
|
||||
test/*.map
|
||||
website/client-old/docs
|
||||
*.sublime-workspace
|
||||
coverage
|
||||
coverage.html
|
||||
common/dist/scripts/*
|
||||
dist
|
||||
dist-client
|
||||
website/client/dist
|
||||
test/client/unit/coverage
|
||||
test/client/e2e/reports
|
||||
test/client-old/spec/mocks/translations.js
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
node_modules/**
|
||||
.bower-cache/**
|
||||
.bower-tmp/**
|
||||
.bower-registry/**
|
||||
website/client-old/**
|
||||
website/client/**
|
||||
website/client/store/**
|
||||
website/views/**
|
||||
website/build/**
|
||||
dist/**
|
||||
test/**
|
||||
.git/**
|
||||
Gruntfile.js
|
||||
CHANGELOG.md
|
||||
.idea*
|
||||
*.log
|
||||
newrelic_agent.log
|
||||
*.swp
|
||||
*.swx
|
||||
website/raw_sprites/**
|
||||
|
||||
29
.travis.yml
29
.travis.yml
@@ -1,29 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '12'
|
||||
services:
|
||||
- mongodb
|
||||
cache:
|
||||
directories:
|
||||
- 'node_modules'
|
||||
addons:
|
||||
chrome: stable
|
||||
before_script:
|
||||
- npm run test:build
|
||||
- cp config.json.example config.json
|
||||
- sleep 5
|
||||
script:
|
||||
- npm run $TEST
|
||||
env:
|
||||
global:
|
||||
- DISABLE_REQUEST_LOGGING=true
|
||||
matrix:
|
||||
- TEST="lint"
|
||||
- TEST="test:api:unit" REQUIRES_SERVER=true COVERAGE=true
|
||||
- TEST="test:api-v3:integration" REQUIRES_SERVER=true COVERAGE=true
|
||||
- TEST="test:api-v4:integration" REQUIRES_SERVER=true COVERAGE=true
|
||||
- TEST="test:sanity"
|
||||
- TEST="test:content" COVERAGE=true
|
||||
- TEST="test:common" COVERAGE=true
|
||||
- TEST="client:unit" COVERAGE=true
|
||||
- TEST="apidoc"
|
||||
@@ -19,12 +19,9 @@ RUN npm install -g gulp-cli mocha
|
||||
RUN mkdir -p /usr/src/habitrpg
|
||||
WORKDIR /usr/src/habitrpg
|
||||
RUN git clone --branch release --depth 1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
|
||||
RUN npm set unsafe-perm true
|
||||
RUN npm install
|
||||
RUN gulp build:prod --force
|
||||
|
||||
# Create Build dir
|
||||
RUN mkdir -p ./website/build
|
||||
|
||||
# Start Habitica
|
||||
EXPOSE 3000
|
||||
EXPOSE 80 8080 36612
|
||||
CMD ["node", "./website/transpiled-babel/index.js"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Habitica [](https://travis-ci.org/HabitRPG/habitica) [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE) [](https://www.codetriage.com/habitrpg/habitica)
|
||||
Habitica  [](https://codeclimate.com/github/HabitRPG/habitrpg) [](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE) [](https://www.codetriage.com/habitrpg/habitica)
|
||||
===============
|
||||
|
||||
[](https://greenkeeper.io/)
|
||||
|
||||
11
VAGRANT.md
11
VAGRANT.md
@@ -1,11 +0,0 @@
|
||||
# Vagrant #
|
||||
|
||||
Vagrant is a system to create reproducible and portable development
|
||||
environments. Because of the variety of systems used for Habitica
|
||||
development and the various issues developers may encounter setting up
|
||||
Habitica on them, vagrant provides a single development enviroment with
|
||||
minimal dependencies on the developer's local platform. It can be used
|
||||
on a variety of systems including Windows, Mac OS X, and Linux.
|
||||
|
||||
Instructions for using the Habitica Vagrant environment are in
|
||||
[Setting up Habitica Locally](http://habitica.fandom.com/wiki/Setting_up_Habitica_Locally).
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 4096
|
||||
v.cpus = 1
|
||||
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
|
||||
end
|
||||
config.vm.box = "thepeopleseason/habitrpg"
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
config.vm.hostname = "habitrpg"
|
||||
config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
|
||||
config.vm.usable_port_range = (3000..3050)
|
||||
config.vm.network "forwarded_port", guest: 8080, host: 8080, auto_correct: true
|
||||
config.vm.usable_port_range = (8080..8130)
|
||||
config.vm.provision :shell, :path => "vagrant_scripts/vagrant.sh"
|
||||
end
|
||||
@@ -33,6 +33,7 @@
|
||||
"LOGGLY_TOKEN": "example-token",
|
||||
"MAINTENANCE_MODE": "false",
|
||||
"NODE_DB_URI": "mongodb://localhost/habitrpg",
|
||||
"MONGODB_POOL_SIZE": "10",
|
||||
"NODE_ENV": "development",
|
||||
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
|
||||
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
|
||||
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
- .:/code
|
||||
- /code/node_modules
|
||||
mongo:
|
||||
image: mongo:3.4
|
||||
image: mongo:3.6
|
||||
networks:
|
||||
- habitica
|
||||
ports:
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
- mongo
|
||||
|
||||
mongo:
|
||||
image: mongo:3.4
|
||||
image: mongo:3.6
|
||||
ports:
|
||||
- "27017:27017"
|
||||
networks:
|
||||
|
||||
@@ -4,12 +4,12 @@ import apidoc from 'apidoc';
|
||||
|
||||
const APIDOC_DEST_PATH = './apidoc_build';
|
||||
const APIDOC_SRC_PATH = './website/server';
|
||||
gulp.task('apidoc:clean', (done) => {
|
||||
gulp.task('apidoc:clean', done => {
|
||||
clean(APIDOC_DEST_PATH, done);
|
||||
});
|
||||
|
||||
gulp.task('apidoc', gulp.series('apidoc:clean', (done) => {
|
||||
let result = apidoc.createDoc({
|
||||
gulp.task('apidoc', gulp.series('apidoc:clean', done => {
|
||||
const result = apidoc.createDoc({
|
||||
src: APIDOC_SRC_PATH,
|
||||
dest: APIDOC_DEST_PATH,
|
||||
});
|
||||
@@ -21,6 +21,4 @@ gulp.task('apidoc', gulp.series('apidoc:clean', (done) => {
|
||||
}
|
||||
}));
|
||||
|
||||
gulp.task('apidoc:watch', gulp.series('apidoc', (done) => {
|
||||
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done));
|
||||
}));
|
||||
gulp.task('apidoc:watch', gulp.series('apidoc', done => gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, gulp.series('apidoc', done))));
|
||||
|
||||
@@ -1,43 +1,28 @@
|
||||
import gulp from 'gulp';
|
||||
import babel from 'gulp-babel';
|
||||
import webpackProductionBuild from '../webpack/build';
|
||||
|
||||
gulp.task('build:src', () => {
|
||||
return gulp.src('website/server/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/transpiled-babel/'));
|
||||
});
|
||||
gulp.task('build:src', () => gulp.src('website/server/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/transpiled-babel/')));
|
||||
|
||||
gulp.task('build:common', () => {
|
||||
return gulp.src('website/common/script/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/common/transpiled-babel/'));
|
||||
});
|
||||
gulp.task('build:common', () => gulp.src('website/common/script/**/*.js')
|
||||
.pipe(babel())
|
||||
.pipe(gulp.dest('website/common/transpiled-babel/')));
|
||||
|
||||
gulp.task('build:server', gulp.series('build:src', 'build:common', done => done()));
|
||||
|
||||
// Client Production Build
|
||||
gulp.task('build:client', (done) => {
|
||||
webpackProductionBuild((err, output) => {
|
||||
if (err) return done(err);
|
||||
console.log(output); // eslint-disable-line no-console
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build:prod', gulp.series(
|
||||
'build:server',
|
||||
'build:client',
|
||||
'apidoc',
|
||||
done => done()
|
||||
done => done(),
|
||||
));
|
||||
|
||||
let buildArgs = [];
|
||||
const buildArgs = [];
|
||||
|
||||
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
|
||||
buildArgs.push('build:prod');
|
||||
}
|
||||
|
||||
gulp.task('build', gulp.series(buildArgs, (done) => {
|
||||
gulp.task('build', gulp.series(buildArgs, done => {
|
||||
done();
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import mongoose from 'mongoose';
|
||||
import logger from '../website/server/libs/logger';
|
||||
import nconf from 'nconf';
|
||||
import repl from 'repl';
|
||||
import gulp from 'gulp';
|
||||
import nconf from 'nconf';
|
||||
import repl from 'repl';
|
||||
import gulp from 'gulp';
|
||||
import logger from '../website/server/libs/logger';
|
||||
|
||||
// Add additional properties to the repl's context
|
||||
let improveRepl = (context) => {
|
||||
const improveRepl = context => {
|
||||
// Let "exit" and "quit" terminate the console
|
||||
['exit', 'quit'].forEach((term) => {
|
||||
Object.defineProperty(context, term, { get () {
|
||||
process.exit();
|
||||
}});
|
||||
['exit', 'quit'].forEach(term => {
|
||||
Object.defineProperty(context, term, {
|
||||
get () { // eslint-disable-line getter-return
|
||||
process.exit();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// "clear" clears the screen
|
||||
Object.defineProperty(context, 'clear', { get () {
|
||||
process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
}});
|
||||
Object.defineProperty(context, 'clear', {
|
||||
get () { // eslint-disable-line getter-return
|
||||
process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
},
|
||||
});
|
||||
|
||||
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
|
||||
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
|
||||
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
|
||||
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
|
||||
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
|
||||
|
||||
const isProd = nconf.get('NODE_ENV') === 'production';
|
||||
const mongooseOptions = !isProd ? {} : {
|
||||
@@ -30,14 +34,14 @@ let improveRepl = (context) => {
|
||||
mongoose.connect(
|
||||
nconf.get('NODE_DB_URI'),
|
||||
mongooseOptions,
|
||||
(err) => {
|
||||
err => {
|
||||
if (err) throw err;
|
||||
logger.info('Connected with Mongoose');
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
gulp.task('console', (done) => {
|
||||
gulp.task('console', done => {
|
||||
improveRepl(repl.start({
|
||||
prompt: 'Habitica > ',
|
||||
}).context);
|
||||
|
||||
@@ -4,29 +4,29 @@ import spritesmith from 'gulp.spritesmith';
|
||||
import clean from 'rimraf';
|
||||
import sizeOf from 'image-size';
|
||||
import mergeStream from 'merge-stream';
|
||||
import {basename} from 'path';
|
||||
import {sync} from 'glob';
|
||||
import {each} from 'lodash';
|
||||
import { basename } from 'path';
|
||||
import { sync } from 'glob';
|
||||
import { each } from 'lodash';
|
||||
import vinylBuffer from 'vinyl-buffer';
|
||||
|
||||
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
|
||||
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
|
||||
|
||||
const IMG_DIST_PATH = 'website/client/assets/images/sprites/';
|
||||
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
|
||||
const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/';
|
||||
const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/';
|
||||
|
||||
function checkForSpecialTreatment (name) {
|
||||
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
|
||||
const regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
|
||||
return name.match(regex) || name === 'head_0';
|
||||
}
|
||||
|
||||
function calculateImgDimensions (img, addPadding) {
|
||||
let dims = sizeOf(img);
|
||||
|
||||
let requiresSpecialTreatment = checkForSpecialTreatment(img);
|
||||
const requiresSpecialTreatment = checkForSpecialTreatment(img);
|
||||
if (requiresSpecialTreatment) {
|
||||
let newWidth = dims.width < 90 ? 90 : dims.width;
|
||||
let newHeight = dims.height < 90 ? 90 : dims.height;
|
||||
const newWidth = dims.width < 90 ? 90 : dims.width;
|
||||
const newHeight = dims.height < 90 ? 90 : dims.height;
|
||||
dims = {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
@@ -41,17 +41,17 @@ function calculateImgDimensions (img, addPadding) {
|
||||
|
||||
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
|
||||
|
||||
let totalPixelSize = dims.width * dims.height + padding;
|
||||
const totalPixelSize = dims.width * dims.height + padding;
|
||||
|
||||
return totalPixelSize;
|
||||
}
|
||||
|
||||
function calculateSpritesheetsSrcIndicies (src) {
|
||||
let totalPixels = 0;
|
||||
let slices = [0];
|
||||
const slices = [0];
|
||||
|
||||
each(src, (img, index) => {
|
||||
let imageSize = calculateImgDimensions(img, true);
|
||||
const imageSize = calculateImgDimensions(img, true);
|
||||
totalPixels += imageSize;
|
||||
|
||||
if (totalPixels > MAX_SPRITESHEET_SIZE) {
|
||||
@@ -64,37 +64,35 @@ function calculateSpritesheetsSrcIndicies (src) {
|
||||
}
|
||||
|
||||
function cssVarMap (sprite) {
|
||||
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
|
||||
// 60x60 image pointing at the proper part of the 90x90 sprite.
|
||||
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class,
|
||||
// which works as a 60x60 image pointing at the proper part of the 90x90 sprite.
|
||||
// We set up the custom info here, and the template makes use of it.
|
||||
let requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
|
||||
const requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
|
||||
if (requiresSpecialTreatment) {
|
||||
sprite.custom = {
|
||||
px: {
|
||||
offsetX: `-${ sprite.x + 25 }px`,
|
||||
offsetY: `-${ sprite.y + 15 }px`,
|
||||
offsetX: `-${sprite.x + 25}px`,
|
||||
offsetY: `-${sprite.y + 15}px`,
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (sprite.name.indexOf('shirt') !== -1)
|
||||
sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts
|
||||
if (sprite.name.indexOf('shirt') !== -1) sprite.custom.px.offsetY = `-${sprite.y + 35}px`; // even more for shirts
|
||||
if (sprite.name.indexOf('hair_base') !== -1) {
|
||||
let styleArray = sprite.name.split('_').slice(2, 3);
|
||||
if (Number(styleArray[0]) > 14)
|
||||
sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos
|
||||
const styleArray = sprite.name.split('_').slice(2, 3);
|
||||
if (Number(styleArray[0]) > 14) sprite.custom.px.offsetY = `-${sprite.y}px`; // don't crop updos
|
||||
}
|
||||
}
|
||||
|
||||
function createSpritesStream (name, src) {
|
||||
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
|
||||
let stream = mergeStream();
|
||||
const spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
|
||||
const stream = mergeStream();
|
||||
|
||||
each(spritesheetSliceIndicies, (start, index) => {
|
||||
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
|
||||
const slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
|
||||
|
||||
let spriteData = gulp.src(slicedSrc)
|
||||
const spriteData = gulp.src(slicedSrc)
|
||||
.pipe(spritesmith({
|
||||
imgName: `spritesmith-${name}-${index}.png`,
|
||||
cssName: `spritesmith-${name}-${index}.css`,
|
||||
@@ -104,12 +102,12 @@ function createSpritesStream (name, src) {
|
||||
cssVarMap,
|
||||
}));
|
||||
|
||||
let imgStream = spriteData.img
|
||||
const imgStream = spriteData.img
|
||||
.pipe(vinylBuffer())
|
||||
.pipe(imagemin())
|
||||
.pipe(gulp.dest(IMG_DIST_PATH));
|
||||
|
||||
let cssStream = spriteData.css
|
||||
const cssStream = spriteData.css
|
||||
.pipe(gulp.dest(CSS_DIST_PATH));
|
||||
|
||||
stream.add(imgStream);
|
||||
@@ -120,32 +118,32 @@ function createSpritesStream (name, src) {
|
||||
}
|
||||
|
||||
gulp.task('sprites:main', () => {
|
||||
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
|
||||
const mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
|
||||
return createSpritesStream('main', mainSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:largeSprites', () => {
|
||||
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
|
||||
const largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
|
||||
return createSpritesStream('largeSprites', largeSrc);
|
||||
});
|
||||
|
||||
gulp.task('sprites:clean', (done) => {
|
||||
gulp.task('sprites:clean', done => {
|
||||
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
|
||||
});
|
||||
|
||||
gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprites:largeSprites', (done) => {
|
||||
gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprites:largeSprites', done => {
|
||||
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
|
||||
|
||||
let numberOfSheetsThatAreTooBig = 0;
|
||||
|
||||
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
|
||||
const distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
|
||||
|
||||
each(distSpritesheets, (img) => {
|
||||
let spriteSize = calculateImgDimensions(img);
|
||||
each(distSpritesheets, img => {
|
||||
const spriteSize = calculateImgDimensions(img);
|
||||
|
||||
if (spriteSize > MAX_SPRITESHEET_SIZE) {
|
||||
numberOfSheetsThatAreTooBig++;
|
||||
let name = basename(img, '.png');
|
||||
numberOfSheetsThatAreTooBig += 1;
|
||||
const name = basename(img, '.png');
|
||||
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
@@ -155,7 +153,8 @@ gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', 'sprite
|
||||
console.error( // eslint-disable-line no-console
|
||||
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
|
||||
them, but there is a margin of error in these calculations so it is probably okay. Mention
|
||||
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`);
|
||||
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`,
|
||||
);
|
||||
} else {
|
||||
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import gulp from 'gulp';
|
||||
import nodemon from 'gulp-nodemon';
|
||||
|
||||
let pkg = require('../package.json');
|
||||
import pkg from '../package.json';
|
||||
|
||||
gulp.task('nodemon', (done) => {
|
||||
gulp.task('nodemon', done => {
|
||||
nodemon({
|
||||
script: pkg.main,
|
||||
ignore: [
|
||||
'website/client-old/*',
|
||||
'website/views/*',
|
||||
'common/dist/script/content/*',
|
||||
],
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1,60 +1,59 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { exec } from 'child_process';
|
||||
import gulp from 'gulp';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
import {
|
||||
pipe,
|
||||
} from './taskHelper';
|
||||
import mongoose from 'mongoose';
|
||||
import { exec } from 'child_process';
|
||||
import gulp from 'gulp';
|
||||
import os from 'os';
|
||||
import nconf from 'nconf';
|
||||
} from './taskHelper';
|
||||
|
||||
// TODO rewrite
|
||||
|
||||
const TEST_SERVER_PORT = 3003;
|
||||
const TEST_SERVER_PORT = 3003;
|
||||
let server;
|
||||
|
||||
const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
const TEST_DB_URI = nconf.get('TEST_DB_URI');
|
||||
|
||||
const SANITY_TEST_COMMAND = 'npm run test:sanity';
|
||||
const COMMON_TEST_COMMAND = 'npm run test:common';
|
||||
const CONTENT_TEST_COMMAND = 'npm run test:content';
|
||||
const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
|
||||
const CONTENT_OPTIONS = { maxBuffer: 1024 * 500 };
|
||||
|
||||
/* Helper methods for reporting test summary */
|
||||
let testResults = [];
|
||||
let testCount = (stdout, regexp) => {
|
||||
let match = stdout.match(regexp);
|
||||
return parseInt(match && match[1] || 0, 10);
|
||||
const testResults = [];
|
||||
const testCount = (stdout, regexp) => {
|
||||
const match = stdout.match(regexp);
|
||||
return parseInt(match && (match[1] || 0), 10);
|
||||
};
|
||||
|
||||
let testBin = (string, additionalEnvVariables = '') => {
|
||||
const testBin = (string, additionalEnvVariables = '') => {
|
||||
if (os.platform() === 'win32') {
|
||||
if (additionalEnvVariables !== '') {
|
||||
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
|
||||
additionalEnvVariables = `set ${additionalEnvVariables}&&`;
|
||||
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set '); // eslint-disable-line no-param-reassign
|
||||
additionalEnvVariables = `set ${additionalEnvVariables}&&`; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
|
||||
} else {
|
||||
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
|
||||
}
|
||||
return `NODE_ENV=test ${additionalEnvVariables} ${string}`;
|
||||
};
|
||||
|
||||
gulp.task('test:nodemon', gulp.series(function setupNodemon (done) {
|
||||
gulp.task('test:nodemon', gulp.series(done => {
|
||||
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
|
||||
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
|
||||
done();
|
||||
}, 'nodemon'));
|
||||
|
||||
gulp.task('test:prepare:mongo', (cb) => {
|
||||
mongoose.connect(TEST_DB_URI, (err) => {
|
||||
gulp.task('test:prepare:mongo', cb => {
|
||||
mongoose.connect(TEST_DB_URI, err => {
|
||||
if (err) return cb(`Unable to connect to mongo database. Are you sure it's running? \n\n${err}`);
|
||||
mongoose.connection.dropDatabase((err2) => {
|
||||
return mongoose.connection.dropDatabase(err2 => {
|
||||
if (err2) return cb(err2);
|
||||
mongoose.connection.close(cb);
|
||||
return mongoose.connection.close(cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', (done) => {
|
||||
gulp.task('test:prepare:server', gulp.series('test:prepare:mongo', done => {
|
||||
if (!server) {
|
||||
server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
@@ -73,45 +72,43 @@ gulp.task('test:prepare:build', gulp.series('build', done => done()));
|
||||
gulp.task('test:prepare', gulp.series(
|
||||
'test:prepare:build',
|
||||
'test:prepare:mongo',
|
||||
done => done()
|
||||
done => done(),
|
||||
));
|
||||
|
||||
gulp.task('test:sanity', (cb) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:sanity', cb => {
|
||||
const runner = exec(
|
||||
testBin(SANITY_TEST_COMMAND),
|
||||
(err) => {
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:common', gulp.series('test:prepare:build', (cb) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:common', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
(err) => {
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:common:clean', (cb) => {
|
||||
gulp.task('test:common:clean', cb => {
|
||||
pipe(exec(testBin(COMMON_TEST_COMMAND), () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:common:watch', gulp.series('test:common:clean', () => {
|
||||
return gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done()));
|
||||
}));
|
||||
gulp.task('test:common:watch', gulp.series('test:common:clean', () => gulp.watch(['common/script/**/*', 'test/common/**/*'], gulp.series('test:common:clean', done => done()))));
|
||||
|
||||
gulp.task('test:common:safe', gulp.series('test:prepare:build', (cb) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:common:safe', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(COMMON_TEST_COMMAND),
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
testResults.push({
|
||||
@@ -121,38 +118,36 @@ gulp.task('test:common:safe', gulp.series('test:prepare:build', (cb) => {
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:content', gulp.series('test:prepare:build', (cb) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:content', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
(err) => {
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:content:clean', (cb) => {
|
||||
gulp.task('test:content:clean', cb => {
|
||||
pipe(exec(testBin(CONTENT_TEST_COMMAND), CONTENT_OPTIONS, () => cb()));
|
||||
});
|
||||
|
||||
gulp.task('test:content:watch', gulp.series('test:content:clean', () => {
|
||||
return gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()));
|
||||
}));
|
||||
gulp.task('test:content:watch', gulp.series('test:content:clean', () => gulp.watch(['common/script/content/**', 'test/**'], gulp.series('test:content:clean', done => done()))));
|
||||
|
||||
gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:content:safe', gulp.series('test:prepare:build', cb => {
|
||||
const runner = exec(
|
||||
testBin(CONTENT_TEST_COMMAND),
|
||||
CONTENT_OPTIONS,
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
(err, stdout) => { // eslint-disable-line handle-callback-err
|
||||
testResults.push({
|
||||
suite: 'Content Specs\t',
|
||||
pass: testCount(stdout, /(\d+) passing/),
|
||||
@@ -160,81 +155,77 @@ gulp.task('test:content:safe', gulp.series('test:prepare:build', (cb) => {
|
||||
pend: testCount(stdout, /(\d+) pending/),
|
||||
});
|
||||
cb();
|
||||
}
|
||||
},
|
||||
);
|
||||
pipe(runner);
|
||||
}));
|
||||
|
||||
gulp.task('test:api:unit', (done) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:api:unit', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-unit node_modules/mocha/bin/_mocha -- test/api/unit --recursive --require ./test/helpers/start-server'),
|
||||
(err) => {
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api:unit:watch', () => {
|
||||
return gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done()));
|
||||
});
|
||||
gulp.task('test:api:unit:watch', () => gulp.watch(['website/server/libs/*', 'test/api/unit/**/*', 'website/server/controllers/**/*'], gulp.series('test:api:unit', done => done())));
|
||||
|
||||
gulp.task('test:api-v3:integration', (done) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:api-v3:integration', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err) => {
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v3:integration:watch', () => {
|
||||
return gulp.watch([
|
||||
'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
|
||||
'test/api/v3/integration/**/*',
|
||||
], gulp.series('test:api-v3:integration', done => done()));
|
||||
});
|
||||
gulp.task('test:api-v3:integration:watch', () => gulp.watch([
|
||||
'website/server/controllers/api-v3/**/*', 'common/script/ops/*', 'website/server/libs/*.js',
|
||||
'test/api/v3/integration/**/*',
|
||||
], gulp.series('test:api-v3:integration', done => done())));
|
||||
|
||||
gulp.task('test:api-v3:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:api-v3:integration:separate-server', done => {
|
||||
const runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err) => done(err)
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => done(err),
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v4:integration', (done) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:api-v4:integration', done => {
|
||||
const runner = exec(
|
||||
testBin('istanbul cover --dir coverage/api-v4-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v4 --recursive --require ./test/helpers/start-server'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err) => {
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => {
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
});
|
||||
|
||||
gulp.task('test:api-v4:integration:separate-server', (done) => {
|
||||
let runner = exec(
|
||||
gulp.task('test:api-v4:integration:separate-server', done => {
|
||||
const runner = exec(
|
||||
testBin('mocha test/api/v4 --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
|
||||
{maxBuffer: 500 * 1024},
|
||||
(err) => done(err)
|
||||
{ maxBuffer: 500 * 1024 },
|
||||
err => done(err),
|
||||
);
|
||||
|
||||
pipe(runner);
|
||||
@@ -247,11 +238,11 @@ gulp.task('test', gulp.series(
|
||||
'test:api:unit',
|
||||
'test:api-v3:integration',
|
||||
'test:api-v4:integration',
|
||||
done => done()
|
||||
done => done(),
|
||||
));
|
||||
|
||||
gulp.task('test:api-v3', gulp.series(
|
||||
'test:api:unit',
|
||||
'test:api-v3:integration',
|
||||
done => done()
|
||||
done => done(),
|
||||
));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import gulp from 'gulp';
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import gulp from 'gulp';
|
||||
import { postToSlack, conf } from './taskHelper';
|
||||
|
||||
const SLACK_CONFIG = {
|
||||
@@ -14,7 +14,7 @@ const ENGLISH_LOCALE = `${LOCALES}en/`;
|
||||
|
||||
|
||||
function getArrayOfLanguages () {
|
||||
let languages = fs.readdirSync(LOCALES);
|
||||
const languages = fs.readdirSync(LOCALES);
|
||||
languages.shift(); // Remove README.md from array of languages
|
||||
|
||||
return languages;
|
||||
@@ -23,18 +23,16 @@ function getArrayOfLanguages () {
|
||||
const ALL_LANGUAGES = getArrayOfLanguages();
|
||||
|
||||
function stripOutNonJsonFiles (collection) {
|
||||
let onlyJson = _.filter(collection, (file) => {
|
||||
return file.match(/[a-zA-Z]*\.json/);
|
||||
});
|
||||
const onlyJson = _.filter(collection, file => file.match(/[a-zA-Z]*\.json/));
|
||||
|
||||
return onlyJson;
|
||||
}
|
||||
|
||||
function eachTranslationFile (languages, cb) {
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
|
||||
_.each(languages, (lang) => {
|
||||
_.each(jsonFiles, (filename) => {
|
||||
_.each(languages, lang => {
|
||||
_.each(jsonFiles, filename => {
|
||||
let parsedTranslationFile;
|
||||
try {
|
||||
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
@@ -43,10 +41,10 @@ function eachTranslationFile (languages, cb) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
let parsedEnglishFile = JSON.parse(englishFile);
|
||||
const englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
|
||||
const parsedEnglishFile = JSON.parse(englishFile);
|
||||
|
||||
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
return cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -71,9 +69,9 @@ function formatMessageForPosting (msg, items) {
|
||||
}
|
||||
|
||||
function getStringsWith (json, interpolationRegex) {
|
||||
let strings = {};
|
||||
const strings = {};
|
||||
|
||||
_.each(json, (fileName) => {
|
||||
_.each(json, fileName => {
|
||||
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
|
||||
const parsedJson = JSON.parse(rawFile);
|
||||
|
||||
@@ -93,66 +91,69 @@ const malformedStringExceptions = {
|
||||
feedPet: true,
|
||||
};
|
||||
|
||||
gulp.task('transifex:missingFiles', (done) => {
|
||||
let missingStrings = [];
|
||||
gulp.task('transifex:missingFiles', done => {
|
||||
const missingStrings = [];
|
||||
|
||||
eachTranslationFile(ALL_LANGUAGES, (error) => {
|
||||
eachTranslationFile(ALL_LANGUAGES, error => {
|
||||
if (error) {
|
||||
missingStrings.push(error.path);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
let message = 'the following files were missing from the translations folder';
|
||||
let formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
const message = 'the following files were missing from the translations folder';
|
||||
const formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('transifex:missingStrings', (done) => {
|
||||
let missingStrings = [];
|
||||
gulp.task('transifex:missingStrings', done => {
|
||||
const missingStrings = [];
|
||||
|
||||
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
|
||||
eachTranslationString(ALL_LANGUAGES, (lang, filename, key, englishString, translationString) => {
|
||||
if (!translationString) {
|
||||
let errorString = `${language} - ${filename} - ${key} - ${englishString}`;
|
||||
const errorString = `${lang} - ${filename} - ${key} - ${englishString}`;
|
||||
missingStrings.push(errorString);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_.isEmpty(missingStrings)) {
|
||||
let message = 'The following strings are not translated';
|
||||
let formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
const message = 'The following strings are not translated';
|
||||
const formattedMessage = formatMessageForPosting(message, missingStrings);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('transifex:malformedStrings', (done) => {
|
||||
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
|
||||
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
|
||||
gulp.task('transifex:malformedStrings', done => {
|
||||
const jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
|
||||
const interpolationRegex = /<%= [a-zA-Z]* %>/g;
|
||||
const stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
|
||||
|
||||
let stringsWithMalformedInterpolations = [];
|
||||
let stringsWithIncorrectNumberOfInterpolations = [];
|
||||
const stringsWithMalformedInterpolations = [];
|
||||
const stringsWithIncorrectNumberOfInterpolations = [];
|
||||
|
||||
_.each(ALL_LANGUAGES, (lang) => {
|
||||
_.each(ALL_LANGUAGES, lang => {
|
||||
_.each(stringsToLookFor, (strings, filename) => {
|
||||
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
let parsedTranslationFile = JSON.parse(translationFile);
|
||||
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
|
||||
const parsedTranslationFile = JSON.parse(translationFile);
|
||||
|
||||
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
|
||||
let translationString = parsedTranslationFile[key];
|
||||
const translationString = parsedTranslationFile[key];
|
||||
if (!translationString) return;
|
||||
|
||||
let englishOccurences = stringsToLookFor[filename][key];
|
||||
let translationOccurences = translationString.match(interpolationRegex);
|
||||
const englishOccurences = stringsToLookFor[filename][key];
|
||||
const translationOccurences = translationString.match(interpolationRegex);
|
||||
|
||||
if (!translationOccurences) {
|
||||
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
const malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithMalformedInterpolations.push(malformedString);
|
||||
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
|
||||
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
} else if (
|
||||
englishOccurences.length !== translationOccurences.length
|
||||
&& !malformedStringExceptions[key]
|
||||
) {
|
||||
const missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
|
||||
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
|
||||
}
|
||||
});
|
||||
@@ -160,14 +161,17 @@ gulp.task('transifex:malformedStrings', (done) => {
|
||||
});
|
||||
|
||||
if (!_.isEmpty(stringsWithMalformedInterpolations)) {
|
||||
let message = 'The following strings have malformed or missing interpolations';
|
||||
let formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
|
||||
const message = 'The following strings have malformed or missing interpolations';
|
||||
const formattedMessage = formatMessageForPosting(message, stringsWithMalformedInterpolations);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
|
||||
if (!_.isEmpty(stringsWithIncorrectNumberOfInterpolations)) {
|
||||
let message = 'The following strings have a different number of string interpolations';
|
||||
let formattedMessage = formatMessageForPosting(message, stringsWithIncorrectNumberOfInterpolations);
|
||||
const message = 'The following strings have a different number of string interpolations';
|
||||
const formattedMessage = formatMessageForPosting(
|
||||
message,
|
||||
stringsWithIncorrectNumberOfInterpolations,
|
||||
);
|
||||
postToSlack(formattedMessage, SLACK_CONFIG);
|
||||
}
|
||||
done();
|
||||
@@ -176,5 +180,5 @@ gulp.task('transifex:malformedStrings', (done) => {
|
||||
gulp.task(
|
||||
'transifex',
|
||||
gulp.series('transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings'),
|
||||
(done) => done()
|
||||
);
|
||||
done => done(),
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { exec } from 'child_process';
|
||||
import psTree from 'ps-tree';
|
||||
import nconf from 'nconf';
|
||||
import net from 'net';
|
||||
import { post } from 'superagent';
|
||||
import { sync as glob } from 'glob';
|
||||
import Mocha from 'mocha';
|
||||
import { resolve } from 'path';
|
||||
import { exec } from 'child_process';
|
||||
import psTree from 'ps-tree';
|
||||
import nconf from 'nconf';
|
||||
import net from 'net';
|
||||
import { post } from 'superagent';
|
||||
import { sync as glob } from 'glob';
|
||||
import Mocha from 'mocha'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import { resolve } from 'path';
|
||||
|
||||
/*
|
||||
* Get access to configruable values
|
||||
@@ -19,15 +19,15 @@ export const conf = nconf;
|
||||
* its tasks.
|
||||
*/
|
||||
export function kill (proc) {
|
||||
let killProcess = (pid) => {
|
||||
const killProcess = pid => {
|
||||
psTree(pid, (_, pids) => {
|
||||
if (pids.length) {
|
||||
pids.forEach(kill); return;
|
||||
}
|
||||
try {
|
||||
exec(/^win/.test(process.platform) ?
|
||||
`taskkill /PID ${pid} /T /F` :
|
||||
`kill -9 ${pid}`);
|
||||
exec(/^win/.test(process.platform)
|
||||
? `taskkill /PID ${pid} /T /F`
|
||||
: `kill -9 ${pid}`);
|
||||
} catch (e) {
|
||||
console.log(e); // eslint-disable-line no-console
|
||||
}
|
||||
@@ -46,16 +46,15 @@ export function kill (proc) {
|
||||
export function awaitPort (port, max = 60) {
|
||||
return new Promise((rej, res) => {
|
||||
let socket;
|
||||
let timeout;
|
||||
let interval;
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
rej(`Timed out after ${max} seconds`);
|
||||
}, max * 1000);
|
||||
|
||||
interval = setInterval(() => {
|
||||
socket = net.connect({port}, () => {
|
||||
socket = net.connect({ port }, () => {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timeout);
|
||||
socket.destroy();
|
||||
@@ -71,10 +70,10 @@ export function awaitPort (port, max = 60) {
|
||||
* Pipe the child's stdin and stderr to the parent process.
|
||||
*/
|
||||
export function pipe (child) {
|
||||
child.stdout.on('data', (data) => {
|
||||
child.stdout.on('data', data => {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
child.stderr.on('data', (data) => {
|
||||
child.stderr.on('data', data => {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
}
|
||||
@@ -83,7 +82,7 @@ export function pipe (child) {
|
||||
* Post request to notify configured slack channel
|
||||
*/
|
||||
export function postToSlack (msg, config = {}) {
|
||||
let slackUrl = nconf.get('SLACK_URL');
|
||||
const slackUrl = nconf.get('SLACK_URL');
|
||||
|
||||
if (!slackUrl) {
|
||||
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
|
||||
@@ -99,7 +98,7 @@ export function postToSlack (msg, config = {}) {
|
||||
text: msg,
|
||||
icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
|
||||
})
|
||||
.end((err) => {
|
||||
.end(err => {
|
||||
if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
|
||||
});
|
||||
}
|
||||
@@ -107,15 +106,15 @@ export function postToSlack (msg, config = {}) {
|
||||
export function runMochaTests (files, server, cb) {
|
||||
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
|
||||
|
||||
let mocha = new Mocha({reporter: 'spec'});
|
||||
let tests = glob(files);
|
||||
const mocha = new Mocha({ reporter: 'spec' });
|
||||
const tests = glob(files);
|
||||
|
||||
tests.forEach((test) => {
|
||||
tests.forEach(test => {
|
||||
delete require.cache[resolve(test)];
|
||||
mocha.addFile(test);
|
||||
});
|
||||
|
||||
mocha.run((numberOfFailures) => {
|
||||
mocha.run(numberOfFailures => {
|
||||
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
|
||||
if (server) kill(server);
|
||||
process.exit(numberOfFailures);
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* directory, and it will automatically be included.
|
||||
*/
|
||||
|
||||
require('babel-register');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
require('@babel/register');
|
||||
|
||||
const gulp = require('gulp');
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"root": false,
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"no-use-before-define": ["error", { "functions": false }]
|
||||
}
|
||||
}
|
||||
8
migrations/.eslintrc.js
Normal file
8
migrations/.eslintrc.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
module.exports = {
|
||||
root: false,
|
||||
rules: {
|
||||
'no-console': 0,
|
||||
'no-use-before-define': ['error', { functions: false }]
|
||||
}
|
||||
}
|
||||
82
migrations/archive/2019/20191022_pet_color_achievements.js
Normal file
82
migrations/archive/2019/20191022_pet_color_achievements.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20191022_pet_color_achievements';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
let set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.items && user.items.pets) {
|
||||
const pets = user.items.pets;
|
||||
if (pets['Wolf-Zombie'] > 0
|
||||
&& pets['TigerCub-Zombie'] > 0
|
||||
&& pets['PandaCub-Zombie'] > 0
|
||||
&& pets['LionCub-Zombie'] > 0
|
||||
&& pets['Fox-Zombie'] > 0
|
||||
&& pets['FlyingPig-Zombie'] > 0
|
||||
&& pets['Dragon-Zombie'] > 0
|
||||
&& pets['Cactus-Zombie'] > 0
|
||||
&& pets['BearCub-Zombie'] > 0) {
|
||||
set['achievements.monsterMagus'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (user && user.items && user.items.mounts) {
|
||||
const mounts = user.items.mounts;
|
||||
if (mounts['Wolf-Zombie']
|
||||
&& mounts['TigerCub-Zombie']
|
||||
&& mounts['PandaCub-Zombie']
|
||||
&& mounts['LionCub-Zombie']
|
||||
&& mounts['Fox-Zombie']
|
||||
&& mounts['FlyingPig-Zombie']
|
||||
&& mounts['Dragon-Zombie']
|
||||
&& mounts['Cactus-Zombie']
|
||||
&& mounts['BearCub-Zombie'] ) {
|
||||
set['achievements.undeadUndertaker'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2019-10-01') },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
items: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
82
migrations/archive/2019/20191031_habitoween_ladder.js
Normal file
82
migrations/archive/2019/20191031_habitoween_ladder.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Award Habitoween ladder items to participants in this month's Habitoween festivities
|
||||
*/
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const MIGRATION_NAME = '20191031_habitoween_ladder'; // Update when running in future years
|
||||
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {};
|
||||
const inc = {
|
||||
'items.food.Candy_Skeleton': 1,
|
||||
'items.food.Candy_Base': 1,
|
||||
'items.food.Candy_CottonCandyBlue': 1,
|
||||
'items.food.Candy_CottonCandyPink': 1,
|
||||
'items.food.Candy_Shade': 1,
|
||||
'items.food.Candy_White': 1,
|
||||
'items.food.Candy_Golden': 1,
|
||||
'items.food.Candy_Zombie': 1,
|
||||
'items.food.Candy_Desert': 1,
|
||||
'items.food.Candy_Red': 1,
|
||||
};
|
||||
|
||||
set.migration = MIGRATION_NAME;
|
||||
|
||||
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Glow']) {
|
||||
set['items.mounts.JackOLantern-Glow'] = true;
|
||||
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Ghost']) {
|
||||
set['items.pets.JackOLantern-Glow'] = 5;
|
||||
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
|
||||
set['items.mounts.JackOLantern-Ghost'] = true;
|
||||
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
|
||||
set['items.pets.JackOLantern-Ghost'] = 5;
|
||||
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
|
||||
set['items.mounts.JackOLantern-Base'] = true;
|
||||
} else {
|
||||
set['items.pets.JackOLantern-Base'] = 5;
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
return await User.update({_id: user._id}, {$inc: inc, $set: set}).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
'auth.timestamps.loggedin': {$gt: new Date('2019-10-01')},
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
items: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -2,14 +2,14 @@ import { model as Challenges } from '../../website/server/models/challenge';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
async function syncChallengeToMembers (challenges) {
|
||||
let challengSyncPromises = challenges.map(async (challenge) => {
|
||||
let users = await User.find({
|
||||
const challengSyncPromises = challenges.map(async challenge => {
|
||||
const users = await User.find({
|
||||
// _id: '',
|
||||
challenges: challenge._id,
|
||||
}).exec();
|
||||
|
||||
let promises = [];
|
||||
users.forEach((user) => {
|
||||
const promises = [];
|
||||
users.forEach(user => {
|
||||
promises.push(challenge.syncToUser(user));
|
||||
promises.push(challenge.save());
|
||||
promises.push(user.save());
|
||||
@@ -18,11 +18,11 @@ async function syncChallengeToMembers (challenges) {
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
return await Promise.all(challengSyncPromises);
|
||||
return Promise.all(challengSyncPromises);
|
||||
}
|
||||
|
||||
async function syncChallenges (lastChallengeDate) {
|
||||
let query = {
|
||||
const query = {
|
||||
// _id: '',
|
||||
};
|
||||
|
||||
@@ -30,16 +30,16 @@ async function syncChallenges (lastChallengeDate) {
|
||||
query.createdOn = { $lte: lastChallengeDate };
|
||||
}
|
||||
|
||||
let challengesFound = await Challenges.find(query)
|
||||
const challengesFound = await Challenges.find(query)
|
||||
.limit(10)
|
||||
.sort('-createdAt')
|
||||
.exec();
|
||||
|
||||
let syncedChallenges = await syncChallengeToMembers(challengesFound)
|
||||
const syncedChallenges = await syncChallengeToMembers(challengesFound)
|
||||
.catch(reason => console.error(reason));
|
||||
let lastChallenge = challengesFound[challengesFound.length - 1];
|
||||
const lastChallenge = challengesFound[challengesFound.length - 1];
|
||||
if (lastChallenge) syncChallenges(lastChallenge.createdAt);
|
||||
return syncedChallenges;
|
||||
}
|
||||
|
||||
module.exports = syncChallenges;
|
||||
export default syncChallenges;
|
||||
|
||||
@@ -1 +1 @@
|
||||
db.users.update({_id: {$in: ['']}}, {$inc: {balance: 0.5}}, {multi: true});
|
||||
db.users.update({ _id: { $in: [''] } }, { $inc: { balance: 0.5 } }, { multi: true });
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// mongo habitrpg ./node_modules/moment/moment.js ./migrations/cancelSubscription.js
|
||||
|
||||
// For some reason people often to contact me to cancel their sub, rather than do it online. Even when I point them to
|
||||
// For some reason people often to contact me to cancel their sub,
|
||||
// rather than do it online. Even when I point them to
|
||||
// the FAQ (http://goo.gl/1uoPGQ) they insist...
|
||||
|
||||
db.users.update(
|
||||
{_id: ''},
|
||||
{$set: {
|
||||
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
|
||||
}}
|
||||
);
|
||||
{ _id: '' },
|
||||
{
|
||||
$set: {
|
||||
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Give contrib.level 7+ free subscription for life
|
||||
db.users.update(
|
||||
|
||||
{
|
||||
'contributor.level': {$gte: 7},
|
||||
'contributor.level': { $gte: 7 },
|
||||
'purchased.plan.customerId': null,
|
||||
},
|
||||
|
||||
@@ -18,6 +17,6 @@ db.users.update(
|
||||
},
|
||||
},
|
||||
|
||||
{multi: true}
|
||||
{ multi: true },
|
||||
|
||||
);
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// mongo habitrpg ./node_modules/moment/moment.js ./migrations/current_period_end.js
|
||||
db.users.update(
|
||||
{_id: ''},
|
||||
{$set: {'purchased.plan.dateTerminated': moment().add({days: 7}).toDate()}}
|
||||
);
|
||||
{ _id: '' },
|
||||
{ $set: { 'purchased.plan.dateTerminated': moment().add({ days: 7 }).toDate() } },
|
||||
);
|
||||
|
||||
@@ -39,38 +39,52 @@
|
||||
// needed. Do not miss any of them!
|
||||
|
||||
|
||||
let uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5';
|
||||
const uuid = '30fb2640-7121-4968-ace5-f385e60ea6c5';
|
||||
|
||||
db.users.aggregate([
|
||||
{$match: {
|
||||
_id: uuid,
|
||||
}},
|
||||
{$project: {
|
||||
_id: 0, todos: 1,
|
||||
}},
|
||||
{$unwind: '$todos'},
|
||||
{$group: {
|
||||
_id: { taskid: '$todos.id' },
|
||||
count: { $sum: 1 },
|
||||
}},
|
||||
{$match: {
|
||||
count: { $gt: 1 },
|
||||
}},
|
||||
{$project: {
|
||||
'_id.taskid': 1,
|
||||
}},
|
||||
{$group: {
|
||||
_id: { taskid: '$todos.id' },
|
||||
troublesomeIds: { $addToSet: '$_id.taskid' },
|
||||
}},
|
||||
{$project: {
|
||||
_id: 0,
|
||||
troublesomeIds: 1,
|
||||
}},
|
||||
]).forEach((data) => {
|
||||
{
|
||||
$match: {
|
||||
_id: uuid,
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0, todos: 1,
|
||||
},
|
||||
},
|
||||
{ $unwind: '$todos' },
|
||||
{
|
||||
$group: {
|
||||
_id: { taskid: '$todos.id' },
|
||||
count: { $sum: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
count: { $gt: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
'_id.taskid': 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: { taskid: '$todos.id' },
|
||||
troublesomeIds: { $addToSet: '$_id.taskid' },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
troublesomeIds: 1,
|
||||
},
|
||||
},
|
||||
]).forEach(data => {
|
||||
// print( "\n" ); printjson(data);
|
||||
data.troublesomeIds.forEach((taskid) => {
|
||||
print(`non-unique task: ${ taskid}`);
|
||||
data.troublesomeIds.forEach(taskid => {
|
||||
print(`non-unique task: ${taskid}`); // eslint-disable-line no-restricted-globals
|
||||
db.users.update({
|
||||
_id: uuid,
|
||||
todos: { $elemMatch: { id: taskid } },
|
||||
@@ -81,8 +95,7 @@ db.users.aggregate([
|
||||
});
|
||||
|
||||
db.users.update(
|
||||
{_id: uuid},
|
||||
{$pull: { todos: { id: 'de666' } } },
|
||||
{multi: false }
|
||||
{ _id: uuid },
|
||||
{ $pull: { todos: { id: 'de666' } } },
|
||||
{ multi: false },
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
let oldId = '';
|
||||
let newId = '';
|
||||
let newUser = db.users.findOne({_id: newId});
|
||||
const oldId = '';
|
||||
const newId = '';
|
||||
const newUser = db.users.findOne({ _id: newId });
|
||||
|
||||
db.users.update({_id: oldId}, {$set: {auth: newUser.auth}});
|
||||
db.users.update({ _id: oldId }, { $set: { auth: newUser.auth } });
|
||||
|
||||
// remove the auth on the new user (which is a template account). The account will be preened automatically later,
|
||||
// remove the auth on the new user (which is a template account).
|
||||
// The account will be preened automatically later,
|
||||
// this allows us to keep the account around a few days in case there was a mistake
|
||||
db.users.update({_id: newId}, {$unset: {auth: 1}});
|
||||
|
||||
db.users.update({ _id: newId }, { $unset: { auth: 1 } });
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* Past in the text of a unique habit here to find the user, then you can restore their UUID
|
||||
*/
|
||||
|
||||
db.users.find().forEach((user) => {
|
||||
db.users.find().forEach(user => {
|
||||
user.tasks = user.habits.concat(user.dailys).concat(user.todos).concat(user.rewards);
|
||||
let found = _.some(user.tasks, {text: ''});
|
||||
if (found) printjson({id: user._id, auth: user.auth});
|
||||
});
|
||||
const found = _.some(user.tasks, { text: '' });
|
||||
if (found) printjson({ id: user._id, auth: user.auth });
|
||||
});
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
// mongo habitrpg ./node_modules/moment/moment.js ./migrations/freeMonth.js
|
||||
|
||||
db.users.update(
|
||||
{_id: ''},
|
||||
{$set: {
|
||||
'purchased.plan.customerId': 'temporary',
|
||||
'purchased.plan.paymentMethod': 'Stripe',
|
||||
'purchased.plan.planId': 'basic_earned',
|
||||
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
|
||||
}}
|
||||
{ _id: '' },
|
||||
{
|
||||
$set: {
|
||||
'purchased.plan.customerId': 'temporary',
|
||||
'purchased.plan.paymentMethod': 'Stripe',
|
||||
'purchased.plan.planId': 'basic_earned',
|
||||
'purchased.plan.dateTerminated': moment().add('month', 1).toDate(),
|
||||
},
|
||||
},
|
||||
);
|
||||
// var m = 12;
|
||||
// db.users.update(
|
||||
// {_id:''},
|
||||
// {$set:{'purchased.plan':{
|
||||
// planId: 'basic_'+m+'mo',
|
||||
// paymentMethod: 'Paypal',
|
||||
// customerId: 'Gift',
|
||||
// dateCreated: new Date(),
|
||||
// dateTerminated: moment().add('month',m).toDate(),
|
||||
// dateUpdated: new Date(),
|
||||
// extraMonths: 0,
|
||||
// gemsBought: 0,
|
||||
// mysteryItems: [],
|
||||
// consecutive: {
|
||||
// count: 0,
|
||||
// offset: m,
|
||||
// gemCapExtra: m/3*5,
|
||||
// trinkets: m/3
|
||||
// }
|
||||
// planId: 'basic_'+m+'mo',
|
||||
// paymentMethod: 'Paypal',
|
||||
// customerId: 'Gift',
|
||||
// dateCreated: new Date(),
|
||||
// dateTerminated: moment().add('month',m).toDate(),
|
||||
// dateUpdated: new Date(),
|
||||
// extraMonths: 0,
|
||||
// gemsBought: 0,
|
||||
// mysteryItems: [],
|
||||
// consecutive: {
|
||||
// count: 0,
|
||||
// offset: m,
|
||||
// gemCapExtra: m/3*5,
|
||||
// trinkets: m/3
|
||||
// }
|
||||
// }}}
|
||||
// )
|
||||
// )
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
db.users.update(
|
||||
{},
|
||||
{$inc: {'achievements.habiticaDays': 1}},
|
||||
{multi: 1}
|
||||
{ $inc: { 'achievements.habiticaDays': 1 } },
|
||||
{ multi: 1 },
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
db.users.update({_id: ''}, {$inc: {balance: 5}});
|
||||
db.users.update({ _id: '' }, { $inc: { balance: 5 } });
|
||||
|
||||
@@ -13,7 +13,7 @@ import { model as Group } from '../../website/server/models/group';
|
||||
|
||||
// @TODO: this should probably be a GroupManager library method
|
||||
async function addUnlimitedSubscription (groupId, dateTerminated) {
|
||||
let group = await Group.findOne({_id: groupId});
|
||||
const group = await Group.findOne({ _id: groupId });
|
||||
|
||||
group.purchased.plan.customerId = 'group-unlimited';
|
||||
group.purchased.plan.dateCreated = new Date();
|
||||
@@ -22,7 +22,7 @@ async function addUnlimitedSubscription (groupId, dateTerminated) {
|
||||
group.purchased.plan.planId = 'group_monthly';
|
||||
group.purchased.plan.dateTerminated = null;
|
||||
if (dateTerminated) {
|
||||
let dateToEnd = moment(dateTerminated).toDate();
|
||||
const dateToEnd = moment(dateTerminated).toDate();
|
||||
group.purchased.plan.dateTerminated = dateToEnd;
|
||||
}
|
||||
// group.purchased.plan.owner = ObjectId();
|
||||
@@ -31,12 +31,12 @@ async function addUnlimitedSubscription (groupId, dateTerminated) {
|
||||
return group.save();
|
||||
}
|
||||
|
||||
module.exports = async function addUnlimitedSubscriptionCreator () {
|
||||
let groupId = process.argv[2];
|
||||
export default async function addUnlimitedSubscriptionCreator () {
|
||||
const groupId = process.argv[2];
|
||||
|
||||
if (!groupId) throw Error('Group ID is required');
|
||||
|
||||
let dateTerminated = process.argv[3];
|
||||
const dateTerminated = process.argv[3];
|
||||
|
||||
await addUnlimitedSubscription(groupId, dateTerminated);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { model as User } from '../../website/server/models/user';
|
||||
|
||||
// @TODO: this should probably be a GroupManager library method
|
||||
async function createGroup (name, privacy, type, leaderId) {
|
||||
let user = await User.findOne({_id: leaderId});
|
||||
const user = await User.findOne({ _id: leaderId });
|
||||
|
||||
let group = new Group({
|
||||
const group = new Group({
|
||||
name,
|
||||
privacy,
|
||||
type,
|
||||
@@ -17,13 +17,11 @@ async function createGroup (name, privacy, type, leaderId) {
|
||||
return Promise.all([group.save(), user.save()]);
|
||||
}
|
||||
|
||||
module.exports = async function groupCreator () {
|
||||
let name = process.argv[2];
|
||||
let privacy = process.argv[3];
|
||||
let type = process.argv[4];
|
||||
let leaderId = process.argv[5];
|
||||
export default async function groupCreator () {
|
||||
const name = process.argv[2];
|
||||
const privacy = process.argv[3];
|
||||
const type = process.argv[4];
|
||||
const leaderId = process.argv[5];
|
||||
|
||||
await createGroup(name, privacy, type, leaderId);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
/* let migrationName = 'Jackalopes for Unlimited Subscribers'; */
|
||||
|
||||
/*
|
||||
* This migration will find users with unlimited subscriptions who are also eligible for Jackalope mounts, and award them
|
||||
* This migration will find users with unlimited subscriptions who are also eligible
|
||||
* for Jackalope mounts, and award them
|
||||
*/
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
async function handOutJackalopes () {
|
||||
let promises = [];
|
||||
let cursor = User.find({
|
||||
const promises = [];
|
||||
const cursor = User.find({
|
||||
'purchased.plan.customerId': 'habitrpg',
|
||||
}).cursor();
|
||||
|
||||
cursor.on('data', async (user) => {
|
||||
console.log(`User: ${ user._id}`);
|
||||
cursor.on('data', async user => {
|
||||
console.log(`User: ${user._id}`);
|
||||
|
||||
let groupList = [];
|
||||
if (user.party._id) groupList.push(user.party._id);
|
||||
groupList = groupList.concat(user.guilds);
|
||||
|
||||
let subscribedGroup =
|
||||
await Group.findOne({
|
||||
_id: {$in: groupList},
|
||||
'purchased.plan.planId': 'group_monthly',
|
||||
'purchased.plan.dateTerminated': null,
|
||||
},
|
||||
{_id: 1}
|
||||
);
|
||||
const subscribedGroup = await Group.findOne({
|
||||
_id: { $in: groupList },
|
||||
'purchased.plan.planId': 'group_monthly',
|
||||
'purchased.plan.dateTerminated': null,
|
||||
},
|
||||
{ _id: 1 });
|
||||
|
||||
if (subscribedGroup) {
|
||||
User.update({_id: user._id}, {$set: {'items.mounts.Jackalope-RoyalPurple': true}}).exec();
|
||||
User.update({ _id: user._id }, { $set: { 'items.mounts.Jackalope-RoyalPurple': true } }).exec();
|
||||
promises.push(user.save());
|
||||
}
|
||||
});
|
||||
|
||||
cursor.on('close', async () => {
|
||||
console.log('done');
|
||||
return await Promise.all(promises);
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = handOutJackalopes;
|
||||
export default handOutJackalopes;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import { model as Chat } from '../../website/server/models/chat';
|
||||
import { chatModel as Chat } from '../../website/server/models/message';
|
||||
|
||||
async function moveGroupChatToModel (skip = 0) {
|
||||
const groups = await Group.find({})
|
||||
@@ -40,7 +40,7 @@ async function moveGroupChatToModel (skip = 0) {
|
||||
|
||||
|
||||
const reducedPromises = promises.reduce((acc, curr) => {
|
||||
acc = acc.concat(curr);
|
||||
acc = acc.concat(curr); // eslint-disable-line no-param-reassign
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
@@ -49,4 +49,4 @@ async function moveGroupChatToModel (skip = 0) {
|
||||
moveGroupChatToModel(skip + 50);
|
||||
}
|
||||
|
||||
module.exports = moveGroupChatToModel;
|
||||
export default moveGroupChatToModel;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import monk from 'monk';
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import nconf from 'nconf';
|
||||
import stripePayments from '../../website/server/libs/payments/stripe';
|
||||
|
||||
@@ -12,8 +12,8 @@ import stripePayments from '../../website/server/libs/payments/stripe';
|
||||
|
||||
const CONNECTION_STRING = nconf.get('MIGRATION_CONNECT_STRING');
|
||||
|
||||
let dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false });
|
||||
let dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
const dbGroups = monk(CONNECTION_STRING).get('groups', { castIds: false });
|
||||
const dbUsers = monk(CONNECTION_STRING).get('users', { castIds: false });
|
||||
|
||||
async function fixGroupPlanMembers () {
|
||||
console.info('Group ID, Customer ID, Plan ID, Quantity, Recorded Member Count, Actual Member Count');
|
||||
@@ -24,15 +24,15 @@ async function fixGroupPlanMembers () {
|
||||
{
|
||||
$and:
|
||||
[
|
||||
{'purchased.plan.planId': {$ne: null}},
|
||||
{'purchased.plan.planId': {$ne: ''}},
|
||||
{'purchased.plan.customerId': {$ne: 'cus_9f0DV4g7WHRzpM'}}, // Demo groups
|
||||
{'purchased.plan.customerId': {$ne: 'cus_9maalqDOFTrvqx'}},
|
||||
{ 'purchased.plan.planId': { $ne: null } },
|
||||
{ 'purchased.plan.planId': { $ne: '' } },
|
||||
{ 'purchased.plan.customerId': { $ne: 'cus_9f0DV4g7WHRzpM' } }, // Demo groups
|
||||
{ 'purchased.plan.customerId': { $ne: 'cus_9maalqDOFTrvqx' } },
|
||||
],
|
||||
$or:
|
||||
[
|
||||
{'purchased.plan.dateTerminated': null},
|
||||
{'purchased.plan.dateTerminated': ''},
|
||||
{ 'purchased.plan.dateTerminated': null },
|
||||
{ 'purchased.plan.dateTerminated': '' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -40,19 +40,19 @@ async function fixGroupPlanMembers () {
|
||||
memberCount: 1,
|
||||
'purchased.plan': 1,
|
||||
},
|
||||
}
|
||||
).each(async (group, {close, pause, resume}) => { // eslint-disable-line no-unused-vars
|
||||
},
|
||||
).each(async (group, { close, pause, resume }) => { // eslint-disable-line no-unused-vars
|
||||
pause();
|
||||
groupPlanCount++;
|
||||
groupPlanCount += 1;
|
||||
|
||||
const canonicalMemberCount = await dbUsers.count(
|
||||
{
|
||||
$or:
|
||||
[
|
||||
{'party._id': group._id},
|
||||
{guilds: group._id},
|
||||
{ 'party._id': group._id },
|
||||
{ guilds: group._id },
|
||||
],
|
||||
}
|
||||
},
|
||||
);
|
||||
const incorrectMemberCount = group.memberCount !== canonicalMemberCount;
|
||||
|
||||
@@ -73,24 +73,24 @@ async function fixGroupPlanMembers () {
|
||||
$set: {
|
||||
memberCount: canonicalMemberCount,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (!groupUpdate) return;
|
||||
|
||||
fixedGroupCount++;
|
||||
fixedGroupCount += 1;
|
||||
if (group.purchased.plan.paymentMethod === 'Stripe') {
|
||||
await stripePayments.chargeForAdditionalGroupMember(group);
|
||||
await dbGroups.update(
|
||||
{_id: group._id},
|
||||
{$set: {'purchased.plan.quantity': canonicalMemberCount + 2}}
|
||||
{ _id: group._id },
|
||||
{ $set: { 'purchased.plan.quantity': canonicalMemberCount + 2 } },
|
||||
);
|
||||
}
|
||||
|
||||
if (incorrectQuantity) {
|
||||
await dbGroups.update(
|
||||
{_id: group._id},
|
||||
{$set: {'purchased.plan.quantity': canonicalMemberCount + 2}}
|
||||
{ _id: group._id },
|
||||
{ $set: { 'purchased.plan.quantity': canonicalMemberCount + 2 } },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -98,10 +98,10 @@ async function fixGroupPlanMembers () {
|
||||
}).then(() => {
|
||||
console.info(`Fixed ${fixedGroupCount} out of ${groupPlanCount} active Group Plans`);
|
||||
return process.exit(0);
|
||||
}).catch((err) => {
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
return process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = fixGroupPlanMembers;
|
||||
export default fixGroupPlanMembers;
|
||||
|
||||
@@ -5,29 +5,27 @@ let authorUuid = ''; // ... own data is done
|
||||
*/
|
||||
|
||||
/*
|
||||
* This migrations will iterate through all groups with a group plan a subscription and resync the free
|
||||
* subscription to all members
|
||||
* This migrations will iterate through all groups with a group plan
|
||||
* a subscription and resync the free subscription to all members
|
||||
*/
|
||||
|
||||
import { model as Group } from '../../website/server/models/group';
|
||||
import * as payments from '../../website/server/libs/payments';
|
||||
import payments from '../../website/server/libs/payments/payments';
|
||||
|
||||
async function updateGroupsWithGroupPlans () {
|
||||
let cursor = Group.find({
|
||||
const cursor = Group.find({
|
||||
'purchased.plan.planId': 'group_monthly',
|
||||
'purchased.plan.dateTerminated': null,
|
||||
}).cursor();
|
||||
|
||||
let promises = [];
|
||||
const promises = [];
|
||||
|
||||
cursor.on('data', (group) => {
|
||||
cursor.on('data', group => {
|
||||
promises.push(payments.addSubscriptionToGroupUsers(group));
|
||||
promises.push(group.save());
|
||||
});
|
||||
|
||||
cursor.on('close', async () => {
|
||||
return await Promise.all(promises);
|
||||
});
|
||||
cursor.on('close', async () => Promise.all(promises));
|
||||
}
|
||||
|
||||
module.exports = updateGroupsWithGroupPlans;
|
||||
export default updateGroupsWithGroupPlans;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require('babel-register');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
require('@babel/register'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
// This file must use ES5, everything required can be in ES6
|
||||
|
||||
@@ -17,12 +18,13 @@ function setUpServer () {
|
||||
setUpServer();
|
||||
|
||||
// Replace this with your migration
|
||||
const processUsers = require('');
|
||||
const processUsers = () => {}; // require('').default;
|
||||
|
||||
processUsers()
|
||||
.then(function success () {
|
||||
.then(() => {
|
||||
process.exit(0);
|
||||
})
|
||||
.catch(function failure (err) {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
/* let migrationName = 'new_stuff.js'; */
|
||||
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
|
||||
/*
|
||||
* set the newStuff flag in all user accounts so they see a Bailey message
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
'flags.newStuff': {$ne: true},
|
||||
const query = {
|
||||
'flags.newStuff': { $ne: true },
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
@@ -23,29 +25,31 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
// specify fields we are interested in to limit retrieved data
|
||||
// (empty if we're not reading data):
|
||||
fields: [],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let userPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const userPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(() => {
|
||||
@@ -54,30 +58,31 @@ function updateUsers (users) {
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
let set = {'flags.newStuff': true};
|
||||
const 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 (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
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`);
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
let migrationName = 'restock_armoire.js';
|
||||
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const migrationName = 'restock_armoire.js';
|
||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
const 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
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
const query = {
|
||||
'flags.armoireEmpty': true,
|
||||
};
|
||||
|
||||
@@ -23,29 +25,31 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
fields: [], // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
// specify fields we are interested in to limit retrieved data
|
||||
// (empty if we're not reading data):
|
||||
fields: [],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let userPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const userPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(() => {
|
||||
@@ -54,30 +58,31 @@ function updateUsers (users) {
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
let set = {migration: migrationName, 'flags.armoireEmpty': false};
|
||||
const 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 (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
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`);
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
let migrationName = 'restock_armoire_for_users_that_need_it.js';
|
||||
let authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ...
|
||||
let authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const migrationName = 'restock_armoire_for_users_that_need_it.js';
|
||||
const authorName = 'Alys (ALittleYellowSpider)'; // in case script author needs to know when their ...
|
||||
const authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Remove flag stating that the Enchanted Armoire is empty,
|
||||
@@ -18,16 +19,17 @@ let authorUuid = '3e595299-3d8a-4a10-bfe0-88f555e4aa0c'; // ... own data is done
|
||||
*
|
||||
*/
|
||||
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
let monk = require('monk');
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
'auth.timestamps.loggedin': {$gt: new Date('2016-01-04')},
|
||||
const query = {
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2016-01-04') },
|
||||
// '_id': authorUuid // FOR TESTING
|
||||
};
|
||||
|
||||
@@ -35,7 +37,7 @@ function processUsers (lastId) {
|
||||
/* let fields = {
|
||||
'flags.armoireEmpty': 1,
|
||||
'items.gear.owned': 1,
|
||||
};*/
|
||||
}; */
|
||||
|
||||
if (lastId) {
|
||||
query._id = {
|
||||
@@ -44,32 +46,34 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
// specify fields we are interested in to limit retrieved data
|
||||
// (empty if we're not reading data):
|
||||
fields: {
|
||||
'flags.armoireEmpty': 1,
|
||||
'items.gear.owned': 1,
|
||||
}, // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
},
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let userPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const userPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(() => {
|
||||
@@ -78,19 +82,69 @@ function updateUsers (users) {
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
let set = {migration: migrationName, 'flags.armoireEmpty': false};
|
||||
const set = { migration: migrationName, 'flags.armoireEmpty': false };
|
||||
|
||||
|
||||
if (user.flags.armoireEmpty) {
|
||||
// this user believes their armoire has no more items in it
|
||||
if (user.items.gear.owned.weapon_armoire_barristerGavel && user.items.gear.owned.armor_armoire_barristerRobes && user.items.gear.owned.head_armoire_jesterCap && user.items.gear.owned.armor_armoire_jesterCostume && user.items.gear.owned.head_armoire_barristerWig && user.items.gear.owned.weapon_armoire_jesterBaton && user.items.gear.owned.weapon_armoire_lunarSceptre && user.items.gear.owned.armor_armoire_gladiatorArmor && user.items.gear.owned.weapon_armoire_basicCrossbow && user.items.gear.owned.head_armoire_gladiatorHelm && user.items.gear.owned.armor_armoire_lunarArmor && user.items.gear.owned.head_armoire_redHairbow && user.items.gear.owned.head_armoire_violetFloppyHat && user.items.gear.owned.head_armoire_rancherHat && user.items.gear.owned.shield_armoire_gladiatorShield && user.items.gear.owned.head_armoire_blueHairbow && user.items.gear.owned.weapon_armoire_mythmakerSword && user.items.gear.owned.head_armoire_royalCrown && user.items.gear.owned.head_armoire_hornedIronHelm && user.items.gear.owned.weapon_armoire_rancherLasso && user.items.gear.owned.armor_armoire_rancherRobes && user.items.gear.owned.armor_armoire_hornedIronArmor && user.items.gear.owned.armor_armoire_goldenToga && user.items.gear.owned.weapon_armoire_ironCrook && user.items.gear.owned.head_armoire_goldenLaurels && user.items.gear.owned.head_armoire_redFloppyHat && user.items.gear.owned.armor_armoire_plagueDoctorOvercoat && user.items.gear.owned.head_armoire_plagueDoctorHat && user.items.gear.owned.weapon_armoire_goldWingStaff && user.items.gear.owned.head_armoire_yellowHairbow && user.items.gear.owned.eyewear_armoire_plagueDoctorMask && user.items.gear.owned.head_armoire_blackCat && user.items.gear.owned.weapon_armoire_batWand && user.items.gear.owned.head_armoire_orangeCat && user.items.gear.owned.shield_armoire_midnightShield && user.items.gear.owned.armor_armoire_royalRobes && user.items.gear.owned.head_armoire_blueFloppyHat && user.items.gear.owned.shield_armoire_royalCane && user.items.gear.owned.weapon_armoire_shepherdsCrook && user.items.gear.owned.armor_armoire_shepherdRobes && user.items.gear.owned.head_armoire_shepherdHeaddress && user.items.gear.owned.weapon_armoire_blueLongbow && user.items.gear.owned.weapon_armoire_crystalCrescentStaff && user.items.gear.owned.head_armoire_crystalCrescentHat && user.items.gear.owned.armor_armoire_dragonTamerArmor && user.items.gear.owned.head_armoire_dragonTamerHelm && user.items.gear.owned.armor_armoire_crystalCrescentRobes && user.items.gear.owned.shield_armoire_dragonTamerShield && user.items.gear.owned.weapon_armoire_glowingSpear) {
|
||||
if (
|
||||
user.items.gear.owned.weapon_armoire_barristerGavel
|
||||
&& user.items.gear.owned.armor_armoire_barristerRobes
|
||||
&& user.items.gear.owned.head_armoire_jesterCap
|
||||
&& user.items.gear.owned.armor_armoire_jesterCostume
|
||||
&& user.items.gear.owned.head_armoire_barristerWig
|
||||
&& user.items.gear.owned.weapon_armoire_jesterBaton
|
||||
&& user.items.gear.owned.weapon_armoire_lunarSceptre
|
||||
&& user.items.gear.owned.armor_armoire_gladiatorArmor
|
||||
&& user.items.gear.owned.weapon_armoire_basicCrossbow
|
||||
&& user.items.gear.owned.head_armoire_gladiatorHelm
|
||||
&& user.items.gear.owned.armor_armoire_lunarArmor
|
||||
&& user.items.gear.owned.head_armoire_redHairbow
|
||||
&& user.items.gear.owned.head_armoire_violetFloppyHat
|
||||
&& user.items.gear.owned.head_armoire_rancherHat
|
||||
&& user.items.gear.owned.shield_armoire_gladiatorShield
|
||||
&& user.items.gear.owned.head_armoire_blueHairbow
|
||||
&& user.items.gear.owned.weapon_armoire_mythmakerSword
|
||||
&& user.items.gear.owned.head_armoire_royalCrown
|
||||
&& user.items.gear.owned.head_armoire_hornedIronHelm
|
||||
&& user.items.gear.owned.weapon_armoire_rancherLasso
|
||||
&& user.items.gear.owned.armor_armoire_rancherRobes
|
||||
&& user.items.gear.owned.armor_armoire_hornedIronArmor
|
||||
&& user.items.gear.owned.armor_armoire_goldenToga
|
||||
&& user.items.gear.owned.weapon_armoire_ironCrook
|
||||
&& user.items.gear.owned.head_armoire_goldenLaurels
|
||||
&& user.items.gear.owned.head_armoire_redFloppyHat
|
||||
&& user.items.gear.owned.armor_armoire_plagueDoctorOvercoat
|
||||
&& user.items.gear.owned.head_armoire_plagueDoctorHat
|
||||
&& user.items.gear.owned.weapon_armoire_goldWingStaff
|
||||
&& user.items.gear.owned.head_armoire_yellowHairbow
|
||||
&& user.items.gear.owned.eyewear_armoire_plagueDoctorMask
|
||||
&& user.items.gear.owned.head_armoire_blackCat
|
||||
&& user.items.gear.owned.weapon_armoire_batWand
|
||||
&& user.items.gear.owned.head_armoire_orangeCat
|
||||
&& user.items.gear.owned.shield_armoire_midnightShield
|
||||
&& user.items.gear.owned.armor_armoire_royalRobes
|
||||
&& user.items.gear.owned.head_armoire_blueFloppyHat
|
||||
&& user.items.gear.owned.shield_armoire_royalCane
|
||||
&& user.items.gear.owned.weapon_armoire_shepherdsCrook
|
||||
&& user.items.gear.owned.armor_armoire_shepherdRobes
|
||||
&& user.items.gear.owned.head_armoire_shepherdHeaddress
|
||||
&& user.items.gear.owned.weapon_armoire_blueLongbow
|
||||
&& user.items.gear.owned.weapon_armoire_crystalCrescentStaff
|
||||
&& user.items.gear.owned.head_armoire_crystalCrescentHat
|
||||
&& user.items.gear.owned.armor_armoire_dragonTamerArmor
|
||||
&& user.items.gear.owned.head_armoire_dragonTamerHelm
|
||||
&& user.items.gear.owned.armor_armoire_crystalCrescentRobes
|
||||
&& user.items.gear.owned.shield_armoire_dragonTamerShield
|
||||
&& user.items.gear.owned.weapon_armoire_glowingSpear
|
||||
) {
|
||||
// this user does have all the armoire items so we don't change the flag
|
||||
// console.log("don't change: " + user._id); // FOR TESTING
|
||||
} else {
|
||||
// console.log("change: " + user._id); // FOR TESTING
|
||||
dbUsers.update({_id: user._id}, {$set: set});
|
||||
dbUsers.update({ _id: user._id }, { $set: set });
|
||||
}
|
||||
} else {
|
||||
// this user already has armoire marked as containing items to be bought
|
||||
@@ -98,24 +152,25 @@ function updateUser (user) {
|
||||
// console.log("DON'T CHANGE: " + user._id); // FOR TESTING
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
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`);
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
/* let migrationName = 'restore_profile_data.js'; */
|
||||
let authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
|
||||
let authorUuid = ''; // ... own data is done
|
||||
const authorName = 'ThehollidayInn'; // in case script author needs to know when their ...
|
||||
const authorUuid = ''; // ... own data is done
|
||||
|
||||
/*
|
||||
* Check if users have empty profile data in new database and update it with old database info
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let connectionString = ''; // FOR TEST DATABASE
|
||||
let dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
let monk2 = require('monk');
|
||||
let oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
let olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
|
||||
const connectionString = ''; // FOR TEST DATABASE
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
const monk2 = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const oldDbConnectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const olDbUsers = monk2(oldDbConnectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
const query = {
|
||||
// 'profile.name': 'profile name not found',
|
||||
'profile.blurb': null,
|
||||
// 'auth.timestamps.loggedin': {$gt: new Date('11/30/2016')},
|
||||
@@ -29,49 +32,47 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
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((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
setTimeout(displayData, 300000);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let userPaymentPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const userPaymentPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPaymentPromises)
|
||||
.then(() => {
|
||||
return processUsers(lastUser._id);
|
||||
});
|
||||
.then(() => processUsers(lastUser._id));
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
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;
|
||||
return olDbUsers.findOne({ _id: user._id }, '_id profile')
|
||||
.then(oldUserData => {
|
||||
if (!oldUserData) return null;
|
||||
// specify user data to change:
|
||||
let set = {};
|
||||
const set = {};
|
||||
|
||||
if (oldUserData.profile.name === 'profile name not found') return;
|
||||
if (oldUserData.profile.name === 'profile name not found') return null;
|
||||
|
||||
let userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
|
||||
const userNeedsProfileName = !user.profile.name || user.profile.name === 'profile name not found';
|
||||
if (userNeedsProfileName && oldUserData.profile.name) {
|
||||
set['profile.name'] = oldUserData.profile.name;
|
||||
}
|
||||
@@ -86,29 +87,34 @@ function updateUser (user) {
|
||||
|
||||
if (Object.keys(set).length !== 0 && set.constructor === Object) {
|
||||
console.log(set);
|
||||
return dbUsers.update({_id: user._id}, {$set: set});
|
||||
return dbUsers.update({ _id: user._id }, { $set: set });
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName} processed`);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
console.warn(`\n${ count } users processed\n`);
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
let request = require('superagent');
|
||||
let last = require('lodash/last');
|
||||
let AWS = require('aws-sdk');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const request = require('superagent');
|
||||
const last = require('lodash/last');
|
||||
const AWS = require('aws-sdk');
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
let config = require('../config');
|
||||
const S3_DIRECTORY = 'mobileApp/images'; // config.S3.SPRITES_DIRECTORY;
|
||||
|
||||
AWS.config.update({
|
||||
@@ -11,8 +13,8 @@ AWS.config.update({
|
||||
// region: config.get('S3_REGION'),
|
||||
});
|
||||
|
||||
let BUCKET_NAME = config.S3.bucket;
|
||||
let s3 = new AWS.S3();
|
||||
const BUCKET_NAME = config.S3.bucket;
|
||||
const s3 = new AWS.S3();
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/22210077/2601552
|
||||
function uploadFile (buffer, fileName) {
|
||||
@@ -21,7 +23,7 @@ function uploadFile (buffer, fileName) {
|
||||
Body: buffer,
|
||||
Key: fileName,
|
||||
Bucket: BUCKET_NAME,
|
||||
}, (error) => {
|
||||
}, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@@ -33,9 +35,9 @@ function uploadFile (buffer, fileName) {
|
||||
}
|
||||
|
||||
function getFileName (file) {
|
||||
let piecesOfPath = file.split('/');
|
||||
let name = last(piecesOfPath);
|
||||
let fullName = S3_DIRECTORY + name;
|
||||
const piecesOfPath = file.split('/');
|
||||
const name = last(piecesOfPath);
|
||||
const fullName = S3_DIRECTORY + name;
|
||||
|
||||
return fullName;
|
||||
}
|
||||
@@ -44,36 +46,32 @@ function getFileFromUrl (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.get(url).end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
let file = res.body;
|
||||
resolve(file);
|
||||
const file = res.body;
|
||||
return resolve(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let commit = '78f94e365c72cc58f66857d5941105638db7d35c';
|
||||
commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015';
|
||||
let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`;
|
||||
const gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`;
|
||||
|
||||
|
||||
let currentIndex = 0;
|
||||
|
||||
function uploadToS3 (start, end, filesUrls) {
|
||||
let urls = filesUrls.slice(start, end);
|
||||
const urls = filesUrls.slice(start, end);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.log('done');
|
||||
return;
|
||||
}
|
||||
|
||||
let promises = urls.map(fullUrl => {
|
||||
return getFileFromUrl(fullUrl)
|
||||
.then((buffer) => {
|
||||
return uploadFile(buffer, getFileName(fullUrl));
|
||||
});
|
||||
});
|
||||
const promises = urls.map(fullUrl => getFileFromUrl(fullUrl)
|
||||
.then(buffer => uploadFile(buffer, getFileName(fullUrl))));
|
||||
console.log(promises.length);
|
||||
|
||||
return Promise.all(promises)
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
currentIndex += 50;
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
@@ -86,12 +84,10 @@ function uploadToS3 (start, end, filesUrls) {
|
||||
request.get(gihuburl)
|
||||
.end((err, res) => {
|
||||
console.log(err);
|
||||
let files = res.body.files;
|
||||
const { files } = res.body;
|
||||
|
||||
let filesUrls = [''];
|
||||
filesUrls = files.map(file => {
|
||||
return file.raw_url;
|
||||
});
|
||||
filesUrls = files.map(file => file.raw_url);
|
||||
|
||||
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
|
||||
});
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
// const migrationName = 'habits-one-history-entry-per-day';
|
||||
// const authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
// const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Iterates over all habits and condense multiple history entries for the same day into a single entry
|
||||
* Iterates over all habits and condense multiple history entries for the same day into a single one
|
||||
*/
|
||||
|
||||
const monk = require('monk');
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
|
||||
function processChallengeHabits (lastId) {
|
||||
let query = {
|
||||
'challenge.id': {$exists: true},
|
||||
userId: {$exists: false},
|
||||
const query = {
|
||||
'challenge.id': { $exists: true },
|
||||
userId: { $exists: false },
|
||||
type: 'habit',
|
||||
};
|
||||
|
||||
@@ -26,37 +28,35 @@ function processChallengeHabits (lastId) {
|
||||
}
|
||||
|
||||
dbTasks.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 500,
|
||||
})
|
||||
.then(updateChallengeHabits)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateChallengeHabits (habits) {
|
||||
if (!habits || habits.length === 0) {
|
||||
console.warn('All appropriate challenge habits found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let habitsPromises = habits.map(updateChallengeHabit);
|
||||
let lastHabit = habits[habits.length - 1];
|
||||
const habitsPromises = habits.map(updateChallengeHabit);
|
||||
const lastHabit = habits[habits.length - 1];
|
||||
|
||||
return Promise.all(habitsPromises)
|
||||
.then(() => {
|
||||
return processChallengeHabits(lastHabit._id);
|
||||
});
|
||||
.then(() => processChallengeHabits(lastHabit._id));
|
||||
}
|
||||
|
||||
function updateChallengeHabit (habit) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
if (habit && habit.history && habit.history.length > 0) {
|
||||
// First remove missing entries
|
||||
@@ -76,13 +76,12 @@ function updateChallengeHabit (habit) {
|
||||
entry.scoreDirection = entry.value > previousValue ? 'up' : 'down';
|
||||
}
|
||||
})
|
||||
.groupBy(entry => { // group entries by aggregateBy
|
||||
return moment(entry.date).format('YYYYMMDD');
|
||||
})
|
||||
// group entries by aggregateBy
|
||||
.groupBy(entry => moment(entry.date).format('YYYYMMDD'))
|
||||
.toPairs() // [key, entry]
|
||||
.sortBy(([key]) => key) // sort by date
|
||||
.map(keyEntryPair => {
|
||||
let entries = keyEntryPair[1]; // 1 is entry, 0 is key
|
||||
const entries = keyEntryPair[1]; // 1 is entry, 0 is key
|
||||
let scoredUp = 0;
|
||||
let scoredDown = 0;
|
||||
|
||||
@@ -107,32 +106,34 @@ function updateChallengeHabit (habit) {
|
||||
})
|
||||
.value();
|
||||
|
||||
return dbTasks.update({_id: habit._id}, {
|
||||
$set: {history: habit.history},
|
||||
return dbTasks.update({ _id: habit._id }, {
|
||||
$set: { history: habit.history },
|
||||
});
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } habits processed`);
|
||||
if (count % progressCount === 0) console.warn(`${count} habits processed`);
|
||||
return null;
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
console.warn(`\n${ count } tasks processed\n`);
|
||||
console.warn(`\n${count} tasks processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processChallengeHabits;
|
||||
export default processChallengeHabits;
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const migrationName = 'habits-one-history-entry-per-day';
|
||||
const authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Iterates over all habits and condense multiple history entries for the same day into a single entry
|
||||
* Iterates over all habits and condense multiple history entries for the same day into a single one
|
||||
*/
|
||||
|
||||
const monk = require('monk');
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
const query = {
|
||||
migration: { $ne: migrationName },
|
||||
};
|
||||
|
||||
if (lastId) {
|
||||
@@ -25,34 +27,32 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 50, // just 50 users per time since we have to process all their habits as well
|
||||
fields: ['_id', 'preferences.timezoneOffset', 'preferences.dayStart'],
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users and their tasks found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let usersPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const usersPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(usersPromises)
|
||||
.then(() => {
|
||||
return processUsers(lastUser._id);
|
||||
});
|
||||
.then(() => processUsers(lastUser._id));
|
||||
}
|
||||
|
||||
function updateHabit (habit, timezoneOffset, dayStart) {
|
||||
@@ -82,7 +82,7 @@ function updateHabit (habit, timezoneOffset, dayStart) {
|
||||
.toPairs() // [key, entry]
|
||||
.sortBy(([key]) => key) // sort by date
|
||||
.map(keyEntryPair => {
|
||||
let entries = keyEntryPair[1]; // 1 is entry, 0 is key
|
||||
const entries = keyEntryPair[1]; // 1 is entry, 0 is key
|
||||
let scoredUp = 0;
|
||||
let scoredDown = 0;
|
||||
|
||||
@@ -107,57 +107,56 @@ function updateHabit (habit, timezoneOffset, dayStart) {
|
||||
})
|
||||
.value();
|
||||
|
||||
return dbTasks.update({_id: habit._id}, {
|
||||
$set: {history: habit.history},
|
||||
return dbTasks.update({ _id: habit._id }, {
|
||||
$set: { history: habit.history },
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
const timezoneOffset = user.preferences.timezoneOffset;
|
||||
const dayStart = user.preferences.dayStart;
|
||||
const { timezoneOffset } = user.preferences;
|
||||
const { dayStart } = user.preferences;
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName } being processed`);
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName} being processed`);
|
||||
|
||||
return dbTasks.find({
|
||||
type: 'habit',
|
||||
userId: user._id,
|
||||
})
|
||||
.then(habits => {
|
||||
return Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart)));
|
||||
})
|
||||
.then(() => {
|
||||
return dbUsers.update({_id: user._id}, {
|
||||
$set: {migration: migrationName},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.then(habits => Promise.all(habits.map(habit => updateHabit(habit, timezoneOffset, dayStart))))
|
||||
.then(() => dbUsers.update({ _id: user._id }, {
|
||||
$set: { migration: migrationName },
|
||||
}))
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
console.warn(`\n${ count } tasks processed\n`);
|
||||
console.warn(`\n${count} tasks processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
export default processUsers;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
/* let migrationName = 'tasks-set-everyX'; */
|
||||
let authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
let authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
const authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||
const authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; // ... own data is done
|
||||
|
||||
/*
|
||||
* Iterates over all tasks and sets invalid everyX values (less than 0 or more than 9999 or not an int) field to 0
|
||||
* Iterates over all tasks and sets invalid everyX values
|
||||
* (less than 0 or more than 9999 or not an int) field to 0
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true';
|
||||
let dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true';
|
||||
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
|
||||
function processTasks (lastId) {
|
||||
// specify a query to limit the affected tasks (empty for all tasks):
|
||||
let query = {
|
||||
const query = {
|
||||
type: 'daily',
|
||||
everyX: {
|
||||
$not: {
|
||||
@@ -30,64 +31,63 @@ function processTasks (lastId) {
|
||||
}
|
||||
|
||||
dbTasks.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
fields: [],
|
||||
})
|
||||
.then(updateTasks)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateTasks (tasks) {
|
||||
if (!tasks || tasks.length === 0) {
|
||||
console.warn('All appropriate tasks found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let taskPromises = tasks.map(updatetask);
|
||||
let lasttask = tasks[tasks.length - 1];
|
||||
const taskPromises = tasks.map(updatetask);
|
||||
const lasttask = tasks[tasks.length - 1];
|
||||
|
||||
return Promise.all(taskPromises)
|
||||
.then(() => {
|
||||
return processTasks(lasttask._id);
|
||||
});
|
||||
.then(() => processTasks(lasttask._id));
|
||||
}
|
||||
|
||||
function updatetask (task) {
|
||||
count++;
|
||||
let set = {everyX: 0};
|
||||
count += 1;
|
||||
const set = { everyX: 0 };
|
||||
|
||||
dbTasks.update({_id: task._id}, {$set: set});
|
||||
dbTasks.update({ _id: task._id }, { $set: set });
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ task._id}`);
|
||||
if (task._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
if (count % progressCount === 0) console.warn(`${count} ${task._id}`);
|
||||
if (task._id === authorUuid) console.warn(`${authorName} processed`);
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
console.warn(`\n${ count } tasks processed\n`);
|
||||
console.warn(`\n${count} tasks processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processTasks;
|
||||
export default processTasks;
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
/* let migrationName = 'tasks-set-yesterdaily'; */
|
||||
let authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
let authorUuid = ''; // ... own data is done
|
||||
// ... own data is done
|
||||
|
||||
/*
|
||||
* Iterates over all tasks and sets the yseterDaily field to True
|
||||
*/
|
||||
|
||||
import monk from 'monk';
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
let connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
let dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
const authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
const authorUuid = '';
|
||||
|
||||
let progressCount = 1000;
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
const dbTasks = monk(connectionString).get('tasks', { castIds: false });
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
@@ -30,39 +33,37 @@ function exiting (code, msg) {
|
||||
}
|
||||
|
||||
function displayData () {
|
||||
console.warn(`\n${ count } tasks processed\n`);
|
||||
console.warn(`\n${count} tasks processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function updatetask (task) {
|
||||
count++;
|
||||
let set = {yesterDaily: true};
|
||||
count += 1;
|
||||
const set = { yesterDaily: true };
|
||||
|
||||
dbTasks.update({_id: task._id}, {$set: set});
|
||||
dbTasks.update({ _id: task._id }, { $set: set });
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ task._id}`);
|
||||
if (task._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
if (count % progressCount === 0) console.warn(`${count} ${task._id}`);
|
||||
if (task._id === authorUuid) console.warn(`${authorName} processed`);
|
||||
}
|
||||
|
||||
function updateTasks (tasks) {
|
||||
if (!tasks || tasks.length === 0) {
|
||||
console.warn('All appropriate tasks found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let taskPromises = tasks.map(updatetask);
|
||||
let lasttask = tasks[tasks.length - 1];
|
||||
const taskPromises = tasks.map(updatetask);
|
||||
const lasttask = tasks[tasks.length - 1];
|
||||
|
||||
return Promise.all(taskPromises)
|
||||
.then(() => {
|
||||
return processTasks(lasttask._id); // eslint-disable-line no-use-before-define
|
||||
});
|
||||
.then(() => processTasks(lasttask._id)); // eslint-disable-line no-use-before-define
|
||||
}
|
||||
|
||||
function processTasks (lastId) {
|
||||
// specify a query to limit the affected tasks (empty for all tasks):
|
||||
let query = {
|
||||
const query = {
|
||||
yesterDaily: false,
|
||||
};
|
||||
|
||||
@@ -73,16 +74,18 @@ function processTasks (lastId) {
|
||||
}
|
||||
|
||||
dbTasks.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
|
||||
// specify fields we are interested in to limit retrieved data
|
||||
// (empty if we're not reading data):
|
||||
fields: [
|
||||
],
|
||||
})
|
||||
.then(updateTasks)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = processTasks;
|
||||
export default processTasks;
|
||||
|
||||
@@ -8,29 +8,30 @@ let authorUuid = ''; // ... own data is done
|
||||
* This migraition will copy user data from prod to test
|
||||
*/
|
||||
|
||||
const monk = require('monk');
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const connectionString = '';
|
||||
const Users = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
module.exports = async function accountTransfer () {
|
||||
export default async function accountTransfer () {
|
||||
const fromAccountId = '';
|
||||
const toAccountId = '';
|
||||
|
||||
const fromAccount = await Users.findOne({_id: fromAccountId});
|
||||
const toAccount = await Users.findOne({_id: toAccountId});
|
||||
const fromAccount = await Users.findOne({ _id: fromAccountId });
|
||||
const toAccount = await Users.findOne({ _id: toAccountId });
|
||||
|
||||
const newMounts = Object.assign({}, fromAccount.items.mounts, toAccount.items.mounts);
|
||||
const newPets = Object.assign({}, fromAccount.items.pets, toAccount.items.pets);
|
||||
const newBackgrounds = Object.assign({}, fromAccount.purchased.background, toAccount.purchased.background);
|
||||
const newMounts = { ...fromAccount.items.mounts, ...toAccount.items.mounts };
|
||||
const newPets = { ...fromAccount.items.pets, ...toAccount.items.pets };
|
||||
const newBackgrounds = { ...fromAccount.purchased.background, ...toAccount.purchased.background };
|
||||
|
||||
await Users.update({_id: toAccountId}, {
|
||||
await Users.update({ _id: toAccountId }, {
|
||||
$set: {
|
||||
'items.pets': newPets,
|
||||
'items.mounts': newMounts,
|
||||
'purchased.background': newBackgrounds,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ const authorUuid = ''; // ... own data is done
|
||||
* This migraition will copy user data from prod to test
|
||||
*/
|
||||
|
||||
const monk = require('monk');
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const connectionString = 'mongodb://localhost/new-habit';
|
||||
const Users = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
@@ -19,15 +20,17 @@ function getAchievementUpdate (newUser, oldUser) {
|
||||
const oldAchievements = oldUser.achievements;
|
||||
const newAchievements = newUser.achievements;
|
||||
|
||||
let achievementsUpdate = Object.assign({}, newAchievements);
|
||||
const achievementsUpdate = { ...newAchievements };
|
||||
|
||||
// ultimateGearSets
|
||||
if (!achievementsUpdate.ultimateGearSets && oldAchievements.ultimateGearSets) {
|
||||
achievementsUpdate.ultimateGearSets = oldAchievements.ultimateGearSets;
|
||||
} else if (oldAchievements.ultimateGearSets) {
|
||||
for (let index in oldAchievements.ultimateGearSets) {
|
||||
if (oldAchievements.ultimateGearSets[index]) achievementsUpdate.ultimateGearSets[index] = true;
|
||||
}
|
||||
Object.keys(oldAchievements.ultimateGearSets).forEach(index => {
|
||||
if (oldAchievements.ultimateGearSets[index]) {
|
||||
achievementsUpdate.ultimateGearSets[index] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// challenges
|
||||
@@ -37,57 +40,57 @@ function getAchievementUpdate (newUser, oldUser) {
|
||||
|
||||
// Quests
|
||||
if (!achievementsUpdate.quests) achievementsUpdate.quests = {};
|
||||
for (let index in oldAchievements.quests) {
|
||||
Object.keys(oldAchievements.quests).forEach(index => {
|
||||
if (!achievementsUpdate.quests[index]) {
|
||||
achievementsUpdate.quests[index] = oldAchievements.quests[index];
|
||||
} else {
|
||||
achievementsUpdate.quests[index] += oldAchievements.quests[index];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Rebirth level
|
||||
if (achievementsUpdate.rebirthLevel) {
|
||||
achievementsUpdate.rebirthLevel = Math.max(achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel);
|
||||
achievementsUpdate.rebirthLevel = Math.max(
|
||||
achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel,
|
||||
);
|
||||
} else if (oldAchievements.rebirthLevel) {
|
||||
achievementsUpdate.rebirthLevel = oldAchievements.rebirthLevel;
|
||||
}
|
||||
|
||||
// All others
|
||||
const indexsToIgnore = ['ultimateGearSets', 'challenges', 'quests', 'rebirthLevel'];
|
||||
for (let index in oldAchievements) {
|
||||
if (indexsToIgnore.indexOf(index) !== -1) continue; // eslint-disable-line no-continue
|
||||
Object.keys(oldAchievements).forEach(index => {
|
||||
if (indexsToIgnore.indexOf(index) !== -1) return;
|
||||
|
||||
if (!achievementsUpdate[index]) {
|
||||
if (!achievementsUpdate[index]) {
|
||||
achievementsUpdate[index] = oldAchievements[index];
|
||||
continue; // eslint-disable-line no-continue
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number.isInteger(oldAchievements[index])) {
|
||||
achievementsUpdate[index] += oldAchievements[index];
|
||||
} else if (oldAchievements[index] === true) achievementsUpdate[index] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return achievementsUpdate;
|
||||
}
|
||||
|
||||
module.exports = async function achievementRestore () {
|
||||
export default async function achievementRestore () {
|
||||
const userIds = [
|
||||
];
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
for (let index in userIds) {
|
||||
const userId = userIds[index];
|
||||
const oldUser = await UsersOld.findOne({_id: userId}, 'achievements');
|
||||
const newUser = await Users.findOne({_id: userId}, 'achievements');
|
||||
await Promise.all(userIds.map(userId => (async () => {
|
||||
const oldUser = await UsersOld.findOne({ _id: userId }, 'achievements');
|
||||
const newUser = await Users.findOne({ _id: userId }, 'achievements');
|
||||
const achievementUpdate = getAchievementUpdate(newUser, oldUser);
|
||||
await Users.update(
|
||||
{_id: userId},
|
||||
{ _id: userId },
|
||||
{
|
||||
$set: {
|
||||
achievements: achievementUpdate,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
console.log(`Updated ${userId}`);
|
||||
/* eslint-enable no-await-in-loop */
|
||||
}
|
||||
};
|
||||
})()));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable no-console */
|
||||
import { sendTxn } from '../../website/server/libs/email';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
import moment from 'moment';
|
||||
import nconf from 'nconf';
|
||||
import { sendTxn } from '../../website/server/libs/email';
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
const EMAIL_SLUG = 'mandrill-email-slug'; // Set email template to send
|
||||
const MIGRATION_NAME = 'bulk-email';
|
||||
@@ -11,23 +12,23 @@ const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
sendTxn(
|
||||
user,
|
||||
EMAIL_SLUG,
|
||||
[{name: 'BASE_URL', content: BASE_URL}] // Add variables from template
|
||||
[{ name: 'BASE_URL', content: BASE_URL }], // Add variables from template
|
||||
);
|
||||
|
||||
return await User.update({_id: user._id}, {$set: {migration: MIGRATION_NAME}}).exec();
|
||||
return User.update({ _id: user._id }, { $set: { migration: MIGRATION_NAME } }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
'auth.timestamps.loggedin': {$gt: moment().subtract(2, 'weeks').toDate()}, // customize or remove to target different populations
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: moment().subtract(2, 'weeks').toDate() }, // customize or remove to target different populations
|
||||
};
|
||||
|
||||
const fields = {
|
||||
@@ -41,7 +42,7 @@ module.exports = async function processUsers () {
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
@@ -58,4 +59,4 @@ module.exports = async function processUsers () {
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = 'full-stable';
|
||||
import each from 'lodash/each';
|
||||
import keys from 'lodash/keys';
|
||||
import content from '../../website/common/script/content/index';
|
||||
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const MIGRATION_NAME = 'full-stable';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
@@ -14,45 +15,45 @@ let count = 0;
|
||||
*/
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
const set = {};
|
||||
|
||||
set.migration = MIGRATION_NAME;
|
||||
|
||||
each(keys(content.pets), (pet) => {
|
||||
each(keys(content.pets), pet => {
|
||||
set[`items.pets.${pet}`] = 5;
|
||||
});
|
||||
each(keys(content.premiumPets), (pet) => {
|
||||
each(keys(content.premiumPets), pet => {
|
||||
set[`items.pets.${pet}`] = 5;
|
||||
});
|
||||
each(keys(content.questPets), (pet) => {
|
||||
each(keys(content.questPets), pet => {
|
||||
set[`items.pets.${pet}`] = 5;
|
||||
});
|
||||
each(keys(content.specialPets), (pet) => {
|
||||
each(keys(content.specialPets), pet => {
|
||||
set[`items.pets.${pet}`] = 5;
|
||||
});
|
||||
each(keys(content.mounts), (mount) => {
|
||||
each(keys(content.mounts), mount => {
|
||||
set[`items.mounts.${mount}`] = true;
|
||||
});
|
||||
each(keys(content.premiumMounts), (mount) => {
|
||||
each(keys(content.premiumMounts), mount => {
|
||||
set[`items.mounts.${mount}`] = true;
|
||||
});
|
||||
each(keys(content.questMounts), (mount) => {
|
||||
each(keys(content.questMounts), mount => {
|
||||
set[`items.mounts.${mount}`] = true;
|
||||
});
|
||||
each(keys(content.specialMounts), (mount) => {
|
||||
each(keys(content.specialMounts), mount => {
|
||||
set[`items.mounts.${mount}`] = true;
|
||||
});
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||
return User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.local.username': 'olson22',
|
||||
};
|
||||
|
||||
@@ -64,7 +65,7 @@ module.exports = async function processUsers () {
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
@@ -81,4 +82,4 @@ module.exports = async function processUsers () {
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = 'mystery_items_201909';
|
||||
const MYSTERY_ITEMS = ['armor_mystery_201909', 'head_mystery_201909'];
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
import { model as UserNotification } from '../../website/server/models/userNotification';
|
||||
|
||||
const MIGRATION_NAME = 'mystery_items_201911';
|
||||
const MYSTERY_ITEMS = ['weapon_mystery_201911', 'head_mystery_201911'];
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
const addToSet = {
|
||||
'purchased.plan.mysteryItems': {
|
||||
@@ -29,12 +30,12 @@ async function updateUser (user) {
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({_id: user._id}, {$set: set, $push: push, $addToSet: addToSet}).exec();
|
||||
return User.update({ _id: user._id }, { $set: set, $push: push, $addToSet: addToSet }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'purchased.plan.customerId': { $ne: null },
|
||||
$or: [
|
||||
{ 'purchased.plan.dateTerminated': { $gte: new Date() } },
|
||||
@@ -51,7 +52,7 @@ module.exports = async function processUsers () {
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
@@ -68,4 +69,4 @@ module.exports = async function processUsers () {
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20190314_pi_day';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const MIGRATION_NAME = '20190314_pi_day';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
count *= 1;
|
||||
|
||||
const inc = {
|
||||
'items.food.Pie_Skeleton': 1,
|
||||
@@ -29,19 +30,21 @@ async function updateUser (user) {
|
||||
set['items.gear.owned.head_special_piDay'] = false;
|
||||
set['items.gear.owned.shield_special_piDay'] = false;
|
||||
const push = [
|
||||
{type: 'marketGear', path: 'gear.flat.head_special_piDay', _id: uuid()},
|
||||
{type: 'marketGear', path: 'gear.flat.shield_special_piDay', _id: uuid()},
|
||||
{ type: 'marketGear', path: 'gear.flat.head_special_piDay', _id: uuid() },
|
||||
{ type: 'marketGear', path: 'gear.flat.shield_special_piDay', _id: uuid() },
|
||||
];
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({_id: user._id}, {$inc: inc, $set: set, $push: {pinnedItems: {$each: push}}}).exec();
|
||||
return User
|
||||
.update({ _id: user._id }, { $inc: inc, $set: set, $push: { pinnedItems: { $each: push } } })
|
||||
.exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
'auth.timestamps.loggedin': {$gt: new Date('2019-02-15')},
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2019-02-15') },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
@@ -53,7 +56,7 @@ module.exports = async function processUsers () {
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
@@ -70,4 +73,4 @@ module.exports = async function processUsers () {
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import monk from 'monk'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const migrationName = 'remove-social-users-extra-data.js';
|
||||
const authorName = 'paglias'; // in case script author needs to know when their ...
|
||||
const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is done
|
||||
@@ -7,13 +9,13 @@ const authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; // ... own data is do
|
||||
*/
|
||||
const connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||
|
||||
const monk = require('monk');
|
||||
|
||||
const dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||
|
||||
function processUsers (lastId) {
|
||||
// specify a query to limit the affected users (empty for all users):
|
||||
let query = {
|
||||
migration: {$ne: migrationName},
|
||||
const query = {
|
||||
migration: { $ne: migrationName },
|
||||
$or: [
|
||||
{ 'auth.facebook.id': { $exists: true } },
|
||||
{ 'auth.google.id': { $exists: true } },
|
||||
@@ -27,28 +29,28 @@ function processUsers (lastId) {
|
||||
}
|
||||
|
||||
dbUsers.find(query, {
|
||||
sort: {_id: 1},
|
||||
sort: { _id: 1 },
|
||||
limit: 250,
|
||||
})
|
||||
.then(updateUsers)
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
return exiting(1, `ERROR! ${ err}`);
|
||||
return exiting(1, `ERROR! ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
let progressCount = 1000;
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
function updateUsers (users) {
|
||||
if (!users || users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
displayData();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let userPromises = users.map(updateUser);
|
||||
let lastUser = users[users.length - 1];
|
||||
const userPromises = users.map(updateUser);
|
||||
const lastUser = users[users.length - 1];
|
||||
|
||||
return Promise.all(userPromises)
|
||||
.then(() => {
|
||||
@@ -57,7 +59,7 @@ function updateUsers (users) {
|
||||
}
|
||||
|
||||
function updateUser (user) {
|
||||
count++;
|
||||
count *= 1;
|
||||
|
||||
const isFacebook = user.auth.facebook && user.auth.facebook.id;
|
||||
const isGoogle = user.auth.google && user.auth.google.id;
|
||||
@@ -82,28 +84,29 @@ function updateUser (user) {
|
||||
_id: user._id,
|
||||
}, update);
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count } ${ user._id}`);
|
||||
if (user._id === authorUuid) console.warn(`${authorName } processed`);
|
||||
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`);
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
return exiting(0);
|
||||
}
|
||||
|
||||
function exiting (code, msg) {
|
||||
code = code || 0; // 0 = success
|
||||
// 0 = success
|
||||
code = code || 0; // eslint-disable-line no-param-reassign
|
||||
if (code && !msg) {
|
||||
msg = 'ERROR!';
|
||||
msg = 'ERROR!'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
if (msg) {
|
||||
if (code) {
|
||||
console.error(msg);
|
||||
} else {
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
module.exports = processUsers;
|
||||
export default processUsers;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20181203_take_this';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { model as User } from '../../website/server/models/user';
|
||||
|
||||
const MIGRATION_NAME = '20181203_take_this';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
count += 1;
|
||||
|
||||
const set = {};
|
||||
let push;
|
||||
@@ -19,36 +20,35 @@ async function updateUser (user) {
|
||||
push = false;
|
||||
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
|
||||
set['items.gear.owned.back_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.back_special_takeThis', _id: uuid() } };
|
||||
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
|
||||
set['items.gear.owned.body_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.body_special_takeThis', _id: uuid() } };
|
||||
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
|
||||
set['items.gear.owned.head_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.head_special_takeThis', _id: uuid() } };
|
||||
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
|
||||
set['items.gear.owned.armor_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.armor_special_takeThis', _id: uuid() } };
|
||||
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
|
||||
set['items.gear.owned.weapon_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', _id: uuid() } };
|
||||
} else {
|
||||
set['items.gear.owned.shield_special_takeThis'] = false;
|
||||
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid()}};
|
||||
push = { pinnedItems: { type: 'marketGear', path: 'gear.flat.shield_special_takeThis', _id: uuid() } };
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
if (push) {
|
||||
return await User.update({_id: user._id}, {$set: set, $push: push}).exec();
|
||||
} else {
|
||||
return await User.update({_id: user._id}, {$set: set}).exec();
|
||||
return User.update({ _id: user._id }, { $set: set, $push: push }).exec();
|
||||
}
|
||||
return User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: {$ne: MIGRATION_NAME},
|
||||
export default async function processUsers () {
|
||||
const query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
challenges: '00708425-d477-41a5-bf27-6270466e7976',
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ module.exports = async function processUsers () {
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.sort({ _id: 1 })
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
@@ -78,4 +78,4 @@ module.exports = async function processUsers () {
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-commonjs */
|
||||
/*
|
||||
let migrationName = 'UserFromProdToTest';
|
||||
let authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
|
||||
@@ -8,22 +9,24 @@ let authorUuid = ''; // ... own data is done
|
||||
* This migraition will copy user data from prod to test
|
||||
*/
|
||||
|
||||
let monk = require('monk');
|
||||
let testConnectionSting = ''; // FOR TEST DATABASE
|
||||
let usersTest = monk(testConnectionSting).get('users', { castIds: false });
|
||||
let groupsTest = monk(testConnectionSting).get('groups', { castIds: false });
|
||||
let challengesTest = monk(testConnectionSting).get('challenges', { castIds: false });
|
||||
let tasksTest = monk(testConnectionSting).get('tasks', { castIds: false });
|
||||
|
||||
let monk2 = require('monk');
|
||||
let liveConnectString = ''; // FOR TEST DATABASE
|
||||
let userLive = monk2(liveConnectString).get('users', { castIds: false });
|
||||
let groupsLive = monk2(liveConnectString).get('groups', { castIds: false });
|
||||
let challengesLive = monk2(liveConnectString).get('challenges', { castIds: false });
|
||||
let tasksLive = monk2(liveConnectString).get('tasks', { castIds: false });
|
||||
|
||||
import uniq from 'lodash/uniq';
|
||||
|
||||
const monk = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const testConnectionSting = ''; // FOR TEST DATABASE
|
||||
const usersTest = monk(testConnectionSting).get('users', { castIds: false });
|
||||
const groupsTest = monk(testConnectionSting).get('groups', { castIds: false });
|
||||
const challengesTest = monk(testConnectionSting).get('challenges', { castIds: false });
|
||||
const tasksTest = monk(testConnectionSting).get('tasks', { castIds: false });
|
||||
|
||||
const monk2 = require('monk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
const liveConnectString = ''; // FOR TEST DATABASE
|
||||
const userLive = monk2(liveConnectString).get('users', { castIds: false });
|
||||
const groupsLive = monk2(liveConnectString).get('groups', { castIds: false });
|
||||
const challengesLive = monk2(liveConnectString).get('challenges', { castIds: false });
|
||||
const tasksLive = monk2(liveConnectString).get('tasks', { castIds: false });
|
||||
|
||||
// Variabls for updating
|
||||
/*
|
||||
let userIds = [
|
||||
@@ -36,11 +39,11 @@ let challengeIds = [];
|
||||
let tasksIds = [];
|
||||
|
||||
async function processUsers () {
|
||||
let userPromises = [];
|
||||
const userPromises = [];
|
||||
// {_id: {$in: userIds}}
|
||||
|
||||
return userLive.find({guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0'})
|
||||
.each((user) => {
|
||||
return userLive.find({ guilds: 'b0764d64-8276-45a1-afa5-5ca9a5c64ca0' })
|
||||
.each(user => {
|
||||
if (user.guilds.length > 0) groupIds = groupIds.concat(user.guilds);
|
||||
if (user.party._id) groupIds.push(user.party._id);
|
||||
if (user.challenges.length > 0) challengeIds = challengeIds.concat(user.challenges);
|
||||
@@ -49,64 +52,56 @@ async function processUsers () {
|
||||
if (user.tasksOrder.dailys.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.dailys);
|
||||
if (user.tasksOrder.habits.length > 0) tasksIds = tasksIds.concat(user.tasksOrder.habits);
|
||||
|
||||
let userPromise = usersTest.update({_id: user._id}, user, {upsert: true});
|
||||
const userPromise = usersTest.update({ _id: user._id }, user, { upsert: true });
|
||||
userPromises.push(userPromise);
|
||||
}).then(() => {
|
||||
return Promise.all(userPromises);
|
||||
})
|
||||
}).then(() => Promise.all(userPromises))
|
||||
.then(() => {
|
||||
console.log('Done User');
|
||||
});
|
||||
}
|
||||
|
||||
function processGroups () {
|
||||
let promises = [];
|
||||
let groupsToQuery = uniq(groupIds);
|
||||
return groupsLive.find({_id: {$in: groupsToQuery}})
|
||||
.each((group) => {
|
||||
let promise = groupsTest.update({_id: group._id}, group, {upsert: true});
|
||||
const promises = [];
|
||||
const groupsToQuery = uniq(groupIds);
|
||||
return groupsLive.find({ _id: { $in: groupsToQuery } })
|
||||
.each(group => {
|
||||
const promise = groupsTest.update({ _id: group._id }, group, { upsert: true });
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Promise.all(promises);
|
||||
})
|
||||
}).then(() => Promise.all(promises))
|
||||
.then(() => {
|
||||
console.log('Done Group');
|
||||
});
|
||||
}
|
||||
|
||||
function processChallenges () {
|
||||
let promises = [];
|
||||
let challengesToQuery = uniq(challengeIds);
|
||||
return challengesLive.find({_id: {$in: challengesToQuery}})
|
||||
.each((challenge) => {
|
||||
let promise = challengesTest.update({_id: challenge._id}, challenge, {upsert: true});
|
||||
const promises = [];
|
||||
const challengesToQuery = uniq(challengeIds);
|
||||
return challengesLive.find({ _id: { $in: challengesToQuery } })
|
||||
.each(challenge => {
|
||||
const promise = challengesTest.update({ _id: challenge._id }, challenge, { upsert: true });
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Promise.all(promises);
|
||||
})
|
||||
}).then(() => Promise.all(promises))
|
||||
.then(() => {
|
||||
console.log('Done Challenge');
|
||||
});
|
||||
}
|
||||
|
||||
function processTasks () {
|
||||
let promises = [];
|
||||
let tasksToQuery = uniq(tasksIds);
|
||||
return tasksLive.find({_id: {$in: tasksToQuery}})
|
||||
.each((task) => {
|
||||
let promise = tasksTest.update({_id: task._id}, task, {upsert: true});
|
||||
const promises = [];
|
||||
const tasksToQuery = uniq(tasksIds);
|
||||
return tasksLive.find({ _id: { $in: tasksToQuery } })
|
||||
.each(task => {
|
||||
const promise = tasksTest.update({ _id: task._id }, task, { upsert: true });
|
||||
promises.push(promise);
|
||||
}).then(() => {
|
||||
return Promise.all(promises);
|
||||
})
|
||||
}).then(() => Promise.all(promises))
|
||||
.then(() => {
|
||||
console.log('Done Tasks');
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = async function prodToTest () {
|
||||
export default async function prodToTest () {
|
||||
await processUsers();
|
||||
await processGroups();
|
||||
await processChallenges();
|
||||
await processTasks();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const MongoClient = require('mongodb').MongoClient;
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const { MongoClient } = require('mongodb'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
const logger = require('./logger');
|
||||
|
||||
let dbConnection;
|
||||
@@ -17,7 +16,7 @@ function connectToDb (dbUri) {
|
||||
|
||||
logger.success(`Connected to ${dbUri}`);
|
||||
|
||||
resolve(database);
|
||||
return resolve(database);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const chalk = require('chalk');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const chalk = require('chalk'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
function loggerGenerator (type, color) {
|
||||
return function logger () {
|
||||
let args = Array.from(arguments).map(arg => chalk[color](arg));
|
||||
const args = Array
|
||||
.from(arguments) // eslint-disable-line prefer-rest-params
|
||||
.map(arg => chalk[color](arg));
|
||||
console[type].apply(null, args);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
let logger = require('./logger');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
const logger = require('./logger');
|
||||
|
||||
class Timer {
|
||||
constructor (options) {
|
||||
options = options || {};
|
||||
let warningThreshold = options.minutesWarningThreshold || 10;
|
||||
constructor (options = {}) {
|
||||
const warningThreshold = options.minutesWarningThreshold || 10;
|
||||
|
||||
this.count = 0;
|
||||
this._minutesWarningThreshold = warningThreshold * 60;
|
||||
|
||||
if (!options.disableAutoStart) this.start();
|
||||
}
|
||||
|
||||
start () {
|
||||
this._internalTimer = setInterval(() => {
|
||||
this.count++;
|
||||
this.count *= 1;
|
||||
|
||||
let shouldWarn = this._minutesWarningThreshold < this.count;
|
||||
let logStyle = shouldWarn ? 'error' : 'warn';
|
||||
let dangerMessage = shouldWarn ? 'DANGER: ' : '';
|
||||
const shouldWarn = this._minutesWarningThreshold < this.count;
|
||||
const logStyle = shouldWarn ? 'error' : 'warn';
|
||||
const dangerMessage = shouldWarn ? 'DANGER: ' : '';
|
||||
|
||||
if (this.count % 30 === 0) {
|
||||
logger[logStyle](`${dangerMessage}Process has been running for`, this.count / 60, 'minutes');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
stop () {
|
||||
if (!this._internalTimer) {
|
||||
throw new Error('Timer has not started');
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable import/no-commonjs */
|
||||
function unique (array) {
|
||||
return Array.from(new Set(array));
|
||||
}
|
||||
|
||||
19414
package-lock.json
generated
19414
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
135
package.json
135
package.json
@@ -1,76 +1,55 @@
|
||||
{
|
||||
"name": "habitica",
|
||||
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
|
||||
"version": "4.116.0",
|
||||
"version": "4.123.0",
|
||||
"main": "./website/server/index.js",
|
||||
"dependencies": {
|
||||
"@google-cloud/trace-agent": "^4.0.0",
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/preset-env": "^7.7.1",
|
||||
"@babel/register": "^7.7.0",
|
||||
"@google-cloud/trace-agent": "^4.2.3",
|
||||
"@slack/client": "^3.8.1",
|
||||
"accepts": "^1.3.5",
|
||||
"amazon-payments": "^0.2.7",
|
||||
"amplitude": "^3.5.0",
|
||||
"amplitude-js": "^5.2.2",
|
||||
"apidoc": "^0.17.5",
|
||||
"apn": "^2.2.0",
|
||||
"autoprefixer": "^9.4.0",
|
||||
"aws-sdk": "^2.432.0",
|
||||
"axios": "^0.19.0",
|
||||
"axios-progress-bar": "^1.2.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
||||
"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",
|
||||
"aws-sdk": "^2.573.0",
|
||||
"bcrypt": "^3.0.6",
|
||||
"body-parser": "^1.18.3",
|
||||
"bootstrap": "^4.1.1",
|
||||
"bootstrap-vue": "^2.0.2",
|
||||
"compression": "^1.7.4",
|
||||
"cookie-session": "^1.3.3",
|
||||
"coupon-code": "^0.4.5",
|
||||
"cross-env": "^6.0.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"csv-stringify": "^5.1.0",
|
||||
"cwait": "^1.1.1",
|
||||
"domain-middleware": "~0.1.0",
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-config-habitrpg": "^6.2.0",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"express": "^4.16.3",
|
||||
"express-basic-auth": "^1.1.5",
|
||||
"express-validator": "^5.2.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"glob": "^7.1.2",
|
||||
"glob": "^7.1.6",
|
||||
"got": "^9.0.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-babel": "^7.0.1",
|
||||
"gulp-imagemin": "^6.0.0",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-imagemin": "^6.2.0",
|
||||
"gulp-nodemon": "^2.4.1",
|
||||
"gulp.spritesmith": "^6.9.0",
|
||||
"habitica-markdown": "^1.3.0",
|
||||
"hellojs": "^1.18.1",
|
||||
"helmet": "^3.21.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"image-size": "^0.8.0",
|
||||
"helmet": "^3.21.2",
|
||||
"image-size": "^0.8.3",
|
||||
"in-app-purchase": "^1.11.3",
|
||||
"intro.js": "^2.9.3",
|
||||
"jquery": ">=3.0.0",
|
||||
"js2xmlparser": "^4.0.0",
|
||||
"lodash": "^4.17.10",
|
||||
"lodash": "^4.17.15",
|
||||
"merge-stream": "^2.0.0",
|
||||
"method-override": "^3.0.0",
|
||||
"moment": "^2.22.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-recur": "^1.0.7",
|
||||
"mongoose": "^5.6.9",
|
||||
"mongoose": "^5.7.11",
|
||||
"morgan": "^1.7.0",
|
||||
"nconf": "^0.10.0",
|
||||
"node-gcm": "^1.0.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"ora": "^3.2.0",
|
||||
"pageres": "^5.1.0",
|
||||
"passport": "^0.4.0",
|
||||
"passport-facebook": "^3.0.0",
|
||||
@@ -78,40 +57,17 @@
|
||||
"passport-google-oauth20": "1.0.0",
|
||||
"paypal-ipn": "3.0.0",
|
||||
"paypal-rest-sdk": "^1.8.1",
|
||||
"popper.js": "^1.14.3",
|
||||
"postcss-easy-import": "^3.0.0",
|
||||
"ps-tree": "^1.0.0",
|
||||
"pug": "^2.0.3",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"rimraf": "^2.4.3",
|
||||
"sass-loader": "^7.0.3",
|
||||
"shelljs": "^0.8.2",
|
||||
"rimraf": "^3.0.0",
|
||||
"short-uuid": "^3.0.0",
|
||||
"smartbanner.js": "^1.11.0",
|
||||
"stripe": "^7.9.0",
|
||||
"stripe": "^7.13.0",
|
||||
"superagent": "^5.0.2",
|
||||
"svg-inline-loader": "^0.8.0",
|
||||
"svg-url-loader": "^3.0.0",
|
||||
"svgo": "^1.2.0",
|
||||
"svgo-loader": "^2.1.0",
|
||||
"universal-analytics": "^0.4.17",
|
||||
"update": "^0.7.4",
|
||||
"upgrade": "^1.1.0",
|
||||
"url-loader": "^1.0.0",
|
||||
"useragent": "^2.1.9",
|
||||
"uuid": "^3.0.1",
|
||||
"uuid": "^3.3.3",
|
||||
"validator": "^11.0.0",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vue": "^2.6.10",
|
||||
"vue-loader": "^14.2.2",
|
||||
"vue-mugen-scroll": "^0.2.1",
|
||||
"vue-router": "^3.0.0",
|
||||
"vue-style-loader": "^4.1.0",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vuedraggable": "^2.20.0",
|
||||
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
|
||||
"webpack": "^3.12.0",
|
||||
"webpack-merge": "^4.1.3",
|
||||
"winston": "^2.4.3",
|
||||
"winston-loggly-bulk": "^2.0.2",
|
||||
"xml2js": "^0.4.4"
|
||||
@@ -122,7 +78,8 @@
|
||||
"npm": "^6"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"lint": "eslint --ext .js --fix . && cd website/client && npm run lint",
|
||||
"lint-no-fix": "eslint --ext .js . && cd website/client && npm run lint-no-fix",
|
||||
"test": "npm run lint && gulp test && gulp apidoc",
|
||||
"test:build": "gulp test:prepare:build",
|
||||
"test:api-v3": "gulp test:api-v3",
|
||||
@@ -137,60 +94,26 @@
|
||||
"test:nodemon": "gulp test:nodemon",
|
||||
"coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html",
|
||||
"sprites": "gulp sprites:compile",
|
||||
"client:dev": "node webpack/dev-server.js",
|
||||
"client:build": "gulp build:client",
|
||||
"client:unit": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js --single-run",
|
||||
"client:unit:watch": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js",
|
||||
"client:e2e": "node test/client/e2e/runner.js",
|
||||
"client:test": "npm run client:unit && npm run client:e2e",
|
||||
"client:dev": "cd website/client && npm run serve",
|
||||
"client:build": "cd website/client && npm run build",
|
||||
"client:unit": "cd website/client && npm run test:unit",
|
||||
"start": "gulp nodemon",
|
||||
"postinstall": "gulp build",
|
||||
"postinstall": "gulp build && cd website/client && npm install",
|
||||
"apidoc": "gulp apidoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"babel-plugin-istanbul": "^4.1.6",
|
||||
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
|
||||
"axios": "^0.19.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chalk": "^2.4.1",
|
||||
"chromedriver": "^77.0.0",
|
||||
"connect-history-api-fallback": "^1.1.0",
|
||||
"cross-spawn": "^7.0.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-habitrpg": "^4.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-loader": "^2.0.0",
|
||||
"eslint-plugin-html": "^4.0.3",
|
||||
"eslint-plugin-mocha": "^5.0.0",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"expect.js": "^0.3.1",
|
||||
"http-proxy-middleware": "^0.20.0",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
"karma": "^4.0.1",
|
||||
"karma-babel-preprocessor": "^7.0.0",
|
||||
"karma-chai-plugins": "^0.9.0",
|
||||
"karma-chrome-launcher": "^3.0.0",
|
||||
"karma-coverage": "^1.1.2",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-sinon-chai": "^2.0.0",
|
||||
"karma-sinon-stub-promise": "^1.0.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "0.0.32",
|
||||
"karma-webpack": "^3.0.0",
|
||||
"mocha": "^5.1.1",
|
||||
"monk": "^6.0.6",
|
||||
"nightwatch": "^1.0.16",
|
||||
"puppeteer": "^1.14.0",
|
||||
"monk": "^7.1.1",
|
||||
"require-again": "^2.0.0",
|
||||
"selenium-server": "^3.12.0",
|
||||
"sinon": "^7.2.4",
|
||||
"sinon-chai": "^3.0.0",
|
||||
"sinon-stub-promise": "^4.0.0",
|
||||
"webpack-bundle-analyzer": "^2.12.0",
|
||||
"webpack-dev-middleware": "^2.0.5",
|
||||
"webpack-hot-middleware": "^2.22.2"
|
||||
"sinon-stub-promise": "^4.0.0"
|
||||
},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable no-console */
|
||||
import axios from 'axios';
|
||||
import { model as User } from '../website/server/models/user';
|
||||
/* eslint-disable no-console, import/no-commonjs */
|
||||
import axios from 'axios'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import nconf from 'nconf';
|
||||
import { model as User } from '../website/server/models/user';
|
||||
|
||||
const AMPLITUDE_KEY = nconf.get('AMPLITUDE_KEY');
|
||||
const AMPLITUDE_SECRET = nconf.get('AMPLITUDE_SECRET');
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
async function _deleteAmplitudeData (userId, email) {
|
||||
async function deleteAmplitudeData (userId, email) {
|
||||
const response = await axios.post(
|
||||
'https://amplitude.com/api/2/deletions/users',
|
||||
{
|
||||
@@ -19,22 +19,24 @@ async function _deleteAmplitudeData (userId, email) {
|
||||
username: AMPLITUDE_KEY,
|
||||
password: AMPLITUDE_SECRET,
|
||||
},
|
||||
}
|
||||
).catch((err) => {
|
||||
},
|
||||
).catch(err => {
|
||||
console.log(err.response.data);
|
||||
});
|
||||
|
||||
if (response) console.log(`${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
async function _deleteHabiticaData (user, email) {
|
||||
async function deleteHabiticaData (user, email) {
|
||||
await User.update(
|
||||
{_id: user._id},
|
||||
{$set: {
|
||||
'auth.local.email': email,
|
||||
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
||||
'auth.local.passwordHashMethod': 'bcrypt',
|
||||
}}
|
||||
{ _id: user._id },
|
||||
{
|
||||
$set: {
|
||||
'auth.local.email': email,
|
||||
'auth.local.hashed_password': '$2a$10$QDnNh1j1yMPnTXDEOV38xOePEWFd4X8DSYwAM8XTmqmacG5X0DKjW',
|
||||
'auth.local.passwordHashMethod': 'bcrypt',
|
||||
},
|
||||
},
|
||||
);
|
||||
const response = await axios.delete(
|
||||
`${BASE_URL}/api/v3/user`,
|
||||
@@ -46,8 +48,8 @@ async function _deleteHabiticaData (user, email) {
|
||||
'x-api-user': user._id,
|
||||
'x-api-key': user.apiToken,
|
||||
},
|
||||
}
|
||||
).catch((err) => {
|
||||
},
|
||||
).catch(err => {
|
||||
console.log(err.response.data);
|
||||
});
|
||||
|
||||
@@ -57,14 +59,15 @@ async function _deleteHabiticaData (user, email) {
|
||||
}
|
||||
}
|
||||
|
||||
async function _processEmailAddress (email) {
|
||||
async function processEmailAddress (email) {
|
||||
const emailRegex = new RegExp(`^${email}$`, 'i');
|
||||
const users = await User.find({
|
||||
$or: [
|
||||
{'auth.local.email': emailRegex},
|
||||
{'auth.facebook.emails.value': emailRegex},
|
||||
{'auth.google.emails.value': emailRegex},
|
||||
]},
|
||||
{ 'auth.local.email': emailRegex },
|
||||
{ 'auth.facebook.emails.value': emailRegex },
|
||||
{ 'auth.google.emails.value': emailRegex },
|
||||
],
|
||||
},
|
||||
{
|
||||
_id: 1,
|
||||
apiToken: 1,
|
||||
@@ -74,15 +77,15 @@ async function _processEmailAddress (email) {
|
||||
if (users.length < 1) {
|
||||
console.log(`No users found with email address ${email}`);
|
||||
} else {
|
||||
for (const user of users) {
|
||||
await _deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
||||
await _deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
Promise.all(users.map(user => (async () => {
|
||||
await deleteAmplitudeData(user._id, email); // eslint-disable-line no-await-in-loop
|
||||
await deleteHabiticaData(user, email); // eslint-disable-line no-await-in-loop
|
||||
})()));
|
||||
}
|
||||
}
|
||||
|
||||
function deleteUserData (emails) {
|
||||
const emailPromises = emails.map(_processEmailAddress);
|
||||
const emailPromises = emails.map(processEmailAddress);
|
||||
return Promise.all(emailPromises);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
require('babel-register');
|
||||
/* eslint-disable import/no-commonjs */
|
||||
require('@babel/register'); // eslint-disable-line import/no-extraneous-dependencies
|
||||
|
||||
// This file is used for creating paypal billing plans. PayPal doesn't have a web interface for setting up recurring
|
||||
// payment plan definitions, instead you have to create it via their REST SDK and keep it updated the same way. So this
|
||||
// file will be used once for initing your billing plan (then you get the resultant plan.id to store in config.json),
|
||||
// This file is used for creating paypal billing plans.
|
||||
// PayPal doesn't have a web interface for setting up recurring
|
||||
// payment plan definitions, instead you have to create it
|
||||
// via their REST SDK and keep it updated the same way. So this
|
||||
// file will be used once for initing your billing plan
|
||||
// (then you get the resultant plan.id to store in config.json),
|
||||
// and once for any time you need to edit the plan thereafter
|
||||
|
||||
/* eslint-disable no-console, camelcase, no-case-declarations */
|
||||
@@ -12,11 +16,12 @@ const nconf = require('nconf');
|
||||
const _ = require('lodash');
|
||||
const paypal = require('paypal-rest-sdk');
|
||||
const blocks = require('../website/common').content.subscriptionBlocks;
|
||||
|
||||
const live = nconf.get('PAYPAL_MODE') === 'live';
|
||||
|
||||
nconf.argv().env().file('user', path.join(path.resolve(__dirname, '../config.json')));
|
||||
|
||||
let OP = 'create'; // list get update create create-webprofile
|
||||
const OP = 'create'; // list get update create create-webprofile
|
||||
|
||||
paypal.configure({
|
||||
mode: nconf.get('PAYPAL_MODE'), // sandbox or live
|
||||
@@ -25,8 +30,8 @@ paypal.configure({
|
||||
});
|
||||
|
||||
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
|
||||
let billingPlanTitle = 'Habitica Subscription';
|
||||
let billingPlanAttributes = {
|
||||
const billingPlanTitle = 'Habitica Subscription';
|
||||
const billingPlanAttributes = {
|
||||
description: billingPlanTitle,
|
||||
type: 'INFINITE',
|
||||
merchant_preferences: {
|
||||
@@ -41,7 +46,7 @@ let billingPlanAttributes = {
|
||||
}],
|
||||
};
|
||||
|
||||
_.each(blocks, (block) => {
|
||||
_.each(blocks, block => {
|
||||
block.definition = _.cloneDeep(billingPlanAttributes);
|
||||
_.merge(block.definition.payment_definitions[0], {
|
||||
name: `${billingPlanTitle} ($${block.price} every ${block.months} months, recurring)`,
|
||||
@@ -57,17 +62,17 @@ _.each(blocks, (block) => {
|
||||
|
||||
switch (OP) {
|
||||
case 'list':
|
||||
paypal.billingPlan.list({status: 'ACTIVE'}, (err, plans) => {
|
||||
console.log({err, plans});
|
||||
paypal.billingPlan.list({ status: 'ACTIVE' }, (err, plans) => {
|
||||
console.log({ err, plans });
|
||||
});
|
||||
break;
|
||||
case 'get':
|
||||
paypal.billingPlan.get(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), (err, plan) => {
|
||||
console.log({err, plan});
|
||||
console.log({ err, plan });
|
||||
});
|
||||
break;
|
||||
case 'update':
|
||||
let updatePayload = {
|
||||
const updatePayload = {
|
||||
op: 'replace',
|
||||
path: '/merchant_preferences',
|
||||
value: {
|
||||
@@ -75,7 +80,7 @@ switch (OP) {
|
||||
},
|
||||
};
|
||||
paypal.billingPlan.update(nconf.get('PAYPAL_BILLING_PLANS_basic_12mo'), updatePayload, (err, res) => {
|
||||
console.log({err, plan: res});
|
||||
console.log({ err, plan: res });
|
||||
});
|
||||
break;
|
||||
case 'create':
|
||||
@@ -83,10 +88,10 @@ switch (OP) {
|
||||
if (err) return console.log(err);
|
||||
|
||||
if (plan.state === 'ACTIVE') {
|
||||
return console.log({err, plan});
|
||||
return console.log({ err, plan });
|
||||
}
|
||||
|
||||
let billingPlanUpdateAttributes = [{
|
||||
const billingPlanUpdateAttributes = [{
|
||||
op: 'replace',
|
||||
path: '/',
|
||||
value: {
|
||||
@@ -96,12 +101,14 @@ switch (OP) {
|
||||
|
||||
// Activate the plan by changing status to Active
|
||||
paypal.billingPlan.update(plan.id, billingPlanUpdateAttributes, (err2, response) => {
|
||||
console.log({err: err2, response, id: plan.id});
|
||||
console.log({ err: err2, response, id: plan.id });
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
break;
|
||||
case 'create-webprofile':
|
||||
let webexpinfo = {
|
||||
const webexpinfo = {
|
||||
name: 'HabiticaProfile',
|
||||
input_fields: {
|
||||
no_shipping: 1,
|
||||
@@ -112,4 +119,6 @@ switch (OP) {
|
||||
console.log(error, result);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid op');
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"habitrpg/mocha",
|
||||
"habitrpg/esnext"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
"globals": {
|
||||
"_": true,
|
||||
}
|
||||
}
|
||||
12
test/.eslintrc.js
Normal file
12
test/.eslintrc.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'habitrpg/lib/mocha',
|
||||
],
|
||||
globals: {
|
||||
_: true,
|
||||
chai: true,
|
||||
expect: true,
|
||||
sinon: true,
|
||||
sandbox: true
|
||||
},
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable camelcase */
|
||||
import analyticsService from '../../../../website/server/libs/analyticsService';
|
||||
import Amplitude from 'amplitude';
|
||||
import { Visitor } from 'universal-analytics';
|
||||
import * as analyticsService from '../../../../website/server/libs/analyticsService';
|
||||
|
||||
describe('analyticsService', () => {
|
||||
beforeEach(() => {
|
||||
@@ -16,7 +16,8 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
describe('#track', () => {
|
||||
let eventType, data;
|
||||
let eventType; let
|
||||
data;
|
||||
|
||||
beforeEach(() => {
|
||||
Visitor.prototype.event.yields();
|
||||
@@ -35,12 +36,10 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
context('Amplitude', () => {
|
||||
it('calls out to amplitude', () => {
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
it('calls out to amplitude', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('uses a dummy user id if none is provided', () => {
|
||||
delete data.uuid;
|
||||
@@ -55,7 +54,7 @@ describe('analyticsService', () => {
|
||||
|
||||
context('platform', () => {
|
||||
it('logs web platform', () => {
|
||||
data.headers = {'x-client': 'habitica-web'};
|
||||
data.headers = { 'x-client': 'habitica-web' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
@@ -66,7 +65,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs iOS platform', () => {
|
||||
data.headers = {'x-client': 'habitica-ios'};
|
||||
data.headers = { 'x-client': 'habitica-ios' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
@@ -77,7 +76,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs Android platform', () => {
|
||||
data.headers = {'x-client': 'habitica-android'};
|
||||
data.headers = { 'x-client': 'habitica-android' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
@@ -88,7 +87,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs 3rd Party platform', () => {
|
||||
data.headers = {'x-client': 'some-third-party'};
|
||||
data.headers = { 'x-client': 'some-third-party' };
|
||||
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
@@ -169,18 +168,16 @@ describe('analyticsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sends details about event', () => {
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
category: 'behavior',
|
||||
resting: true,
|
||||
cronCount: 5,
|
||||
},
|
||||
});
|
||||
it('sends details about event', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
category: 'behavior',
|
||||
resting: true,
|
||||
cronCount: 5,
|
||||
},
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('sends english item name for gear if itemKey is provided', () => {
|
||||
data.itemKey = 'headAccessory_special_foxEars';
|
||||
@@ -267,16 +264,18 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('sends user data if provided', () => {
|
||||
let stats = { class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30 };
|
||||
let user = {
|
||||
const stats = {
|
||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
||||
};
|
||||
const user = {
|
||||
stats,
|
||||
contributor: { level: 1 },
|
||||
purchased: { plan: { planId: 'foo-plan' } },
|
||||
flags: {tour: {intro: -2}},
|
||||
habits: [{_id: 'habit'}],
|
||||
dailys: [{_id: 'daily'}],
|
||||
todos: [{_id: 'todo'}],
|
||||
rewards: [{_id: 'reward'}],
|
||||
flags: { tour: { intro: -2 } },
|
||||
habits: [{ _id: 'habit' }],
|
||||
dailys: [{ _id: 'daily' }],
|
||||
todos: [{ _id: 'todo' }],
|
||||
rewards: [{ _id: 'reward' }],
|
||||
balance: 12,
|
||||
loginIncentives: 1,
|
||||
};
|
||||
@@ -312,27 +311,24 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
context('GA', () => {
|
||||
it('calls out to GA', () => {
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
it('calls out to GA', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('sends details about event', () => {
|
||||
return analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledWith({
|
||||
ea: 'Cron',
|
||||
ec: 'behavior',
|
||||
});
|
||||
it('sends details about event', () => analyticsService.track(eventType, data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledWith({
|
||||
ea: 'Cron',
|
||||
ec: 'behavior',
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trackPurchase', () => {
|
||||
let data, itemSpy;
|
||||
let data; let
|
||||
itemSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
Visitor.prototype.event.yields();
|
||||
@@ -361,12 +357,10 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
context('Amplitude', () => {
|
||||
it('calls out to amplitude', () => {
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
it('calls out to amplitude', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('uses a dummy user id if none is provided', () => {
|
||||
delete data.uuid;
|
||||
@@ -381,7 +375,7 @@ describe('analyticsService', () => {
|
||||
|
||||
context('platform', () => {
|
||||
it('logs web platform', () => {
|
||||
data.headers = {'x-client': 'habitica-web'};
|
||||
data.headers = { 'x-client': 'habitica-web' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
@@ -392,7 +386,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs iOS platform', () => {
|
||||
data.headers = {'x-client': 'habitica-ios'};
|
||||
data.headers = { 'x-client': 'habitica-ios' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
@@ -403,7 +397,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs Android platform', () => {
|
||||
data.headers = {'x-client': 'habitica-android'};
|
||||
data.headers = { 'x-client': 'habitica-android' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
@@ -414,7 +408,7 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
it('logs 3rd Party platform', () => {
|
||||
data.headers = {'x-client': 'some-third-party'};
|
||||
data.headers = { 'x-client': 'some-third-party' };
|
||||
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
@@ -495,33 +489,33 @@ describe('analyticsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('sends details about purchase', () => {
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
gift: false,
|
||||
itemPurchased: 'Gems',
|
||||
paymentMethod: 'PayPal',
|
||||
purchaseType: 'checkout',
|
||||
quantity: 1,
|
||||
sku: 'paypal-checkout',
|
||||
},
|
||||
});
|
||||
it('sends details about purchase', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Amplitude.prototype.track).to.be.calledWithMatch({
|
||||
event_properties: {
|
||||
gift: false,
|
||||
itemPurchased: 'Gems',
|
||||
paymentMethod: 'PayPal',
|
||||
purchaseType: 'checkout',
|
||||
quantity: 1,
|
||||
sku: 'paypal-checkout',
|
||||
},
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('sends user data if provided', () => {
|
||||
let stats = { class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30 };
|
||||
let user = {
|
||||
const stats = {
|
||||
class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30,
|
||||
};
|
||||
const user = {
|
||||
stats,
|
||||
contributor: { level: 1 },
|
||||
purchased: { plan: { planId: 'foo-plan' } },
|
||||
flags: {tour: {intro: -2}},
|
||||
habits: [{_id: 'habit'}],
|
||||
dailys: [{_id: 'daily'}],
|
||||
todos: [{_id: 'todo'}],
|
||||
rewards: [{_id: 'reward'}],
|
||||
flags: { tour: { intro: -2 } },
|
||||
habits: [{ _id: 'habit' }],
|
||||
dailys: [{ _id: 'daily' }],
|
||||
todos: [{ _id: 'todo' }],
|
||||
rewards: [{ _id: 'reward' }],
|
||||
};
|
||||
|
||||
data.user = user;
|
||||
@@ -552,27 +546,23 @@ describe('analyticsService', () => {
|
||||
});
|
||||
|
||||
context('GA', () => {
|
||||
it('calls out to GA', () => {
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledOnce;
|
||||
expect(Visitor.prototype.transaction).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
it('calls out to GA', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledOnce;
|
||||
expect(Visitor.prototype.transaction).to.be.calledOnce;
|
||||
}));
|
||||
|
||||
it('sends details about purchase', () => {
|
||||
return analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledWith({
|
||||
ea: 'checkout',
|
||||
ec: 'commerce',
|
||||
el: 'PayPal',
|
||||
ev: 8,
|
||||
});
|
||||
expect(Visitor.prototype.transaction).to.be.calledWith('user-id', 8);
|
||||
expect(itemSpy).to.be.calledWith(8, 1, 'paypal-checkout', 'Gems', 'checkout');
|
||||
it('sends details about purchase', () => analyticsService.trackPurchase(data)
|
||||
.then(() => {
|
||||
expect(Visitor.prototype.event).to.be.calledWith({
|
||||
ea: 'checkout',
|
||||
ec: 'commerce',
|
||||
el: 'PayPal',
|
||||
ev: 8,
|
||||
});
|
||||
});
|
||||
expect(Visitor.prototype.transaction).to.be.calledWith('user-id', 8);
|
||||
expect(itemSpy).to.be.calledWith(8, 1, 'paypal-checkout', 'Gems', 'checkout');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('API Messages', () => {
|
||||
});
|
||||
|
||||
it('clones the passed variables', () => {
|
||||
let vars = {a: 1};
|
||||
const vars = { a: 1 };
|
||||
sandbox.stub(_, 'clone').returns({});
|
||||
apiError('guildsOnlyPaginate', vars);
|
||||
expect(_.clone).to.have.been.calledOnce;
|
||||
@@ -19,8 +19,8 @@ describe('API Messages', () => {
|
||||
});
|
||||
|
||||
it('pass the message through _.template', () => {
|
||||
let vars = {a: 1};
|
||||
let stub = sinon.stub().returns('string');
|
||||
const vars = { a: 1 };
|
||||
const stub = sinon.stub().returns('string');
|
||||
sandbox.stub(_, 'template').returns(stub);
|
||||
apiError('guildsOnlyPaginate', vars);
|
||||
expect(_.template).to.have.been.calledOnce;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import baseModel from '../../../../website/server/libs/baseModel';
|
||||
import mongoose from 'mongoose';
|
||||
import baseModel from '../../../../website/server/libs/baseModel';
|
||||
|
||||
describe('Base model plugin', () => {
|
||||
let schema;
|
||||
@@ -25,7 +25,7 @@ describe('Base model plugin', () => {
|
||||
});
|
||||
|
||||
it('can add timestamps fields', () => {
|
||||
schema.plugin(baseModel, {timestamps: true});
|
||||
schema.plugin(baseModel, { timestamps: true });
|
||||
|
||||
expect(schema.add).to.be.calledTwice;
|
||||
});
|
||||
@@ -36,7 +36,7 @@ describe('Base model plugin', () => {
|
||||
});
|
||||
|
||||
expect(schema.statics.sanitize).to.exist;
|
||||
let sanitized = schema.statics.sanitize({ok: true, noUpdateForMe: true});
|
||||
const sanitized = schema.statics.sanitize({ ok: true, noUpdateForMe: true });
|
||||
|
||||
expect(sanitized).to.have.property('ok');
|
||||
expect(sanitized).not.to.have.property('noUpdateForMe');
|
||||
@@ -49,7 +49,7 @@ describe('Base model plugin', () => {
|
||||
});
|
||||
|
||||
expect(schema.statics.sanitize).to.exist;
|
||||
let sanitized = schema.statics.sanitize({ok: true, noUpdateForMe: true, usuallySettable: true}, ['usuallySettable']);
|
||||
const sanitized = schema.statics.sanitize({ ok: true, noUpdateForMe: true, usuallySettable: true }, ['usuallySettable']);
|
||||
|
||||
expect(sanitized).to.have.property('ok');
|
||||
expect(sanitized).not.to.have.property('noUpdateForMe');
|
||||
@@ -63,31 +63,31 @@ describe('Base model plugin', () => {
|
||||
});
|
||||
|
||||
expect(schema.options.toJSON.transform).to.exist;
|
||||
let objToTransform = {ok: true, amPrivate: true};
|
||||
let privatized = schema.options.toJSON.transform({}, objToTransform);
|
||||
const objToTransform = { ok: true, amPrivate: true };
|
||||
const privatized = schema.options.toJSON.transform({}, objToTransform);
|
||||
|
||||
expect(privatized).to.have.property('ok');
|
||||
expect(privatized).not.to.have.property('amPrivate');
|
||||
});
|
||||
|
||||
it('accepts a further transform function for toJSON', () => {
|
||||
let options = {
|
||||
const options = {
|
||||
private: ['amPrivate'],
|
||||
toJSONTransform: sandbox.stub().returns(true),
|
||||
};
|
||||
|
||||
schema.plugin(baseModel, options);
|
||||
|
||||
let objToTransform = {ok: true, amPrivate: true};
|
||||
let doc = {doc: true};
|
||||
let privatized = schema.options.toJSON.transform(doc, objToTransform);
|
||||
const objToTransform = { ok: true, amPrivate: true };
|
||||
const doc = { doc: true };
|
||||
const privatized = schema.options.toJSON.transform(doc, objToTransform);
|
||||
|
||||
expect(privatized).to.equals(true);
|
||||
expect(options.toJSONTransform).to.be.calledWith(objToTransform, doc);
|
||||
});
|
||||
|
||||
it('accepts a transform function for sanitize', () => {
|
||||
let options = {
|
||||
const options = {
|
||||
private: ['amPrivate'],
|
||||
sanitizeTransform: sandbox.stub().returns(true),
|
||||
};
|
||||
@@ -95,8 +95,8 @@ describe('Base model plugin', () => {
|
||||
schema.plugin(baseModel, options);
|
||||
|
||||
expect(schema.options.toJSON.transform).to.exist;
|
||||
let objToSanitize = {ok: true, noUpdateForMe: true};
|
||||
let sanitized = schema.statics.sanitize(objToSanitize);
|
||||
const objToSanitize = { ok: true, noUpdateForMe: true };
|
||||
const sanitized = schema.statics.sanitize(objToSanitize);
|
||||
|
||||
expect(sanitized).to.equals(true);
|
||||
expect(options.sanitizeTransform).to.be.calledWith(objToSanitize);
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
describe('Collection Manipulators', () => {
|
||||
describe('removeFromArray', () => {
|
||||
it('removes element from array', () => {
|
||||
let array = ['a', 'b', 'c', 'd'];
|
||||
const array = ['a', 'b', 'c', 'd'];
|
||||
|
||||
removeFromArray(array, 'c');
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('Collection Manipulators', () => {
|
||||
});
|
||||
|
||||
it('removes object from array', () => {
|
||||
let array = [
|
||||
const array = [
|
||||
{ id: 'a', foo: 'bar' },
|
||||
{ id: 'b', foo: 'bar' },
|
||||
{ id: 'c', foo: 'bar' },
|
||||
@@ -28,7 +28,7 @@ describe('Collection Manipulators', () => {
|
||||
});
|
||||
|
||||
it('does not change array if value is not found', () => {
|
||||
let array = ['a', 'b', 'c', 'd'];
|
||||
const array = ['a', 'b', 'c', 'd'];
|
||||
|
||||
removeFromArray(array, 'z');
|
||||
|
||||
@@ -40,15 +40,15 @@ describe('Collection Manipulators', () => {
|
||||
});
|
||||
|
||||
it('returns the removed element', () => {
|
||||
let array = ['a', 'b', 'c'];
|
||||
const array = ['a', 'b', 'c'];
|
||||
|
||||
let result = removeFromArray(array, 'b');
|
||||
const result = removeFromArray(array, 'b');
|
||||
|
||||
expect(result).to.eql('b');
|
||||
});
|
||||
|
||||
it('returns the removed object element', () => {
|
||||
let array = [
|
||||
const array = [
|
||||
{ id: 'a', foo: 'bar' },
|
||||
{ id: 'b', foo: 'bar' },
|
||||
{ id: 'c', foo: 'bar' },
|
||||
@@ -56,31 +56,31 @@ describe('Collection Manipulators', () => {
|
||||
{ id: 'e', foo: 'bar' },
|
||||
];
|
||||
|
||||
let result = removeFromArray(array, { id: 'c' });
|
||||
const result = removeFromArray(array, { id: 'c' });
|
||||
|
||||
expect(result).to.eql({ id: 'c', foo: 'bar' });
|
||||
});
|
||||
|
||||
it('returns false if item is not found', () => {
|
||||
let array = ['a', 'b', 'c'];
|
||||
const array = ['a', 'b', 'c'];
|
||||
|
||||
let result = removeFromArray(array, 'z');
|
||||
const result = removeFromArray(array, 'z');
|
||||
|
||||
expect(result).to.eql(false);
|
||||
});
|
||||
|
||||
it('persists removal of element when mongoose document is saved', async () => {
|
||||
let schema = new mongoose.Schema({
|
||||
const schema = new mongoose.Schema({
|
||||
array: Array,
|
||||
});
|
||||
let Model = mongoose.model('ModelToTestRemoveFromArray', schema);
|
||||
let model = await new Model({
|
||||
const Model = mongoose.model('ModelToTestRemoveFromArray', schema);
|
||||
const model = await new Model({
|
||||
array: ['a', 'b', 'c'],
|
||||
}).save(); // Initial creation
|
||||
|
||||
removeFromArray(model.array, 'b');
|
||||
|
||||
let savedModel = await model.save();
|
||||
const savedModel = await model.save();
|
||||
|
||||
expect(savedModel.array).to.not.include('b');
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,20 +31,20 @@ function getUser () {
|
||||
}
|
||||
|
||||
describe('emails', () => {
|
||||
let pathToEmailLib = '../../../../website/server/libs/email';
|
||||
const pathToEmailLib = '../../../../website/server/libs/email';
|
||||
|
||||
describe('getUserInfo', () => {
|
||||
it('returns an empty object if no field request', () => {
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let getUserInfo = attachEmail.getUserInfo;
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const { getUserInfo } = attachEmail;
|
||||
expect(getUserInfo({}, [])).to.be.empty;
|
||||
});
|
||||
|
||||
it('returns correct user data', () => {
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let getUserInfo = attachEmail.getUserInfo;
|
||||
let user = getUser();
|
||||
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const { getUserInfo } = attachEmail;
|
||||
const user = getUser();
|
||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
|
||||
expect(data).to.have.property('name', user.auth.local.username);
|
||||
expect(data).to.have.property('email', user.auth.local.email);
|
||||
@@ -53,13 +53,13 @@ describe('emails', () => {
|
||||
});
|
||||
|
||||
it('returns correct user data [facebook users]', () => {
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let getUserInfo = attachEmail.getUserInfo;
|
||||
let user = getUser();
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const { getUserInfo } = attachEmail;
|
||||
const user = getUser();
|
||||
delete user.profile.name;
|
||||
delete user.auth.local.email;
|
||||
|
||||
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
|
||||
expect(data).to.have.property('name', user.auth.local.username);
|
||||
expect(data).to.have.property('email', user.auth.facebook.emails[0].value);
|
||||
@@ -68,13 +68,13 @@ describe('emails', () => {
|
||||
});
|
||||
|
||||
it('has fallbacks for missing data', () => {
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let getUserInfo = attachEmail.getUserInfo;
|
||||
let user = getUser();
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const { getUserInfo } = attachEmail;
|
||||
const user = getUser();
|
||||
delete user.auth.local.email;
|
||||
delete user.auth.facebook;
|
||||
|
||||
let data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||
|
||||
expect(data).to.have.property('name', user.auth.local.username);
|
||||
expect(data).not.to.have.property('email');
|
||||
@@ -85,18 +85,18 @@ describe('emails', () => {
|
||||
|
||||
describe('getGroupUrl', () => {
|
||||
it('returns correct url if group is the tavern', () => {
|
||||
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
|
||||
expect(getGroupUrl({_id: TAVERN_ID, type: 'guild'})).to.eql('/groups/tavern');
|
||||
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
|
||||
expect(getGroupUrl({ _id: TAVERN_ID, type: 'guild' })).to.eql('/groups/tavern');
|
||||
});
|
||||
|
||||
it('returns correct url if group is a guild', () => {
|
||||
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
|
||||
expect(getGroupUrl({_id: 'random _id', type: 'guild'})).to.eql('/groups/guild/random _id');
|
||||
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
|
||||
expect(getGroupUrl({ _id: 'random _id', type: 'guild' })).to.eql('/groups/guild/random _id');
|
||||
});
|
||||
|
||||
it('returns correct url if group is a party', () => {
|
||||
let getGroupUrl = require(pathToEmailLib).getGroupUrl;
|
||||
expect(getGroupUrl({_id: 'random _id', type: 'party'})).to.eql('party');
|
||||
const { getGroupUrl } = require(pathToEmailLib); // eslint-disable-line import/no-dynamic-require, max-len
|
||||
expect(getGroupUrl({ _id: 'random _id', type: 'party' })).to.eql('party');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -111,10 +111,10 @@ describe('emails', () => {
|
||||
|
||||
it('can send a txn email to one recipient', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let sendTxnEmail = attachEmail.sendTxn;
|
||||
let emailType = 'an email type';
|
||||
let mailingInfo = {
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const sendTxnEmail = attachEmail.sendTxn;
|
||||
const emailType = 'an email type';
|
||||
const mailingInfo = {
|
||||
name: 'my name',
|
||||
email: 'my@email',
|
||||
};
|
||||
@@ -125,9 +125,7 @@ describe('emails', () => {
|
||||
body: {
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match((value) => {
|
||||
return Array.isArray(value) && value[0].name === mailingInfo.name;
|
||||
}, 'matches mailing info array'),
|
||||
to: sinon.match(value => Array.isArray(value) && value[0].name === mailingInfo.name, 'matches mailing info array'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -135,10 +133,10 @@ describe('emails', () => {
|
||||
|
||||
it('does not send email if address is missing', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let sendTxnEmail = attachEmail.sendTxn;
|
||||
let emailType = 'an email type';
|
||||
let mailingInfo = {
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const sendTxnEmail = attachEmail.sendTxn;
|
||||
const emailType = 'an email type';
|
||||
const mailingInfo = {
|
||||
name: 'my name',
|
||||
// email: 'my@email',
|
||||
};
|
||||
@@ -149,10 +147,10 @@ describe('emails', () => {
|
||||
|
||||
it('uses getUserInfo in case of user data', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let sendTxnEmail = attachEmail.sendTxn;
|
||||
let emailType = 'an email type';
|
||||
let mailingInfo = getUser();
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const sendTxnEmail = attachEmail.sendTxn;
|
||||
const emailType = 'an email type';
|
||||
const mailingInfo = getUser();
|
||||
|
||||
sendTxnEmail(mailingInfo, emailType);
|
||||
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
|
||||
@@ -168,28 +166,24 @@ describe('emails', () => {
|
||||
|
||||
it('sends email with some default variables', () => {
|
||||
sandbox.stub(nconf, 'get').withArgs('IS_PROD').returns(true);
|
||||
let attachEmail = requireAgain(pathToEmailLib);
|
||||
let sendTxnEmail = attachEmail.sendTxn;
|
||||
let emailType = 'an email type';
|
||||
let mailingInfo = {
|
||||
const attachEmail = requireAgain(pathToEmailLib);
|
||||
const sendTxnEmail = attachEmail.sendTxn;
|
||||
const emailType = 'an email type';
|
||||
const mailingInfo = {
|
||||
name: 'my name',
|
||||
email: 'my@email',
|
||||
};
|
||||
let variables = [1, 2, 3];
|
||||
const variables = [1, 2, 3];
|
||||
|
||||
sendTxnEmail(mailingInfo, emailType, variables);
|
||||
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
|
||||
json: true,
|
||||
body: {
|
||||
data: {
|
||||
variables: sinon.match((value) => {
|
||||
return value[0].name === 'BASE_URL';
|
||||
}, 'matches variables'),
|
||||
personalVariables: sinon.match((value) => {
|
||||
return value[0].rcpt === mailingInfo.email &&
|
||||
value[0].vars[0].name === 'RECIPIENT_NAME' &&
|
||||
value[0].vars[1].name === 'RECIPIENT_UNSUB_URL';
|
||||
}, 'matches personal variables'),
|
||||
variables: sinon.match(value => value[0].name === 'BASE_URL', 'matches variables'),
|
||||
personalVariables: sinon.match(value => value[0].rcpt === mailingInfo.email
|
||||
&& value[0].vars[0].name === 'RECIPIENT_NAME'
|
||||
&& value[0].vars[1].name === 'RECIPIENT_UNSUB_URL', 'matches personal variables'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
|
||||
describe('encryption', () => {
|
||||
it('can encrypt and decrypt', () => {
|
||||
let data = 'some secret text';
|
||||
let encrypted = encrypt(data);
|
||||
let decrypted = decrypt(encrypted);
|
||||
const data = 'some secret text';
|
||||
const encrypted = encrypt(data);
|
||||
const decrypted = decrypt(encrypted);
|
||||
|
||||
expect(encrypted).not.to.equal(data);
|
||||
expect(data).to.equal(decrypted);
|
||||
|
||||
@@ -12,7 +12,7 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
describe('Custom Errors', () => {
|
||||
describe('CustomError', () => {
|
||||
it('is an instance of Error', () => {
|
||||
let customError = new CustomError();
|
||||
const customError = new CustomError();
|
||||
|
||||
expect(customError).to.be.an.instanceOf(Error);
|
||||
});
|
||||
@@ -20,25 +20,25 @@ describe('Custom Errors', () => {
|
||||
|
||||
describe('NotAuthorized', () => {
|
||||
it('is an instance of CustomError', () => {
|
||||
let notAuthorizedError = new NotAuthorized();
|
||||
const notAuthorizedError = new NotAuthorized();
|
||||
|
||||
expect(notAuthorizedError).to.be.an.instanceOf(CustomError);
|
||||
});
|
||||
|
||||
it('it returns an http code of 401', () => {
|
||||
let notAuthorizedError = new NotAuthorized();
|
||||
const notAuthorizedError = new NotAuthorized();
|
||||
|
||||
expect(notAuthorizedError.httpCode).to.eql(401);
|
||||
});
|
||||
|
||||
it('returns a default message', () => {
|
||||
let notAuthorizedError = new NotAuthorized();
|
||||
const notAuthorizedError = new NotAuthorized();
|
||||
|
||||
expect(notAuthorizedError.message).to.eql('Not authorized.');
|
||||
});
|
||||
|
||||
it('allows a custom message', () => {
|
||||
let notAuthorizedError = new NotAuthorized('Custom Error Message');
|
||||
const notAuthorizedError = new NotAuthorized('Custom Error Message');
|
||||
|
||||
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
||||
});
|
||||
@@ -46,25 +46,25 @@ describe('Custom Errors', () => {
|
||||
|
||||
describe('NotFound', () => {
|
||||
it('is an instance of CustomError', () => {
|
||||
let notAuthorizedError = new NotFound();
|
||||
const notAuthorizedError = new NotFound();
|
||||
|
||||
expect(notAuthorizedError).to.be.an.instanceOf(CustomError);
|
||||
});
|
||||
|
||||
it('it returns an http code of 404', () => {
|
||||
let notAuthorizedError = new NotFound();
|
||||
const notAuthorizedError = new NotFound();
|
||||
|
||||
expect(notAuthorizedError.httpCode).to.eql(404);
|
||||
});
|
||||
|
||||
it('returns a default message', () => {
|
||||
let notAuthorizedError = new NotFound();
|
||||
const notAuthorizedError = new NotFound();
|
||||
|
||||
expect(notAuthorizedError.message).to.eql('Not found.');
|
||||
});
|
||||
|
||||
it('allows a custom message', () => {
|
||||
let notAuthorizedError = new NotFound('Custom Error Message');
|
||||
const notAuthorizedError = new NotFound('Custom Error Message');
|
||||
|
||||
expect(notAuthorizedError.message).to.eql('Custom Error Message');
|
||||
});
|
||||
@@ -89,25 +89,25 @@ describe('Custom Errors', () => {
|
||||
|
||||
describe('BadRequest', () => {
|
||||
it('is an instance of CustomError', () => {
|
||||
let badRequestError = new BadRequest();
|
||||
const badRequestError = new BadRequest();
|
||||
|
||||
expect(badRequestError).to.be.an.instanceOf(CustomError);
|
||||
});
|
||||
|
||||
it('it returns an http code of 401', () => {
|
||||
let badRequestError = new BadRequest();
|
||||
const badRequestError = new BadRequest();
|
||||
|
||||
expect(badRequestError.httpCode).to.eql(400);
|
||||
});
|
||||
|
||||
it('returns a default message', () => {
|
||||
let badRequestError = new BadRequest();
|
||||
const badRequestError = new BadRequest();
|
||||
|
||||
expect(badRequestError.message).to.eql('Bad request.');
|
||||
});
|
||||
|
||||
it('allows a custom message', () => {
|
||||
let badRequestError = new BadRequest('Custom Error Message');
|
||||
const badRequestError = new BadRequest('Custom Error Message');
|
||||
|
||||
expect(badRequestError.message).to.eql('Custom Error Message');
|
||||
});
|
||||
@@ -115,25 +115,25 @@ describe('Custom Errors', () => {
|
||||
|
||||
describe('InternalServerError', () => {
|
||||
it('is an instance of CustomError', () => {
|
||||
let internalServerError = new InternalServerError();
|
||||
const internalServerError = new InternalServerError();
|
||||
|
||||
expect(internalServerError).to.be.an.instanceOf(CustomError);
|
||||
});
|
||||
|
||||
it('it returns an http code of 500', () => {
|
||||
let internalServerError = new InternalServerError();
|
||||
const internalServerError = new InternalServerError();
|
||||
|
||||
expect(internalServerError.httpCode).to.eql(500);
|
||||
});
|
||||
|
||||
it('returns a default message', () => {
|
||||
let internalServerError = new InternalServerError();
|
||||
const internalServerError = new InternalServerError();
|
||||
|
||||
expect(internalServerError.message).to.eql('An unexpected error occurred.');
|
||||
});
|
||||
|
||||
it('allows a custom message', () => {
|
||||
let internalServerError = new InternalServerError('Custom Error Message');
|
||||
const internalServerError = new InternalServerError('Custom Error Message');
|
||||
|
||||
expect(internalServerError.message).to.eql('Custom Error Message');
|
||||
});
|
||||
|
||||
60
test/api/unit/libs/highlightMentions.js
Normal file
60
test/api/unit/libs/highlightMentions.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import mongoose from 'mongoose';
|
||||
import {
|
||||
highlightMentions,
|
||||
} from '../../../../website/server/libs/highlightMentions';
|
||||
|
||||
describe('highlightMentions', () => {
|
||||
beforeEach(() => {
|
||||
const mockFind = {
|
||||
select () {
|
||||
return this;
|
||||
},
|
||||
lean () {
|
||||
return this;
|
||||
},
|
||||
exec () {
|
||||
return Promise.resolve([{
|
||||
auth: { local: { username: 'user' } }, _id: '111',
|
||||
}, { auth: { local: { username: 'user2' } }, _id: '222' }, { auth: { local: { username: 'user3' } }, _id: '333' }, { auth: { local: { username: 'user-dash' } }, _id: '444' }, { auth: { local: { username: 'user_underscore' } }, _id: '555' },
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
sinon.stub(mongoose.Model, 'find').returns(mockFind);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('doesn\'t change text without mentions', async () => {
|
||||
const text = 'some chat text';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
it('highlights existing users', async () => {
|
||||
const text = '@user: message';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal('[@user](/profile/111): message');
|
||||
});
|
||||
it('highlights special characters', async () => {
|
||||
const text = '@user-dash: message @user_underscore';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal('[@user-dash](/profile/444): message [@user_underscore](/profile/555)');
|
||||
});
|
||||
it('doesn\'t highlight nonexisting users', async () => {
|
||||
const text = '@nouser message';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal('@nouser message');
|
||||
});
|
||||
it('highlights multiple existing users', async () => {
|
||||
const text = '@user message (@user2) @user3 @user';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal('[@user](/profile/111) message ([@user2](/profile/222)) [@user3](/profile/333) [@user](/profile/111)');
|
||||
});
|
||||
it('doesn\'t highlight more than 5 users', async () => {
|
||||
const text = '@user @user2 @user3 @user4 @user5 @user6';
|
||||
const result = await highlightMentions(text);
|
||||
expect(result[0]).to.equal(text);
|
||||
});
|
||||
});
|
||||
@@ -1,31 +1,15 @@
|
||||
import {
|
||||
translations,
|
||||
localePath,
|
||||
langCodes,
|
||||
approvedLanguages,
|
||||
} from '../../../../website/server/libs/i18n';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('i18n', () => {
|
||||
let listOfLocales = [];
|
||||
|
||||
before((done) => {
|
||||
fs.readdir(localePath, (err, files) => {
|
||||
if (err) return done(err);
|
||||
|
||||
files.forEach((file) => {
|
||||
if (fs.statSync(path.join(localePath, file)).isDirectory() === false) return;
|
||||
listOfLocales.push(file);
|
||||
});
|
||||
|
||||
listOfLocales = listOfLocales.sort();
|
||||
done();
|
||||
});
|
||||
});
|
||||
const listOfLocales = approvedLanguages.sort();
|
||||
|
||||
describe('translations', () => {
|
||||
it('includes a translation object for each locale', () => {
|
||||
listOfLocales.forEach((locale) => {
|
||||
listOfLocales.forEach(locale => {
|
||||
expect(translations[locale]).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,14 +100,23 @@ describe('Items Utils', () => {
|
||||
});
|
||||
|
||||
it('converts values for mounts paths to numbers', () => {
|
||||
expect(castItemVal('items.mounts.Cactus-Base', '5')).to.equal(5);
|
||||
expect(castItemVal('items.mounts.Aether-Invisible', '5')).to.equal(5);
|
||||
expect(castItemVal('items.mounts.Aether-Invalid', '5')).to.equal(5);
|
||||
expect(castItemVal('items.mounts.Cactus-Base', 'true')).to.equal(true);
|
||||
expect(castItemVal('items.mounts.Aether-Invisible', 'false')).to.equal(false);
|
||||
expect(castItemVal('items.mounts.Aether-Invalid', 'true')).to.equal(true);
|
||||
expect(castItemVal('items.mounts.Aether-Invalid', 'truish')).to.equal(true);
|
||||
expect(castItemVal('items.mounts.Aether-Invalid', 0)).to.equal(false);
|
||||
});
|
||||
|
||||
it('converts values for quests paths to numbers', () => {
|
||||
expect(castItemVal('items.quests.atom3', '5')).to.equal(5);
|
||||
expect(castItemVal('items.quests.invalid', '5')).to.equal(5);
|
||||
});
|
||||
|
||||
it('converts values for owned gear', () => {
|
||||
expect(castItemVal('items.gear.owned.shield_warrior_0', 'true')).to.equal(true);
|
||||
expect(castItemVal('items.gear.owned.invalid', 'false')).to.equal(false);
|
||||
expect(castItemVal('items.gear.owned.invalid', 'thruthy')).to.equal(true);
|
||||
expect(castItemVal('items.gear.owned.invalid', 0)).to.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import winston from 'winston';
|
||||
import logger from '../../../../website/server/libs/logger';
|
||||
import {
|
||||
NotFound,
|
||||
} from '../../../../website/server/libs//errors';
|
||||
} from '../../../../website/server/libs/errors';
|
||||
|
||||
describe('logger', () => {
|
||||
let logSpy;
|
||||
@@ -34,7 +34,7 @@ describe('logger', () => {
|
||||
|
||||
context('error object', () => {
|
||||
it('logs the stack and the err data', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
logger.error(errInstance, {
|
||||
data: 1,
|
||||
}, 2, 3);
|
||||
@@ -45,13 +45,13 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
{ data: 1, fullError: errInstance },
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs the stack and the err data with it\'s own fullError property', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
let anotherError = new Error('another error');
|
||||
const errInstance = new Error('An error.');
|
||||
const anotherError = new Error('another error');
|
||||
|
||||
logger.error(errInstance, {
|
||||
data: 1,
|
||||
@@ -64,12 +64,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
{ data: 1, fullError: anotherError },
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs the error when errorData is null', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
|
||||
logger.error(errInstance, null, 2, 3);
|
||||
|
||||
@@ -79,12 +79,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
null,
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs the error when errorData is not an object', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
|
||||
logger.error(errInstance, true, 2, 3);
|
||||
|
||||
@@ -94,12 +94,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
true,
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs the error when errorData does not include isHandledError property', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
|
||||
logger.error(errInstance, { httpCode: 400 }, 2, 3);
|
||||
|
||||
@@ -109,12 +109,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
{ httpCode: 400, fullError: errInstance },
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs the error when errorData includes isHandledError property but is a 500 error', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
|
||||
logger.error(errInstance, {
|
||||
isHandledError: true,
|
||||
@@ -127,12 +127,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
{ httpCode: 502, isHandledError: true, fullError: errInstance },
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs a warning when errorData includes isHandledError property and is not a 500 error', () => {
|
||||
let errInstance = new Error('An error.');
|
||||
const errInstance = new Error('An error.');
|
||||
|
||||
logger.error(errInstance, {
|
||||
isHandledError: true,
|
||||
@@ -145,12 +145,12 @@ describe('logger', () => {
|
||||
errInstance.stack,
|
||||
{ httpCode: 403, isHandledError: true, fullError: errInstance },
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
it('logs additional data from a CustomError', () => {
|
||||
let errInstance = new NotFound('An error.');
|
||||
const errInstance = new NotFound('An error.');
|
||||
|
||||
errInstance.customField = 'Some interesting data';
|
||||
|
||||
@@ -166,7 +166,7 @@ describe('logger', () => {
|
||||
},
|
||||
},
|
||||
2,
|
||||
3
|
||||
3,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import moment from 'moment';
|
||||
import {
|
||||
encrypt,
|
||||
} from '../../../../website/server/libs/encryption';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/api-integration/v3';
|
||||
@@ -20,11 +20,11 @@ import {
|
||||
describe('Password Utilities', () => {
|
||||
describe('compare', () => {
|
||||
it('can compare a correct password hashed with SHA1', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -34,16 +34,16 @@ describe('Password Utilities', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let isValidPassword = await compare(user, textPassword);
|
||||
const isValidPassword = await compare(user, textPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
it('can compare an invalid password hashed with SHA1', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -53,15 +53,15 @@ describe('Password Utilities', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let isValidPassword = await compare(user, 'wrongPassword');
|
||||
const isValidPassword = await compare(user, 'wrongPassword');
|
||||
expect(isValidPassword).to.eql(false);
|
||||
});
|
||||
|
||||
it('can compare a correct password hashed with bcrypt', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -70,15 +70,15 @@ describe('Password Utilities', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let isValidPassword = await compare(user, textPassword);
|
||||
const isValidPassword = await compare(user, textPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
it('can compare an invalid password hashed with bcrypt', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -87,7 +87,7 @@ describe('Password Utilities', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let isValidPassword = await compare(user, 'wrongPassword');
|
||||
const isValidPassword = await compare(user, 'wrongPassword');
|
||||
expect(isValidPassword).to.eql(false);
|
||||
});
|
||||
|
||||
@@ -101,18 +101,18 @@ describe('Password Utilities', () => {
|
||||
|
||||
it('throws an error if passwordToCheck is missing', async () => {
|
||||
try {
|
||||
await compare({a: true});
|
||||
await compare({ a: true });
|
||||
} catch (e) {
|
||||
expect(e.toString()).to.equal('Error: user and passwordToCheck are required parameters.');
|
||||
}
|
||||
});
|
||||
|
||||
it('defaults to SHA1 encryption if salt is provided', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -122,7 +122,7 @@ describe('Password Utilities', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let isValidPassword = await compare(user, textPassword);
|
||||
const isValidPassword = await compare(user, textPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
@@ -141,29 +141,29 @@ describe('Password Utilities', () => {
|
||||
});
|
||||
|
||||
it('returns true if comparing the same password', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||
const isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
it('returns true if comparing a different password', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||
const isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||
expect(isValidPassword).to.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToBcrypt', () => {
|
||||
it('converts an user password hashed with sha1 to bcrypt', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const hashedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
let user = {
|
||||
const user = {
|
||||
auth: {
|
||||
local: {
|
||||
hashed_password: hashedPassword,
|
||||
@@ -178,7 +178,7 @@ describe('Password Utilities', () => {
|
||||
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
|
||||
expect(user.auth.local.hashed_password).to.be.a.string;
|
||||
|
||||
let isValidPassword = await compare(user, textPassword);
|
||||
const isValidPassword = await compare(user, textPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
@@ -192,7 +192,7 @@ describe('Password Utilities', () => {
|
||||
|
||||
it('throws an error if plainTextPassword is missing', async () => {
|
||||
try {
|
||||
await convertToBcrypt({a: true});
|
||||
await convertToBcrypt({ a: true });
|
||||
} catch (e) {
|
||||
expect(e.toString()).to.equal('Error: user and plainTextPassword are required parameters.');
|
||||
}
|
||||
@@ -201,18 +201,18 @@ describe('Password Utilities', () => {
|
||||
|
||||
describe('validatePasswordResetCodeAndFindUser', () => {
|
||||
it('returns false if the code is missing', async () => {
|
||||
let res = await validatePasswordResetCodeAndFindUser();
|
||||
const res = await validatePasswordResetCodeAndFindUser();
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false if the code is invalid json', async () => {
|
||||
let res = await validatePasswordResetCodeAndFindUser('invalid json');
|
||||
const res = await validatePasswordResetCodeAndFindUser('invalid json');
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false if the code cannot be decrypted', async () => {
|
||||
let user = await generateUser();
|
||||
let res = await validatePasswordResetCodeAndFindUser(JSON.stringify({ // not encrypted
|
||||
const user = await generateUser();
|
||||
const res = await validatePasswordResetCodeAndFindUser(JSON.stringify({ // not encrypted
|
||||
userId: user._id,
|
||||
expiresAt: new Date(),
|
||||
}));
|
||||
@@ -220,71 +220,71 @@ describe('Password Utilities', () => {
|
||||
});
|
||||
|
||||
it('returns false if the code is expired', async () => {
|
||||
let user = await generateUser();
|
||||
const user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
const code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().subtract({minutes: 1}),
|
||||
expiresAt: moment().subtract({ minutes: 1 }),
|
||||
}));
|
||||
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await validatePasswordResetCodeAndFindUser(code);
|
||||
const res = await validatePasswordResetCodeAndFindUser(code);
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false if the user does not exist', async () => {
|
||||
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||
userId: Date.now().toString(),
|
||||
expiresAt: moment().add({days: 1}),
|
||||
expiresAt: moment().add({ days: 1 }),
|
||||
})));
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false if the user has no local auth', async () => {
|
||||
let user = await generateUser({
|
||||
const user = await generateUser({
|
||||
auth: {
|
||||
facebook: {},
|
||||
},
|
||||
});
|
||||
let res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
expiresAt: moment().add({ days: 1 }),
|
||||
})));
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
|
||||
let user = await generateUser();
|
||||
const user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
const code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
expiresAt: moment().add({ days: 1 }),
|
||||
}));
|
||||
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': 'invalid',
|
||||
});
|
||||
|
||||
let res = await validatePasswordResetCodeAndFindUser(code);
|
||||
const res = await validatePasswordResetCodeAndFindUser(code);
|
||||
expect(res).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns the user if the password reset code is valid', async () => {
|
||||
let user = await generateUser();
|
||||
const user = await generateUser();
|
||||
|
||||
let code = encrypt(JSON.stringify({
|
||||
const code = encrypt(JSON.stringify({
|
||||
userId: user._id,
|
||||
expiresAt: moment().add({days: 1}),
|
||||
expiresAt: moment().add({ days: 1 }),
|
||||
}));
|
||||
|
||||
await user.update({
|
||||
'auth.local.passwordResetCode': code,
|
||||
});
|
||||
|
||||
let res = await validatePasswordResetCodeAndFindUser(code);
|
||||
const res = await validatePasswordResetCodeAndFindUser(code);
|
||||
expect(res).not.to.equal(false);
|
||||
expect(res._id).to.equal(user._id);
|
||||
});
|
||||
@@ -293,8 +293,8 @@ describe('Password Utilities', () => {
|
||||
describe('bcrypt', () => {
|
||||
describe('Hash', () => {
|
||||
it('returns a hashed string', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
expect(hashedPassword).to.be.a.string;
|
||||
});
|
||||
@@ -302,18 +302,18 @@ describe('Password Utilities', () => {
|
||||
|
||||
describe('Compare', () => {
|
||||
it('returns true if comparing the same password', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||
const isValidPassword = await bcryptCompare(textPassword, hashedPassword);
|
||||
expect(isValidPassword).to.eql(true);
|
||||
});
|
||||
|
||||
it('returns true if comparing a different password', async () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let hashedPassword = await bcryptHash(textPassword);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const hashedPassword = await bcryptHash(textPassword);
|
||||
|
||||
let isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||
const isValidPassword = await bcryptCompare('anotherPassword', hashedPassword);
|
||||
expect(isValidPassword).to.eql(false);
|
||||
});
|
||||
});
|
||||
@@ -322,19 +322,19 @@ describe('Password Utilities', () => {
|
||||
describe('SHA1', () => {
|
||||
describe('Encrypt', () => {
|
||||
it('always encrypt the same password to the same value when using the same salt', () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let salt = sha1MakeSalt();
|
||||
let encryptedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const salt = sha1MakeSalt();
|
||||
const encryptedPassword = sha1EncryptPassword(textPassword, salt);
|
||||
|
||||
expect(sha1EncryptPassword(textPassword, salt)).to.eql(encryptedPassword);
|
||||
});
|
||||
|
||||
it('never encrypt the same password to the same value when using a different salt', () => {
|
||||
let textPassword = 'mySecretPassword';
|
||||
let aSalt = sha1MakeSalt();
|
||||
let anotherSalt = sha1MakeSalt();
|
||||
let anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
|
||||
let anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
|
||||
const textPassword = 'mySecretPassword';
|
||||
const aSalt = sha1MakeSalt();
|
||||
const anotherSalt = sha1MakeSalt();
|
||||
const anEncryptedPassword = sha1EncryptPassword(textPassword, aSalt);
|
||||
const anotherEncryptedPassword = sha1EncryptPassword(textPassword, anotherSalt);
|
||||
|
||||
expect(anEncryptedPassword).not.to.eql(anotherEncryptedPassword);
|
||||
});
|
||||
@@ -342,14 +342,14 @@ describe('Password Utilities', () => {
|
||||
|
||||
describe('Make Salt', () => {
|
||||
it('creates a salt with length 10 by default', () => {
|
||||
let salt = sha1MakeSalt();
|
||||
const salt = sha1MakeSalt();
|
||||
|
||||
expect(salt.length).to.eql(10);
|
||||
});
|
||||
|
||||
it('can create a salt of any length', () => {
|
||||
let length = 24;
|
||||
let salt = sha1MakeSalt(length);
|
||||
const length = 24;
|
||||
const salt = sha1MakeSalt(length);
|
||||
|
||||
expect(salt.length).to.eql(length);
|
||||
});
|
||||
|
||||
@@ -2,19 +2,20 @@ import moment from 'moment';
|
||||
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../website/common';
|
||||
import { createNonLeaderGroupMember } from '../paymentHelpers';
|
||||
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Amazon Payments - Cancel Subscription', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
|
||||
let user, group, headers, billingAgreementId, subscriptionBlock, subscriptionLength;
|
||||
let user; let group; let headers; let billingAgreementId; let subscriptionBlock; let
|
||||
subscriptionLength;
|
||||
let getBillingAgreementDetailsSpy;
|
||||
let paymentCancelSubscriptionSpy;
|
||||
|
||||
@@ -50,7 +51,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||
.resolves({
|
||||
BillingAgreementDetails: {
|
||||
BillingAgreementStatus: {State: 'Open'},
|
||||
BillingAgreementStatus: { State: 'Open' },
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -81,7 +82,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
|
||||
getBillingAgreementDetailsSpy.resolves({
|
||||
BillingAgreementDetails: {
|
||||
BillingAgreementStatus: {State: 'Closed'},
|
||||
BillingAgreementStatus: { State: 'Closed' },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -89,7 +90,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
paymentCancelSubscriptionSpy.resolves({});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
amzLib.getBillingAgreementDetails.restore();
|
||||
payments.cancelSubscription.restore();
|
||||
});
|
||||
@@ -97,7 +98,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
it('should throw an error if we are missing a subscription', async () => {
|
||||
user.purchased.plan.customerId = undefined;
|
||||
|
||||
await expect(amzLib.cancelSubscription({user}))
|
||||
await expect(amzLib.cancelSubscription({ user }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -108,7 +109,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
it('should cancel a user subscription', async () => {
|
||||
billingAgreementId = user.purchased.plan.customerId;
|
||||
|
||||
await amzLib.cancelSubscription({user, headers});
|
||||
await amzLib.cancelSubscription({ user, headers });
|
||||
|
||||
expectAmazonCancelUserSubscriptionSpy();
|
||||
expectAmazonStubs();
|
||||
@@ -117,10 +118,10 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
it('should close a user subscription if amazon not closed', async () => {
|
||||
amzLib.getBillingAgreementDetails.restore();
|
||||
expectBillingAggreementDetailSpy();
|
||||
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||
const closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||
billingAgreementId = user.purchased.plan.customerId;
|
||||
|
||||
await amzLib.cancelSubscription({user, headers});
|
||||
await amzLib.cancelSubscription({ user, headers });
|
||||
|
||||
expectAmazonStubs();
|
||||
expect(closeBillingAgreementSpy).to.be.calledOnce;
|
||||
@@ -132,7 +133,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if group is not found', async () => {
|
||||
await expect(amzLib.cancelSubscription({user, groupId: 'fake-id'}))
|
||||
await expect(amzLib.cancelSubscription({ user, groupId: 'fake-id' }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 404,
|
||||
name: 'NotFound',
|
||||
@@ -141,9 +142,9 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if user is not group leader', async () => {
|
||||
let nonLeader = await createNonLeaderGroupMember(group);
|
||||
const nonLeader = await createNonLeaderGroupMember(group);
|
||||
|
||||
await expect(amzLib.cancelSubscription({user: nonLeader, groupId: group._id}))
|
||||
await expect(amzLib.cancelSubscription({ user: nonLeader, groupId: group._id }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -154,7 +155,7 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
it('should cancel a group subscription', async () => {
|
||||
billingAgreementId = group.purchased.plan.customerId;
|
||||
|
||||
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
||||
await amzLib.cancelSubscription({ user, groupId: group._id, headers });
|
||||
|
||||
expectAmazonCancelGroupSubscriptionSpy(group._id);
|
||||
expectAmazonStubs();
|
||||
@@ -163,10 +164,10 @@ describe('Amazon Payments - Cancel Subscription', () => {
|
||||
it('should close a group subscription if amazon not closed', async () => {
|
||||
amzLib.getBillingAgreementDetails.restore();
|
||||
expectBillingAggreementDetailSpy();
|
||||
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||
const closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').resolves({});
|
||||
billingAgreementId = group.purchased.plan.customerId;
|
||||
|
||||
await amzLib.cancelSubscription({user, groupId: group._id, headers});
|
||||
await amzLib.cancelSubscription({ user, groupId: group._id, headers });
|
||||
|
||||
expectAmazonStubs();
|
||||
expect(closeBillingAgreementSpy).to.be.calledOnce;
|
||||
|
||||
@@ -3,11 +3,12 @@ import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Amazon Payments - Checkout', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user, orderReferenceId, headers;
|
||||
let user; let orderReferenceId; let
|
||||
headers;
|
||||
let setOrderReferenceDetailsSpy;
|
||||
let confirmOrderReferenceSpy;
|
||||
let authorizeSpy;
|
||||
@@ -62,7 +63,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
expect(closeOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
headers = {};
|
||||
orderReferenceId = 'orderReferenceId';
|
||||
@@ -88,7 +89,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
amzLib.setOrderReferenceDetails.restore();
|
||||
amzLib.confirmOrderReference.restore();
|
||||
amzLib.authorize.restore();
|
||||
@@ -101,7 +102,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
function expectBuyGemsStub (paymentMethod, gift) {
|
||||
expect(paymentBuyGemsStub).to.be.calledOnce;
|
||||
|
||||
let expectedArgs = {
|
||||
const expectedArgs = {
|
||||
user,
|
||||
paymentMethod,
|
||||
headers,
|
||||
@@ -112,7 +113,7 @@ describe('Amazon Payments - Checkout', () => {
|
||||
|
||||
it('should purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
await amzLib.checkout({user, orderReferenceId, headers});
|
||||
await amzLib.checkout({ user, orderReferenceId, headers });
|
||||
|
||||
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD);
|
||||
expectAmazonStubs();
|
||||
@@ -121,9 +122,9 @@ describe('Amazon Payments - Checkout', () => {
|
||||
});
|
||||
|
||||
it('should error if gem amount is too low', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 0,
|
||||
@@ -131,7 +132,9 @@ describe('Amazon Payments - Checkout', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await expect(amzLib.checkout({gift, user, orderReferenceId, headers}))
|
||||
await expect(amzLib.checkout({
|
||||
gift, user, orderReferenceId, headers,
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: 'Amount must be at least 1.',
|
||||
@@ -141,18 +144,19 @@ describe('Amazon Payments - Checkout', () => {
|
||||
|
||||
it('should error if user cannot get gems gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
await expect(amzLib.checkout({ user, orderReferenceId, headers }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
});
|
||||
user.canGetGems.restore();
|
||||
});
|
||||
|
||||
it('should gift gems', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'gems',
|
||||
uuid: receivingUser._id,
|
||||
gems: {
|
||||
@@ -160,16 +164,18 @@ describe('Amazon Payments - Checkout', () => {
|
||||
},
|
||||
};
|
||||
amount = 16 / 4;
|
||||
await amzLib.checkout({gift, user, orderReferenceId, headers});
|
||||
await amzLib.checkout({
|
||||
gift, user, orderReferenceId, headers,
|
||||
});
|
||||
|
||||
expectBuyGemsStub(amzLib.constants.PAYMENT_METHOD_GIFT, gift);
|
||||
expectAmazonStubs();
|
||||
});
|
||||
|
||||
it('should gift a subscription', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'subscription',
|
||||
subscription: {
|
||||
key: subKey,
|
||||
@@ -178,7 +184,9 @@ describe('Amazon Payments - Checkout', () => {
|
||||
};
|
||||
amount = common.content.subscriptionBlocks[subKey].price;
|
||||
|
||||
await amzLib.checkout({user, orderReferenceId, headers, gift});
|
||||
await amzLib.checkout({
|
||||
user, orderReferenceId, headers, gift,
|
||||
});
|
||||
|
||||
gift.member = receivingUser;
|
||||
expect(paymentCreateSubscritionStub).to.be.calledOnce;
|
||||
|
||||
@@ -2,18 +2,19 @@ import cc from 'coupon-code';
|
||||
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { model as Coupon } from '../../../../../../website/server/models/coupon';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
import common from '../../../../../../website/common';
|
||||
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Amazon Payments - Subscribe', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user, group, amount, billingAgreementId, sub, coupon, groupId, headers;
|
||||
let user; let group; let amount; let billingAgreementId; let sub; let coupon; let groupId; let
|
||||
headers;
|
||||
let amazonSetBillingAgreementDetailsSpy;
|
||||
let amazonConfirmBillingAgreementSpy;
|
||||
let amazonAuthorizeOnBillingAgreementSpy;
|
||||
@@ -60,7 +61,7 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
sinon.stub(common, 'uuid').returns('uuid-generated');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
amzLib.setBillingAgreementDetails.restore();
|
||||
amzLib.confirmBillingAgreement.restore();
|
||||
amzLib.authorizeOnBillingAgreement.restore();
|
||||
@@ -168,7 +169,7 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
sub.key = 'google_6mo';
|
||||
coupon = 'example-coupon';
|
||||
|
||||
let couponModel = new Coupon();
|
||||
const couponModel = new Coupon();
|
||||
couponModel.event = 'google_6mo';
|
||||
await couponModel.save();
|
||||
|
||||
@@ -195,9 +196,9 @@ describe('Amazon Payments - Subscribe', () => {
|
||||
sub.key = 'google_6mo';
|
||||
coupon = 'example-coupon';
|
||||
|
||||
let couponModel = new Coupon();
|
||||
const couponModel = new Coupon();
|
||||
couponModel.event = 'google_6mo';
|
||||
let updatedCouponModel = await couponModel.save();
|
||||
const updatedCouponModel = await couponModel.save();
|
||||
|
||||
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);
|
||||
|
||||
|
||||
@@ -2,16 +2,17 @@ import uuid from 'uuid';
|
||||
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../website/server/models/group';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import payments from '../../../../../../website/server/libs/payments/payments';
|
||||
|
||||
describe('#upgradeGroupPlan', () => {
|
||||
let spy, data, user, group, uuidString;
|
||||
let spy; let data; let user; let group; let
|
||||
uuidString;
|
||||
|
||||
beforeEach(async function () {
|
||||
beforeEach(async () => {
|
||||
user = new User();
|
||||
user.profile.name = 'sender';
|
||||
|
||||
@@ -46,7 +47,7 @@ describe('#upgradeGroupPlan', () => {
|
||||
data.sub.quantity = 3;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
amzLib.authorizeOnBillingAgreement.restore();
|
||||
uuid.v4.restore();
|
||||
});
|
||||
@@ -55,7 +56,7 @@ describe('#upgradeGroupPlan', () => {
|
||||
data.paymentMethod = amzLib.constants.PAYMENT_METHOD;
|
||||
await payments.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
|
||||
updatedGroup.memberCount += 1;
|
||||
await updatedGroup.save();
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/* eslint-disable camelcase */
|
||||
import iapModule from '../../../../../website/server/libs/inAppPurchases';
|
||||
import moment from 'moment';
|
||||
import payments from '../../../../../website/server/libs/payments/payments';
|
||||
import applePayments from '../../../../../website/server/libs/payments/apple';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../website/server/models/user';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import moment from 'moment';
|
||||
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||
import { mockFindById, restoreFindById } from '../../../../helpers/mongoose.helper';
|
||||
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Apple Payments', () => {
|
||||
let subKey = 'basic_3mo';
|
||||
describe('Apple Payments', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
|
||||
describe('verifyGemPurchase', () => {
|
||||
let sku, user, token, receipt, headers;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentBuyGemsStub, iapGetPurchaseDataStub;
|
||||
let sku; let user; let token; let receipt; let
|
||||
headers;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let paymentBuyGemsStub; let
|
||||
iapGetPurchaseDataStub;
|
||||
|
||||
beforeEach(() => {
|
||||
token = 'testToken';
|
||||
@@ -24,33 +25,34 @@ describe('Apple Payments', () => {
|
||||
receipt = `{"token": "${token}", "productId": "${sku}"}`;
|
||||
headers = {};
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({});
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: 'com.habitrpg.ios.Habitica.21gems',
|
||||
transactionId: token,
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
productId: 'com.habitrpg.ios.Habitica.21gems',
|
||||
transactionId: token,
|
||||
}]);
|
||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iapModule.getPurchaseData.restore();
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
iap.getPurchaseData.restore();
|
||||
payments.buyGems.restore();
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||
await expect(applePayments.verifyGemPurchase({ user, receipt, headers }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -60,9 +62,9 @@ describe('Apple Payments', () => {
|
||||
|
||||
it('should throw an error if getPurchaseData is invalid', async () => {
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData').returns([]);
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData').returns([]);
|
||||
|
||||
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||
await expect(applePayments.verifyGemPurchase({ user, receipt, headers }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -72,7 +74,7 @@ describe('Apple Payments', () => {
|
||||
|
||||
it('errors if the user cannot purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||
await expect(applePayments.verifyGemPurchase({ user, receipt, headers }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -85,12 +87,13 @@ describe('Apple Payments', () => {
|
||||
it('errors if amount does not exist', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: 'badProduct',
|
||||
transactionId: token,
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
productId: 'badProduct',
|
||||
transactionId: token,
|
||||
}]);
|
||||
|
||||
await expect(applePayments.verifyGemPurchase({user, receipt, headers}))
|
||||
await expect(applePayments.verifyGemPurchase({ user, receipt, headers }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -126,13 +129,14 @@ describe('Apple Payments', () => {
|
||||
gemsCanPurchase.forEach(gemTest => {
|
||||
it(`purchases ${gemTest.productId} gems`, async () => {
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: gemTest.productId,
|
||||
transactionId: token,
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
productId: gemTest.productId,
|
||||
transactionId: token,
|
||||
}]);
|
||||
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
await applePayments.verifyGemPurchase({user, receipt, headers});
|
||||
await applePayments.verifyGemPurchase({ user, receipt, headers });
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
@@ -160,13 +164,16 @@ describe('Apple Payments', () => {
|
||||
mockFindById(receivingUser);
|
||||
|
||||
iapGetPurchaseDataStub.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{productId: gemsCanPurchase[0].productId,
|
||||
transactionId: token,
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
productId: gemsCanPurchase[0].productId,
|
||||
transactionId: token,
|
||||
}]);
|
||||
|
||||
const gift = {uuid: receivingUser._id};
|
||||
await applePayments.verifyGemPurchase({user, gift, receipt, headers});
|
||||
const gift = { uuid: receivingUser._id };
|
||||
await applePayments.verifyGemPurchase({
|
||||
user, gift, receipt, headers,
|
||||
});
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
@@ -187,8 +194,11 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
let sub, sku, user, token, receipt, headers, nextPaymentProcessing;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentsCreateSubscritionStub, iapGetPurchaseDataStub;
|
||||
let sub; let sku; let user; let token; let receipt; let headers; let
|
||||
nextPaymentProcessing;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub;
|
||||
let paymentsCreateSubscritionStub; let
|
||||
iapGetPurchaseDataStub;
|
||||
|
||||
beforeEach(() => {
|
||||
sub = common.content.subscriptionBlocks[subKey];
|
||||
@@ -197,25 +207,25 @@ describe('Apple Payments', () => {
|
||||
token = 'test-token';
|
||||
headers = {};
|
||||
receipt = `{"token": "${token}"}`;
|
||||
nextPaymentProcessing = moment.utc().add({days: 2});
|
||||
nextPaymentProcessing = moment.utc().add({ days: 2 });
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({});
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().subtract({day: 1}).toDate(),
|
||||
expirationDate: moment.utc().subtract({ day: 1 }).toDate(),
|
||||
productId: sku,
|
||||
transactionId: token,
|
||||
}, {
|
||||
expirationDate: moment.utc().add({day: 1}).toDate(),
|
||||
expirationDate: moment.utc().add({ day: 1 }).toDate(),
|
||||
productId: 'wrongsku',
|
||||
transactionId: token,
|
||||
}, {
|
||||
expirationDate: moment.utc().add({day: 1}).toDate(),
|
||||
expirationDate: moment.utc().add({ day: 1 }).toDate(),
|
||||
productId: sku,
|
||||
transactionId: token,
|
||||
}]);
|
||||
@@ -223,10 +233,10 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iapModule.getPurchaseData.restore();
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
iap.getPurchaseData.restore();
|
||||
if (payments.createSubscription.restore) payments.createSubscription.restore();
|
||||
});
|
||||
|
||||
@@ -240,8 +250,8 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
|
||||
@@ -272,10 +282,10 @@ describe('Apple Payments', () => {
|
||||
];
|
||||
subOptions.forEach(option => {
|
||||
it(`creates a user subscription for ${option.sku}`, async () => {
|
||||
iapModule.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
iap.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{
|
||||
expirationDate: moment.utc().add({day: 1}).toDate(),
|
||||
expirationDate: moment.utc().add({ day: 1 }).toDate(),
|
||||
productId: option.sku,
|
||||
transactionId: token,
|
||||
}]);
|
||||
@@ -319,8 +329,10 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
describe('cancelSubscribe ', () => {
|
||||
let user, token, receipt, headers, customerId, expirationDate;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, iapGetPurchaseDataStub, paymentCancelSubscriptionSpy;
|
||||
let user; let token; let receipt; let headers; let customerId; let
|
||||
expirationDate;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let iapGetPurchaseDataStub; let
|
||||
paymentCancelSubscriptionSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
token = 'test-token';
|
||||
@@ -329,15 +341,15 @@ describe('Apple Payments', () => {
|
||||
customerId = 'test-customerId';
|
||||
expirationDate = moment.utc();
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({
|
||||
expirationDate,
|
||||
});
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{expirationDate: expirationDate.toDate()}]);
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{ expirationDate: expirationDate.toDate() }]);
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
|
||||
user = new User();
|
||||
@@ -350,11 +362,11 @@ describe('Apple Payments', () => {
|
||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iapModule.getPurchaseData.restore();
|
||||
afterEach(() => {
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
iap.getPurchaseData.restore();
|
||||
payments.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
@@ -370,9 +382,9 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if subscription is still valid', async () => {
|
||||
iapModule.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{expirationDate: expirationDate.add({day: 1}).toDate()}]);
|
||||
iap.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{ expirationDate: expirationDate.add({ day: 1 }).toDate() }]);
|
||||
|
||||
await expect(applePayments.cancelSubscribe(user, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
@@ -383,8 +395,8 @@ describe('Apple Payments', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(applePayments.cancelSubscribe(user, headers))
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/* eslint-disable camelcase */
|
||||
import iapModule from '../../../../../website/server/libs/inAppPurchases';
|
||||
import moment from 'moment';
|
||||
import payments from '../../../../../website/server/libs/payments/payments';
|
||||
import googlePayments from '../../../../../website/server/libs/payments/google';
|
||||
import iap from '../../../../../website/server/libs/inAppPurchases';
|
||||
import {model as User} from '../../../../../website/server/models/user';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import common from '../../../../../website/common';
|
||||
import moment from 'moment';
|
||||
import {mockFindById, restoreFindById} from '../../../../helpers/mongoose.helper';
|
||||
import { mockFindById, restoreFindById } from '../../../../helpers/mongoose.helper';
|
||||
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('Google Payments', () => {
|
||||
let subKey = 'basic_3mo';
|
||||
describe('Google Payments', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
|
||||
describe('verifyGemPurchase', () => {
|
||||
let sku, user, token, receipt, signature, headers;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentBuyGemsStub;
|
||||
let sku; let user; let token; let receipt; let signature; let
|
||||
headers;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let
|
||||
paymentBuyGemsStub;
|
||||
|
||||
beforeEach(() => {
|
||||
sku = 'com.habitrpg.android.habitica.iap.21gems';
|
||||
@@ -24,28 +25,30 @@ describe('Google Payments', () => {
|
||||
signature = '';
|
||||
headers = {};
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({});
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
paymentBuyGemsStub = sinon.stub(payments, 'buyGems').resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
payments.buyGems.restore();
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||
await expect(googlePayments.verifyGemPurchase({
|
||||
user, receipt, signature, headers,
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -56,7 +59,9 @@ describe('Google Payments', () => {
|
||||
it('should throw an error if productId is invalid', async () => {
|
||||
receipt = `{"token": "${token}", "productId": "invalid"}`;
|
||||
|
||||
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||
await expect(googlePayments.verifyGemPurchase({
|
||||
user, receipt, signature, headers,
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -67,7 +72,9 @@ describe('Google Payments', () => {
|
||||
it('should throw an error if user cannot purchase gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
|
||||
await expect(googlePayments.verifyGemPurchase({user, receipt, signature, headers}))
|
||||
await expect(googlePayments.verifyGemPurchase({
|
||||
user, receipt, signature, headers,
|
||||
}))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -79,7 +86,9 @@ describe('Google Payments', () => {
|
||||
|
||||
it('purchases gems', async () => {
|
||||
sinon.stub(user, 'canGetGems').resolves(true);
|
||||
await googlePayments.verifyGemPurchase({user, receipt, signature, headers});
|
||||
await googlePayments.verifyGemPurchase({
|
||||
user, receipt, signature, headers,
|
||||
});
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
@@ -107,8 +116,10 @@ describe('Google Payments', () => {
|
||||
|
||||
mockFindById(receivingUser);
|
||||
|
||||
const gift = {uuid: receivingUser._id};
|
||||
await googlePayments.verifyGemPurchase({user, gift, receipt, signature, headers});
|
||||
const gift = { uuid: receivingUser._id };
|
||||
await googlePayments.verifyGemPurchase({
|
||||
user, gift, receipt, signature, headers,
|
||||
});
|
||||
|
||||
expect(iapSetupStub).to.be.calledOnce;
|
||||
expect(iapValidateStub).to.be.calledOnce;
|
||||
@@ -131,8 +142,10 @@ describe('Google Payments', () => {
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
let sub, sku, user, token, receipt, signature, headers, nextPaymentProcessing;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, paymentsCreateSubscritionStub;
|
||||
let sub; let sku; let user; let token; let receipt; let signature; let headers; let
|
||||
nextPaymentProcessing;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let
|
||||
paymentsCreateSubscritionStub;
|
||||
|
||||
beforeEach(() => {
|
||||
sub = common.content.subscriptionBlocks[subKey];
|
||||
@@ -142,30 +155,31 @@ describe('Google Payments', () => {
|
||||
headers = {};
|
||||
receipt = `{"token": "${token}"}`;
|
||||
signature = '';
|
||||
nextPaymentProcessing = moment.utc().add({days: 2});
|
||||
nextPaymentProcessing = moment.utc().add({ days: 2 });
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({});
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
paymentsCreateSubscritionStub = sinon.stub(payments, 'createSubscription').resolves({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
payments.createSubscription.restore();
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(googlePayments.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||
await expect(googlePayments
|
||||
.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -176,7 +190,8 @@ describe('Google Payments', () => {
|
||||
it('should throw an error if sku is invalid', async () => {
|
||||
sku = 'invalid';
|
||||
|
||||
await expect(googlePayments.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||
await expect(googlePayments
|
||||
.subscribe(sku, user, receipt, signature, headers, nextPaymentProcessing))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
name: 'NotAuthorized',
|
||||
@@ -203,15 +218,17 @@ describe('Google Payments', () => {
|
||||
paymentMethod: googlePayments.constants.PAYMENT_METHOD_GOOGLE,
|
||||
sub,
|
||||
headers,
|
||||
additionalData: {data: receipt, signature},
|
||||
additionalData: { data: receipt, signature },
|
||||
nextPaymentProcessing,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelSubscribe ', () => {
|
||||
let user, token, receipt, signature, headers, customerId, expirationDate;
|
||||
let iapSetupStub, iapValidateStub, iapIsValidatedStub, iapGetPurchaseDataStub, paymentCancelSubscriptionSpy;
|
||||
let user; let token; let receipt; let signature; let headers; let customerId; let
|
||||
expirationDate;
|
||||
let iapSetupStub; let iapValidateStub; let iapIsValidatedStub; let iapGetPurchaseDataStub; let
|
||||
paymentCancelSubscriptionSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
token = 'test-token';
|
||||
@@ -221,15 +238,15 @@ describe('Google Payments', () => {
|
||||
customerId = 'test-customerId';
|
||||
expirationDate = moment.utc();
|
||||
|
||||
iapSetupStub = sinon.stub(iapModule, 'setup')
|
||||
iapSetupStub = sinon.stub(iap, 'setup')
|
||||
.resolves();
|
||||
iapValidateStub = sinon.stub(iapModule, 'validate')
|
||||
iapValidateStub = sinon.stub(iap, 'validate')
|
||||
.resolves({
|
||||
expirationDate,
|
||||
});
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{expirationDate: expirationDate.toDate()}]);
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{ expirationDate: expirationDate.toDate() }]);
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(true);
|
||||
|
||||
user = new User();
|
||||
@@ -237,16 +254,16 @@ describe('Google Payments', () => {
|
||||
user.purchased.plan.customerId = customerId;
|
||||
user.purchased.plan.paymentMethod = googlePayments.constants.PAYMENT_METHOD_GOOGLE;
|
||||
user.purchased.plan.planId = subKey;
|
||||
user.purchased.plan.additionalData = {data: receipt, signature};
|
||||
user.purchased.plan.additionalData = { data: receipt, signature };
|
||||
|
||||
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription').resolves({});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
iapModule.setup.restore();
|
||||
iapModule.validate.restore();
|
||||
iapModule.isValidated.restore();
|
||||
iapModule.getPurchaseData.restore();
|
||||
afterEach(() => {
|
||||
iap.setup.restore();
|
||||
iap.validate.restore();
|
||||
iap.isValidated.restore();
|
||||
iap.getPurchaseData.restore();
|
||||
payments.cancelSubscription.restore();
|
||||
});
|
||||
|
||||
@@ -262,9 +279,9 @@ describe('Google Payments', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if subscription is still valid', async () => {
|
||||
iapModule.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iapModule, 'getPurchaseData')
|
||||
.returns([{expirationDate: expirationDate.add({day: 1}).toDate()}]);
|
||||
iap.getPurchaseData.restore();
|
||||
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
|
||||
.returns([{ expirationDate: expirationDate.add({ day: 1 }).toDate() }]);
|
||||
|
||||
await expect(googlePayments.cancelSubscribe(user, headers))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
@@ -275,8 +292,8 @@ describe('Google Payments', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if receipt is invalid', async () => {
|
||||
iapModule.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iapModule, 'isValidated')
|
||||
iap.isValidated.restore();
|
||||
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
|
||||
.returns(false);
|
||||
|
||||
await expect(googlePayments.cancelSubscribe(user, headers))
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import * as sender from '../../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../../website/server/libs/payments/payments';
|
||||
import api from '../../../../../../website/server/libs/payments/payments';
|
||||
import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../website/server/models/group';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
import i18n from '../../../../../../website/common/script/i18n';
|
||||
|
||||
describe('Canceling a subscription for group', () => {
|
||||
let plan, group, user, data;
|
||||
let plan; let group; let user; let
|
||||
data;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = new User();
|
||||
@@ -67,9 +68,9 @@ describe('Canceling a subscription for group', () => {
|
||||
data.groupId = group._id;
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
});
|
||||
@@ -81,9 +82,9 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
|
||||
});
|
||||
@@ -95,9 +96,9 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
|
||||
});
|
||||
@@ -108,9 +109,9 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
@@ -122,7 +123,7 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
|
||||
@@ -134,12 +135,12 @@ describe('Canceling a subscription for group', () => {
|
||||
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(user._id);
|
||||
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-cancel-subscription');
|
||||
expect(sender.sendTxn.firstCall.args[2]).to.eql([
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
]);
|
||||
});
|
||||
|
||||
it('prevents non group leader from managing subscription', async () => {
|
||||
let groupMember = new User();
|
||||
const groupMember = new User();
|
||||
data.user = groupMember;
|
||||
data.groupId = group._id;
|
||||
|
||||
@@ -160,7 +161,7 @@ describe('Canceling a subscription for group', () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
let newLeader = new User();
|
||||
const newLeader = new User();
|
||||
updatedGroup.leader = newLeader._id;
|
||||
await updatedGroup.save();
|
||||
|
||||
@@ -192,15 +193,15 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
let updatedLeader = await User.findById(user._id).exec();
|
||||
let daysTillTermination = moment(updatedLeader.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const updatedLeader = await User.findById(user._id).exec();
|
||||
const daysTillTermination = moment(updatedLeader.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
expect(daysTillTermination).to.be.within(2, 3); // only a few days
|
||||
});
|
||||
|
||||
it('sends an email to members of group', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.guilds.push(group._id);
|
||||
await recipient.save();
|
||||
@@ -214,8 +215,8 @@ describe('Canceling a subscription for group', () => {
|
||||
expect(sender.sendTxn.thirdCall.args[0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.thirdCall.args[1]).to.equal('group-member-cancel');
|
||||
expect(sender.sendTxn.thirdCall.args[2]).to.eql([
|
||||
{name: 'LEADER', content: user.profile.name},
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{ name: 'LEADER', content: user.profile.name },
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -223,7 +224,7 @@ describe('Canceling a subscription for group', () => {
|
||||
plan.key = 'basic_earned';
|
||||
plan.customerId = api.constants.UNLIMITED_CUSTOMER_ID;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -233,12 +234,12 @@ describe('Canceling a subscription for group', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let updatedLeader = await User.findById(user._id).exec();
|
||||
const updatedLeader = await User.findById(user._id).exec();
|
||||
expect(updatedLeader.purchased.plan.dateTerminated).to.not.exist;
|
||||
});
|
||||
|
||||
it('does not cancel a user subscription if they are still in another active group plan', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
recipient.purchased.plan = plan;
|
||||
@@ -252,10 +253,10 @@ describe('Canceling a subscription for group', () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
let firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
let extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
const firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
|
||||
let group2 = generateGroup({
|
||||
const group2 = generateGroup({
|
||||
name: 'test group2',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
@@ -291,10 +292,10 @@ describe('Canceling a subscription for group', () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(user._id).exec();
|
||||
let firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
let extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
const firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
|
||||
let group2 = generateGroup({
|
||||
const group2 = generateGroup({
|
||||
name: 'test group2',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
|
||||
@@ -3,7 +3,7 @@ import stripeModule from 'stripe';
|
||||
import nconf from 'nconf';
|
||||
|
||||
import * as sender from '../../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../../website/server/libs/payments/payments';
|
||||
import api from '../../../../../../website/server/libs/payments/payments';
|
||||
import amzLib from '../../../../../../website/server/libs/payments/amazon';
|
||||
import paypalPayments from '../../../../../../website/server/libs/payments/paypal';
|
||||
import stripePayments from '../../../../../../website/server/libs/payments/stripe';
|
||||
@@ -11,7 +11,7 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
import { model as Group } from '../../../../../../website/server/models/group';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../../helpers/api-unit.helper';
|
||||
|
||||
describe('Purchasing a group plan for group', () => {
|
||||
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE = 'Google_subscription';
|
||||
@@ -19,10 +19,11 @@ describe('Purchasing a group plan for group', () => {
|
||||
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL = 'normal_subscription';
|
||||
const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE = 'no_subscription';
|
||||
|
||||
let plan, group, user, data;
|
||||
let stripe = stripeModule('test');
|
||||
let groupLeaderName = 'sender';
|
||||
let groupName = 'test group';
|
||||
let plan; let group; let user; let
|
||||
data;
|
||||
const stripe = stripeModule('test');
|
||||
const groupLeaderName = 'sender';
|
||||
const groupName = 'test group';
|
||||
|
||||
beforeEach(async () => {
|
||||
user = new User();
|
||||
@@ -68,14 +69,17 @@ describe('Purchasing a group plan for group', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let subscriptionId = 'subId';
|
||||
const subscriptionId = 'subId';
|
||||
sinon.stub(stripe.customers, 'del').resolves({});
|
||||
|
||||
let currentPeriodEndTimeStamp = moment().add(3, 'months').unix();
|
||||
const currentPeriodEndTimeStamp = moment().add(3, 'months').unix();
|
||||
sinon.stub(stripe.customers, 'retrieve')
|
||||
.resolves({
|
||||
subscriptions: {
|
||||
data: [{id: subscriptionId, current_period_end: currentPeriodEndTimeStamp}], // eslint-disable-line camelcase
|
||||
data: [{
|
||||
id: subscriptionId,
|
||||
current_period_end: currentPeriodEndTimeStamp,
|
||||
}], // eslint-disable-line camelcase
|
||||
},
|
||||
});
|
||||
|
||||
@@ -95,7 +99,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
|
||||
expect(updatedGroup.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(updatedGroup.purchased.plan.customerId).to.eql('customer-id');
|
||||
@@ -126,7 +130,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.within(1.9, 2);
|
||||
});
|
||||
|
||||
@@ -139,7 +143,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
|
||||
});
|
||||
|
||||
@@ -150,7 +154,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
data.groupId = group._id;
|
||||
|
||||
await api.createSubscription(data);
|
||||
let updatedLeader = await User.findById(user._id).exec();
|
||||
const updatedLeader = await User.findById(user._id).exec();
|
||||
|
||||
expect(updatedLeader.purchased.plan.planId).to.eql('group_plan_auto');
|
||||
expect(updatedLeader.purchased.plan.customerId).to.eql('group-plan');
|
||||
@@ -166,7 +170,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('sends an email to member of group who was not a subscriber', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.guilds.push(group._id);
|
||||
await recipient.save();
|
||||
@@ -179,9 +183,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.firstCall.args[2]).to.eql([
|
||||
{name: 'LEADER', content: user.profile.name},
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE},
|
||||
{ name: 'LEADER', content: user.profile.name },
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE },
|
||||
]);
|
||||
// confirm that the other email sent is appropriate:
|
||||
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
|
||||
@@ -189,7 +193,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('sends one email to subscribed member of group, stating subscription is cancelled (Stripe)', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -205,9 +209,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.firstCall.args[2]).to.eql([
|
||||
{name: 'LEADER', content: user.profile.name},
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
|
||||
{ name: 'LEADER', content: user.profile.name },
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL },
|
||||
]);
|
||||
// confirm that the other email sent is not a cancel-subscription email:
|
||||
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
|
||||
@@ -218,11 +222,11 @@ describe('Purchasing a group plan for group', () => {
|
||||
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||
.resolves({
|
||||
BillingAgreementDetails: {
|
||||
BillingAgreementStatus: {State: 'Closed'},
|
||||
BillingAgreementStatus: { State: 'Closed' },
|
||||
},
|
||||
});
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.planId = 'basic_earned';
|
||||
plan.paymentMethod = amzLib.constants.PAYMENT_METHOD;
|
||||
@@ -238,9 +242,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.firstCall.args[2]).to.eql([
|
||||
{name: 'LEADER', content: user.profile.name},
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
|
||||
{ name: 'LEADER', content: user.profile.name },
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL },
|
||||
]);
|
||||
// confirm that the other email sent is not a cancel-subscription email:
|
||||
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
|
||||
@@ -259,7 +263,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
},
|
||||
});
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.planId = 'basic_earned';
|
||||
plan.paymentMethod = paypalPayments.constants.PAYMENT_METHOD;
|
||||
@@ -275,9 +279,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.firstCall.args[0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.firstCall.args[1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.firstCall.args[2]).to.eql([
|
||||
{name: 'LEADER', content: user.profile.name},
|
||||
{name: 'GROUP_NAME', content: group.name},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL},
|
||||
{ name: 'LEADER', content: user.profile.name },
|
||||
{ name: 'GROUP_NAME', content: group.name },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL },
|
||||
]);
|
||||
// confirm that the other email sent is not a cancel-subscription email:
|
||||
expect(sender.sendTxn.secondCall.args[0]._id).to.equal(group.leader);
|
||||
@@ -292,7 +296,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.customerId = 'random';
|
||||
plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -310,9 +314,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.args[1][1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.args[1][2]).to.eql([
|
||||
{name: 'LEADER', content: groupLeaderName},
|
||||
{name: 'GROUP_NAME', content: groupName},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE},
|
||||
{ name: 'LEADER', content: groupLeaderName },
|
||||
{ name: 'GROUP_NAME', content: groupName },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE },
|
||||
]);
|
||||
expect(sender.sendTxn.args[2][0]._id).to.equal(group.leader);
|
||||
expect(sender.sendTxn.args[2][1]).to.equal('group-member-join');
|
||||
@@ -325,7 +329,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.customerId = 'random';
|
||||
plan.paymentMethod = api.constants.IOS_PAYMENT_METHOD;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -343,9 +347,9 @@ describe('Purchasing a group plan for group', () => {
|
||||
expect(sender.sendTxn.args[1][0]._id).to.equal(recipient._id);
|
||||
expect(sender.sendTxn.args[1][1]).to.equal('group-member-join');
|
||||
expect(sender.sendTxn.args[1][2]).to.eql([
|
||||
{name: 'LEADER', content: groupLeaderName},
|
||||
{name: 'GROUP_NAME', content: groupName},
|
||||
{name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS},
|
||||
{ name: 'LEADER', content: groupLeaderName },
|
||||
{ name: 'GROUP_NAME', content: groupName },
|
||||
{ name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS },
|
||||
]);
|
||||
expect(sender.sendTxn.args[2][0]._id).to.equal(group.leader);
|
||||
expect(sender.sendTxn.args[2][1]).to.equal('group-member-join');
|
||||
@@ -354,7 +358,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('adds months to members with existing gift subscription', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -378,7 +382,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql('group-plan');
|
||||
@@ -392,7 +396,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('adds months to members with existing multi-month gift subscription', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -414,7 +418,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql('group-plan');
|
||||
@@ -428,7 +432,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('adds months to members with existing recurring subscription (Stripe)', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -450,11 +454,11 @@ describe('Purchasing a group plan for group', () => {
|
||||
sinon.stub(amzLib, 'getBillingAgreementDetails')
|
||||
.resolves({
|
||||
BillingAgreementDetails: {
|
||||
BillingAgreementStatus: {State: 'Closed'},
|
||||
BillingAgreementStatus: { State: 'Closed' },
|
||||
},
|
||||
});
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.planId = 'basic_earned';
|
||||
plan.paymentMethod = amzLib.constants.PAYMENT_METHOD;
|
||||
@@ -470,7 +474,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5);
|
||||
});
|
||||
@@ -485,7 +489,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
},
|
||||
});
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.planId = 'basic_earned';
|
||||
plan.paymentMethod = paypalPayments.constants.PAYMENT_METHOD;
|
||||
@@ -500,7 +504,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3);
|
||||
paypalPayments.paypalBillingAgreementGet.restore();
|
||||
@@ -511,7 +515,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
it('adds months to members with existing recurring subscription (iOS)');
|
||||
|
||||
it('adds months to members who already cancelled but not yet terminated recurring subscription', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -527,13 +531,13 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3);
|
||||
});
|
||||
|
||||
it('adds months to members who already cancelled but not yet terminated group plan subscription', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = api.constants.GROUP_PLAN_PAYMENT_METHOD;
|
||||
@@ -550,12 +554,12 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(3, 4);
|
||||
});
|
||||
|
||||
it('resets date terminated if user has old subscription', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -570,13 +574,13 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.dateTerminated).to.not.exist;
|
||||
});
|
||||
|
||||
it('adds months to members with existing recurring subscription and includes existing extraMonths', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -591,13 +595,13 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(7, 9);
|
||||
});
|
||||
|
||||
it('adds months to members with existing recurring subscription and ignores existing negative extraMonths', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD;
|
||||
@@ -612,23 +616,23 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3);
|
||||
});
|
||||
|
||||
it('does not override gemsBought, mysteryItems, dateCreated, and consective fields', async () => {
|
||||
let planCreatedDate = moment().toDate();
|
||||
let mysteryItem = {title: 'item'};
|
||||
let mysteryItems = [mysteryItem];
|
||||
let consecutive = {
|
||||
const planCreatedDate = moment().toDate();
|
||||
const mysteryItem = { title: 'item' };
|
||||
const mysteryItems = [mysteryItem];
|
||||
const consecutive = {
|
||||
trinkets: 3,
|
||||
gemCapExtra: 20,
|
||||
offset: 1,
|
||||
count: 13,
|
||||
};
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
|
||||
plan.key = 'basic_earned';
|
||||
@@ -647,7 +651,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.gemsBought).to.equal(3);
|
||||
expect(updatedUser.purchased.plan.mysteryItems[0]).to.eql(mysteryItem);
|
||||
@@ -659,7 +663,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('does not modify a user with a group subscription when they join another group', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
recipient.purchased.plan = plan;
|
||||
@@ -673,10 +677,10 @@ describe('Purchasing a group plan for group', () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
let firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
let extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
const firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
|
||||
let group2 = generateGroup({
|
||||
const group2 = generateGroup({
|
||||
name: 'test group2',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
@@ -703,7 +707,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
});
|
||||
|
||||
it('does not remove a user who is in two groups plans and leaves one', async () => {
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
plan.key = 'basic_earned';
|
||||
recipient.purchased.plan = plan;
|
||||
@@ -717,10 +721,10 @@ describe('Purchasing a group plan for group', () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
let firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
let extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
const firstDateCreated = updatedUser.purchased.plan.dateCreated;
|
||||
const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;
|
||||
|
||||
let group2 = generateGroup({
|
||||
const group2 = generateGroup({
|
||||
name: 'test group2',
|
||||
type: 'guild',
|
||||
privacy: 'public',
|
||||
@@ -733,7 +737,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedGroup = await Group.findById(group._id).exec();
|
||||
const updatedGroup = await Group.findById(group._id).exec();
|
||||
await updatedGroup.leave(recipient);
|
||||
|
||||
updatedUser = await User.findById(recipient._id).exec();
|
||||
@@ -753,7 +757,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.key = 'basic_earned';
|
||||
plan.customerId = api.constants.UNLIMITED_CUSTOMER_ID;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -765,7 +769,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql(api.constants.UNLIMITED_CUSTOMER_ID);
|
||||
@@ -782,7 +786,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.customerId = 'random';
|
||||
plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -794,7 +798,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql('random');
|
||||
@@ -811,7 +815,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.customerId = 'random';
|
||||
plan.paymentMethod = api.constants.IOS_PAYMENT_METHOD;
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -823,7 +827,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql('random');
|
||||
@@ -841,7 +845,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
plan.customerId = api.constants.GROUP_PLAN_CUSTOMER_ID;
|
||||
plan.dateTerminated = moment().add(1, 'months');
|
||||
|
||||
let recipient = new User();
|
||||
const recipient = new User();
|
||||
recipient.profile.name = 'recipient';
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.guilds.push(group._id);
|
||||
@@ -853,7 +857,7 @@ describe('Purchasing a group plan for group', () => {
|
||||
|
||||
await api.createSubscription(data);
|
||||
|
||||
let updatedUser = await User.findById(recipient._id).exec();
|
||||
const updatedUser = await User.findById(recipient._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql(api.constants.GROUP_PLAN_CUSTOMER_ID);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
|
||||
export async function createNonLeaderGroupMember (group) {
|
||||
let nonLeader = new User();
|
||||
export async function createNonLeaderGroupMember (group) { // eslint-disable-line import/prefer-default-export, max-len
|
||||
const nonLeader = new User();
|
||||
nonLeader.guilds.push(group._id);
|
||||
return await nonLeader.save();
|
||||
return nonLeader.save();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import moment from 'moment';
|
||||
|
||||
import * as sender from '../../../../../website/server/libs/email';
|
||||
import * as api from '../../../../../website/server/libs/payments/payments';
|
||||
import analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
import api from '../../../../../website/server/libs/payments/payments';
|
||||
import * as analytics from '../../../../../website/server/libs/analyticsService';
|
||||
import * as notifications from '../../../../../website/server/libs/pushNotifications';
|
||||
import { model as User } from '../../../../../website/server/models/user';
|
||||
import { translate as t } from '../../../../helpers/api-integration/v3';
|
||||
import {
|
||||
generateGroup,
|
||||
} from '../../../../helpers/api-unit.helper.js';
|
||||
} from '../../../../helpers/api-unit.helper';
|
||||
|
||||
describe('payments/index', () => {
|
||||
let user, group, data, plan;
|
||||
let user; let group; let data; let
|
||||
plan;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = new User();
|
||||
@@ -102,7 +103,7 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
it('does not set negative extraMonths if plan has past dateTerminated date', async () => {
|
||||
let dateTerminated = moment().subtract(2, 'months').toDate();
|
||||
const dateTerminated = moment().subtract(2, 'months').toDate();
|
||||
recipient.purchased.plan.dateTerminated = dateTerminated;
|
||||
|
||||
await api.createSubscription(data);
|
||||
@@ -120,7 +121,7 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
it('adds to date terminated for an existing plan with a future terminated date', async () => {
|
||||
let dateTerminated = moment().add(1, 'months').toDate();
|
||||
const dateTerminated = moment().add(1, 'months').toDate();
|
||||
recipient.purchased.plan = plan;
|
||||
recipient.purchased.plan.dateTerminated = dateTerminated;
|
||||
|
||||
@@ -130,7 +131,7 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
it('replaces date terminated for an account with a past terminated date', async () => {
|
||||
let dateTerminated = moment().subtract(1, 'months').toDate();
|
||||
const dateTerminated = moment().subtract(1, 'months').toDate();
|
||||
recipient.purchased.plan.dateTerminated = dateTerminated;
|
||||
|
||||
await api.createSubscription(data);
|
||||
@@ -208,18 +209,21 @@ describe('payments/index', () => {
|
||||
|
||||
it('sends a private message about the gift', async () => {
|
||||
await api.createSubscription(data);
|
||||
let msg = '\`Hello recipient, sender has sent you 3 months of subscription!\`';
|
||||
const msg = '`Hello recipient, sender has sent you 3 months of subscription!`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledOnce;
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: msg, senderMsg: msg, save: false });
|
||||
expect(user.sendMessage).to.be.calledWith(
|
||||
recipient,
|
||||
{ receiverMsg: msg, senderMsg: msg, save: false },
|
||||
);
|
||||
});
|
||||
|
||||
it('sends an email about the gift', async () => {
|
||||
await api.createSubscription(data);
|
||||
|
||||
expect(sender.sendTxn).to.be.calledWith(recipient, 'gifted-subscription', [
|
||||
{name: 'GIFTER', content: 'sender'},
|
||||
{name: 'X_MONTHS_SUBSCRIPTION', content: 3},
|
||||
{ name: 'GIFTER', content: 'sender' },
|
||||
{ name: 'X_MONTHS_SUBSCRIPTION', content: 3 },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -416,8 +420,8 @@ describe('payments/index', () => {
|
||||
|
||||
context('Mystery Items', () => {
|
||||
it('awards mystery items when within the timeframe for a mystery item', async () => {
|
||||
let mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
let fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
|
||||
@@ -437,7 +441,7 @@ describe('payments/index', () => {
|
||||
|
||||
it('does not awards mystery items when not within the timeframe for a mystery item', async () => {
|
||||
const noMysteryItemTimeframe = 1462183920000; // May 2nd 2016
|
||||
let fakeClock = sinon.useFakeTimers(noMysteryItemTimeframe);
|
||||
const fakeClock = sinon.useFakeTimers(noMysteryItemTimeframe);
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
|
||||
await api.createSubscription(data);
|
||||
@@ -449,7 +453,7 @@ describe('payments/index', () => {
|
||||
|
||||
it('does not add a notification for mystery items if none was awarded', async () => {
|
||||
const noMysteryItemTimeframe = 1462183920000; // May 2nd 2016
|
||||
let fakeClock = sinon.useFakeTimers(noMysteryItemTimeframe);
|
||||
const fakeClock = sinon.useFakeTimers(noMysteryItemTimeframe);
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
|
||||
await api.createSubscription(data);
|
||||
@@ -461,9 +465,9 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already owns the item', async () => {
|
||||
let mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
let fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
let mayMysteryItem = 'armor_mystery_201605';
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.items.gear.owned[mayMysteryItem] = true;
|
||||
|
||||
data = { paymentMethod: 'PaymentMethod', user, sub: { key: 'basic_3mo' } };
|
||||
@@ -477,9 +481,9 @@ describe('payments/index', () => {
|
||||
});
|
||||
|
||||
it('does not award mystery item when user already has the item in the mystery box', async () => {
|
||||
let mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
let fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
let mayMysteryItem = 'armor_mystery_201605';
|
||||
const mayMysteryItemTimeframe = 1464725113000; // May 31st 2016
|
||||
const fakeClock = sinon.useFakeTimers(mayMysteryItemTimeframe);
|
||||
const mayMysteryItem = 'armor_mystery_201605';
|
||||
user.purchased.plan.mysteryItems = [mayMysteryItem];
|
||||
|
||||
sandbox.spy(user.purchased.plan.mysteryItems, 'push');
|
||||
@@ -504,8 +508,8 @@ describe('payments/index', () => {
|
||||
it('adds a month termination date by default', async () => {
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
|
||||
});
|
||||
@@ -515,8 +519,8 @@ describe('payments/index', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
|
||||
});
|
||||
@@ -526,8 +530,8 @@ describe('payments/index', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
|
||||
});
|
||||
@@ -537,8 +541,8 @@ describe('payments/index', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
@@ -549,8 +553,8 @@ describe('payments/index', () => {
|
||||
|
||||
await api.cancelSubscription(data);
|
||||
|
||||
let now = new Date();
|
||||
let daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
const now = new Date();
|
||||
const daysTillTermination = moment(user.purchased.plan.dateTerminated).diff(now, 'days');
|
||||
|
||||
expect(daysTillTermination).to.be.within(13, 15);
|
||||
});
|
||||
@@ -641,9 +645,10 @@ describe('payments/index', () => {
|
||||
|
||||
it('sends a message from purchaser to recipient', async () => {
|
||||
await api.buyGems(data);
|
||||
let msg = '\`Hello recipient, sender has sent you 4 gems!\`';
|
||||
const msg = '`Hello recipient, sender has sent you 4 gems!`';
|
||||
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: msg, senderMsg: msg, save: false });
|
||||
expect(user.sendMessage).to.be
|
||||
.calledWith(recipient, { receiverMsg: msg, senderMsg: msg, save: false });
|
||||
});
|
||||
|
||||
it('sends a message from purchaser to recipient wtih custom message', async () => {
|
||||
@@ -652,7 +657,8 @@ describe('payments/index', () => {
|
||||
await api.buyGems(data);
|
||||
|
||||
const msg = `\`Hello recipient, sender has sent you 4 gems!\` ${data.gift.message}`;
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: msg, senderMsg: msg, save: false });
|
||||
expect(user.sendMessage).to.be
|
||||
.calledWith(recipient, { receiverMsg: msg, senderMsg: msg, save: false });
|
||||
});
|
||||
|
||||
it('sends a push notification if user did not gift to self', async () => {
|
||||
@@ -671,8 +677,8 @@ describe('payments/index', () => {
|
||||
});
|
||||
await api.buyGems(data);
|
||||
|
||||
let [recipientsMessageContent, sendersMessageContent] = ['en', 'en'].map((lang) => {
|
||||
let messageContent = t('giftedGemsFull', {
|
||||
const [recipientsMessageContent, sendersMessageContent] = ['en', 'en'].map(lang => {
|
||||
const messageContent = t('giftedGemsFull', {
|
||||
username: recipient.profile.name,
|
||||
sender: user.profile.name,
|
||||
gemAmount: data.gift.gems.amount,
|
||||
@@ -681,7 +687,10 @@ describe('payments/index', () => {
|
||||
return `\`${messageContent}\``;
|
||||
});
|
||||
|
||||
expect(user.sendMessage).to.be.calledWith(recipient, { receiverMsg: recipientsMessageContent, senderMsg: sendersMessageContent, save: false });
|
||||
expect(user.sendMessage).to.be.calledWith(
|
||||
recipient,
|
||||
{ receiverMsg: recipientsMessageContent, senderMsg: sendersMessageContent, save: false },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -693,7 +702,7 @@ describe('payments/index', () => {
|
||||
|
||||
await api.addSubToGroupUser(user, group);
|
||||
|
||||
let updatedUser = await User.findById(user._id).exec();
|
||||
const updatedUser = await User.findById(user._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
|
||||
expect(updatedUser.purchased.plan.customerId).to.eql('group-plan');
|
||||
@@ -709,17 +718,17 @@ describe('payments/index', () => {
|
||||
it('awards the Royal Purple Jackalope pet', async () => {
|
||||
await api.addSubToGroupUser(user, group);
|
||||
|
||||
let updatedUser = await User.findById(user._id).exec();
|
||||
const updatedUser = await User.findById(user._id).exec();
|
||||
|
||||
expect(updatedUser.items.pets['Jackalope-RoyalPurple']).to.eql(5);
|
||||
});
|
||||
|
||||
it('saves previously unused Mystery Items and Hourglasses for an expired subscription', async () => {
|
||||
let planExpirationDate = new Date();
|
||||
const planExpirationDate = new Date();
|
||||
planExpirationDate.setDate(planExpirationDate.getDate() - 2);
|
||||
let mysteryItem = 'item';
|
||||
let mysteryItems = [mysteryItem];
|
||||
let consecutive = {
|
||||
const mysteryItem = 'item';
|
||||
const mysteryItems = [mysteryItem];
|
||||
const consecutive = {
|
||||
trinkets: 3,
|
||||
};
|
||||
|
||||
@@ -735,7 +744,7 @@ describe('payments/index', () => {
|
||||
await user.save();
|
||||
await api.addSubToGroupUser(user, group);
|
||||
|
||||
let updatedUser = await User.findById(user._id).exec();
|
||||
const updatedUser = await User.findById(user._id).exec();
|
||||
|
||||
expect(updatedUser.purchased.plan.mysteryItems[0]).to.eql(mysteryItem);
|
||||
expect(updatedUser.purchased.plan.consecutive.trinkets).to.equal(consecutive.trinkets);
|
||||
|
||||
@@ -5,8 +5,10 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
|
||||
describe('checkout success', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
let user, gift, customerId, paymentId;
|
||||
let paypalPaymentExecuteStub, paymentBuyGemsStub, paymentsCreateSubscritionStub;
|
||||
let user; let gift; let customerId; let
|
||||
paymentId;
|
||||
let paypalPaymentExecuteStub; let paymentBuyGemsStub; let
|
||||
paymentsCreateSubscritionStub;
|
||||
|
||||
beforeEach(() => {
|
||||
user = new User();
|
||||
@@ -25,7 +27,9 @@ describe('checkout success', () => {
|
||||
});
|
||||
|
||||
it('purchases gems', async () => {
|
||||
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||
await paypalPayments.checkoutSuccess({
|
||||
user, gift, paymentId, customerId,
|
||||
});
|
||||
|
||||
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||
@@ -38,7 +42,7 @@ describe('checkout success', () => {
|
||||
});
|
||||
|
||||
it('gifts gems', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
gift = {
|
||||
type: 'gems',
|
||||
@@ -48,7 +52,9 @@ describe('checkout success', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||
await paypalPayments.checkoutSuccess({
|
||||
user, gift, paymentId, customerId,
|
||||
});
|
||||
|
||||
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||
@@ -62,7 +68,7 @@ describe('checkout success', () => {
|
||||
});
|
||||
|
||||
it('gifts subscription', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
gift = {
|
||||
type: 'subscription',
|
||||
@@ -72,7 +78,9 @@ describe('checkout success', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await paypalPayments.checkoutSuccess({user, gift, paymentId, customerId});
|
||||
await paypalPayments.checkoutSuccess({
|
||||
user, gift, paymentId, customerId,
|
||||
});
|
||||
|
||||
expect(paypalPaymentExecuteStub).to.be.calledOnce;
|
||||
expect(paypalPaymentExecuteStub).to.be.calledWith(paymentId, { payer_id: customerId });
|
||||
|
||||
@@ -6,7 +6,7 @@ import { model as User } from '../../../../../../website/server/models/user';
|
||||
import common from '../../../../../../website/common';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
const i18n = common.i18n;
|
||||
const { i18n } = common;
|
||||
|
||||
describe('checkout', () => {
|
||||
const subKey = 'basic_3mo';
|
||||
@@ -53,7 +53,7 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('creates a link for gem purchases', async () => {
|
||||
let link = await paypalPayments.checkout({user: new User()});
|
||||
const link = await paypalPayments.checkout({ user: new User() });
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems', 5.00));
|
||||
@@ -61,9 +61,9 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('should error if gem amount is too low', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'gems',
|
||||
gems: {
|
||||
amount: 0,
|
||||
@@ -71,7 +71,7 @@ describe('checkout', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await expect(paypalPayments.checkout({gift}))
|
||||
await expect(paypalPayments.checkout({ gift }))
|
||||
.to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 400,
|
||||
message: 'Amount must be at least 1.',
|
||||
@@ -80,10 +80,10 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('should error if the user cannot get gems', async () => {
|
||||
let user = new User();
|
||||
const user = new User();
|
||||
sinon.stub(user, 'canGetGems').resolves(false);
|
||||
|
||||
await expect(paypalPayments.checkout({user})).to.eventually.be.rejected.and.to.eql({
|
||||
await expect(paypalPayments.checkout({ user })).to.eventually.be.rejected.and.to.eql({
|
||||
httpCode: 401,
|
||||
message: i18n.t('groupPolicyCannotGetGems'),
|
||||
name: 'NotAuthorized',
|
||||
@@ -91,9 +91,9 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('creates a link for gifting gems', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
await receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'gems',
|
||||
uuid: receivingUser._id,
|
||||
gems: {
|
||||
@@ -101,7 +101,7 @@ describe('checkout', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let link = await paypalPayments.checkout({gift});
|
||||
const link = await paypalPayments.checkout({ gift });
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('Habitica Gems (Gift)', '4.00'));
|
||||
@@ -109,9 +109,9 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('creates a link for gifting a subscription', async () => {
|
||||
let receivingUser = new User();
|
||||
const receivingUser = new User();
|
||||
receivingUser.save();
|
||||
let gift = {
|
||||
const gift = {
|
||||
type: 'subscription',
|
||||
subscription: {
|
||||
key: subKey,
|
||||
@@ -119,7 +119,7 @@ describe('checkout', () => {
|
||||
},
|
||||
};
|
||||
|
||||
let link = await paypalPayments.checkout({gift});
|
||||
const link = await paypalPayments.checkout({ gift });
|
||||
|
||||
expect(paypalPaymentCreateStub).to.be.calledOnce;
|
||||
expect(paypalPaymentCreateStub).to.be.calledWith(getPaypalCreateOptions('mo. Habitica Subscription (Gift)', '15.00'));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user