@@ -7,18 +7,19 @@ import { saveUser, generateToken, userResponse } from '../../helpers';
7
7
import { createMockUser } from '../../__testUtils__' ;
8
8
9
9
import { mailerService } from '../../../../utils/mail' ;
10
- import { UserDocument } from '../../../../types' ;
10
+ import { UpdateSettingsRequestBody , UserDocument } from '../../../../types' ;
11
11
12
12
jest . mock ( '../../../../models/user' ) ;
13
13
jest . mock ( '../../../../utils/mail' ) ;
14
14
jest . mock ( '../../helpers' , ( ) => ( {
15
- ...jest . requireActual ( '../../helpers' ) ,
15
+ ...jest . requireActual ( '../../helpers' ) , // use actual userResponse
16
16
saveUser : jest . fn ( ) ,
17
17
generateToken : jest . fn ( )
18
18
} ) ) ;
19
19
20
- describe ( 'user.controller > auth management' , ( ) => {
20
+ describe ( 'user.controller > auth management > updateSettings (email, username, password) ' , ( ) => {
21
21
let request : any ;
22
+ let requestBody : UpdateSettingsRequestBody ;
22
23
let response : any ;
23
24
let next : MockNext ;
24
25
const fixedTime = 100000000 ;
@@ -69,13 +70,31 @@ describe('user.controller > auth management', () => {
69
70
70
71
// the below tests match the current logic, but logic can be improved
71
72
describe ( 'if the user is found' , ( ) => {
73
+ const OLD_USERNAME = 'oldusername' ;
74
+ const NEW_USERNAME = 'newusername' ;
75
+
76
+ const OLD_EMAIL = '[email protected] ' ;
77
+ const NEW_EMAIL = '[email protected] ' ;
78
+
79
+ const OLD_PASSWORD = 'oldpassword' ;
80
+ const NEW_PASSWORD = 'newpassword' ;
81
+
72
82
const startingUser = createMockUser ( {
73
- username : 'oldusername' ,
74
-
75
- id : 'valid-id' ,
83
+ username : OLD_USERNAME ,
84
+ email : OLD_EMAIL ,
85
+ password : OLD_PASSWORD ,
86
+ id : '123459' ,
76
87
comparePassword : jest . fn ( ) . mockResolvedValue ( true )
77
88
} ) ;
78
89
90
+ // minimum valid request body to manipulate per test
91
+ // from manual testing on the account form:
92
+ // both username and email are required & there is client-side validation for valid email & username-taken prior to submit
93
+ const minimumValidRequest : UpdateSettingsRequestBody = {
94
+ username : OLD_USERNAME ,
95
+ email : OLD_EMAIL
96
+ } ;
97
+
79
98
beforeEach ( ( ) => {
80
99
User . findById = jest . fn ( ) . mockResolvedValue ( startingUser ) ;
81
100
@@ -85,40 +104,145 @@ describe('user.controller > auth management', () => {
85
104
( generateToken as jest . Mock ) . mockResolvedValue ( 'token12343' ) ;
86
105
} ) ;
87
106
88
- describe ( 'and when there is a username in the request' , ( ) => {
89
- beforeEach ( async ( ) => {
90
- request . setBody ( {
91
- username : 'newusername'
92
- } ) ;
93
- await updateSettings ( request , response , next ) ;
107
+ // Q: should we add check & logic that if no username or email are on the request,
108
+ // we fallback to the username and/or email on the found user for safety?
109
+ // not sure if anyone is hitting this api directly, so the client-side checks may not be enough
110
+
111
+ // duplicate username check happens client-side before this request is made
112
+ it ( 'saves the user with any username in the request' , async ( ) => {
113
+ // saves with old username
114
+ requestBody = { ...minimumValidRequest , username : OLD_USERNAME } ;
115
+ request . setBody ( requestBody ) ;
116
+
117
+ await updateSettings ( request , response , next ) ;
118
+
119
+ expect ( saveUser ) . toHaveBeenCalledWith ( response , {
120
+ ...startingUser
94
121
} ) ;
95
- it ( 'calls saveUser' , ( ) => {
96
- expect ( saveUser ) . toHaveBeenCalledWith ( response , {
97
- ...startingUser ,
98
- username : 'newusername'
99
- } ) ;
122
+
123
+ // saves with new username
124
+ requestBody = { ...minimumValidRequest , username : NEW_USERNAME } ;
125
+ request . setBody ( requestBody ) ;
126
+
127
+ await updateSettings ( request , response , next ) ;
128
+
129
+ expect ( saveUser ) . toHaveBeenCalledWith ( response , {
130
+ ...startingUser ,
131
+ username : NEW_USERNAME
100
132
} ) ;
101
133
} ) ;
102
134
103
- // currently frontend doesn't seem to call the below
104
- describe ( 'and when there is a newPassword in the request' , ( ) => {
105
- beforeEach ( async ( ) => { } ) ;
135
+ // currently frontend doesn't seem to call password-change related things the below
136
+ // not sure if we should update the logic to be cleaner?
137
+ describe ( 'and when there is a new password in the request' , ( ) => {
138
+ beforeEach ( ( ) => {
139
+ requestBody = { ...minimumValidRequest , newPassword : NEW_PASSWORD } ;
140
+ } ) ;
106
141
describe ( 'and the current password is not provided' , ( ) => {
107
- it ( 'returns 401 with a "current password not provided" message' , ( ) => { } ) ;
108
- it ( 'does not save the user with the new password' , ( ) => { } ) ;
142
+ beforeEach ( async ( ) => {
143
+ request . setBody ( requestBody ) ;
144
+ await updateSettings ( request , response , next ) ;
145
+ } ) ;
146
+ it ( 'returns 401 with a "current password not provided" message' , ( ) => {
147
+ expect ( response . status ) . toHaveBeenCalledWith ( 401 ) ;
148
+ expect ( response . json ) . toHaveBeenCalledWith ( {
149
+ error : 'Current password is not provided.'
150
+ } ) ;
151
+ } ) ;
152
+ it ( 'does not save the user with the new password' , ( ) => {
153
+ expect ( saveUser ) . not . toHaveBeenCalled ( ) ;
154
+ } ) ;
109
155
} ) ;
110
156
} ) ;
157
+
158
+ // this should be nested in the previous block but currently here to match existing logic as-is
159
+ // NOTE: will make a PR into this branch to propose the change
111
160
describe ( 'and when there is a currentPassword in the request' , ( ) => {
112
161
describe ( 'and the current password does not match' , ( ) => {
113
- beforeEach ( async ( ) => { } ) ;
114
- it ( 'returns 401 with a "current password invalid" message' , ( ) => { } ) ;
115
- it ( 'does not save the user with the new password' , ( ) => { } ) ;
162
+ beforeEach ( async ( ) => {
163
+ requestBody = {
164
+ ...minimumValidRequest ,
165
+ newPassword : NEW_PASSWORD ,
166
+ currentPassword : 'SOMETHING ELSE'
167
+ } ;
168
+
169
+ // simulate check for password match failing
170
+ startingUser . comparePassword = jest . fn ( ) . mockResolvedValue ( false ) ;
171
+
172
+ request . setBody ( requestBody ) ;
173
+ await updateSettings ( request , response , next ) ;
174
+ } ) ;
175
+
176
+ it ( 'returns 401 with a "current password invalid" message' , ( ) => {
177
+ expect ( response . status ) . toHaveBeenCalledWith ( 401 ) ;
178
+ expect ( response . json ) . toHaveBeenCalledWith ( {
179
+ error : 'Current password is invalid.'
180
+ } ) ;
181
+ } ) ;
182
+ it ( 'does not save the user with the new password' , ( ) => {
183
+ expect ( saveUser ) . not . toHaveBeenCalled ( ) ;
184
+ } ) ;
116
185
} ) ;
117
186
describe ( 'and when the current password does match' , ( ) => {
118
- beforeEach ( async ( ) => { } ) ;
119
- it ( 'calls saveUser with the new password' , ( ) => { } ) ;
187
+ beforeEach ( async ( ) => {
188
+ requestBody = {
189
+ ...minimumValidRequest ,
190
+ newPassword : NEW_PASSWORD ,
191
+ currentPassword : OLD_PASSWORD
192
+ } ;
193
+
194
+ // simulate check for password match passing
195
+ startingUser . comparePassword = jest . fn ( ) . mockResolvedValue ( true ) ;
196
+
197
+ request . setBody ( requestBody ) ;
198
+ await updateSettings ( request , response , next ) ;
199
+ } ) ;
200
+ it ( 'calls saveUser with the new password' , ( ) => {
201
+ expect ( saveUser ) . toHaveBeenCalledWith ( response , {
202
+ ...startingUser ,
203
+ password : NEW_PASSWORD
204
+ } ) ;
205
+ } ) ;
206
+ } ) ;
207
+
208
+ // NOTE: This should not pass, but it currently does!!
209
+ describe ( 'and when there is no new password on the request' , ( ) => {
210
+ beforeEach ( async ( ) => {
211
+ requestBody = {
212
+ ...minimumValidRequest ,
213
+ newPassword : undefined ,
214
+ currentPassword : OLD_PASSWORD
215
+ } ;
216
+
217
+ // simulate check for password match passing
218
+ startingUser . comparePassword = jest . fn ( ) . mockResolvedValue ( true ) ;
219
+
220
+ request . setBody ( requestBody ) ;
221
+ await updateSettings ( request , response , next ) ;
222
+ } ) ;
223
+ it ( 'calls saveUser with the new empty password' , ( ) => {
224
+ expect ( saveUser ) . toHaveBeenCalledWith ( response , {
225
+ ...startingUser ,
226
+ password : undefined
227
+ } ) ;
228
+ } ) ;
120
229
} ) ;
121
230
} ) ;
231
+
232
+ describe ( 'and when there is an email in the request' , ( ) => {
233
+ describe ( 'and the email is the same as the old email' , ( ) => {
234
+ it ( 'saves the user with the old email' , ( ) => { } ) ;
235
+ it ( 'does not send a verification email' , ( ) => { } ) ;
236
+ } ) ;
237
+ describe ( 'and the email is not the same as the old email' , ( ) => {
238
+ it ( 'saves the user with the new email' , ( ) => { } ) ;
239
+ it ( 'sends a verification email' , ( ) => { } ) ;
240
+ } ) ;
241
+ } ) ;
242
+
243
+ describe ( 'and when there is any other error' , ( ) => {
244
+ it ( 'returns a 500 error' , ( ) => { } ) ;
245
+ } ) ;
122
246
} ) ;
123
247
} ) ;
124
248
} ) ;
0 commit comments