@@ -7,18 +7,19 @@ import { saveUser, generateToken, userResponse } from '../../helpers';
77import { createMockUser } from '../../__testUtils__' ;
88
99import { mailerService } from '../../../../utils/mail' ;
10- import { UserDocument } from '../../../../types' ;
10+ import { UpdateSettingsRequestBody , UserDocument } from '../../../../types' ;
1111
1212jest . mock ( '../../../../models/user' ) ;
1313jest . mock ( '../../../../utils/mail' ) ;
1414jest . mock ( '../../helpers' , ( ) => ( {
15- ...jest . requireActual ( '../../helpers' ) ,
15+ ...jest . requireActual ( '../../helpers' ) , // use actual userResponse
1616 saveUser : jest . fn ( ) ,
1717 generateToken : jest . fn ( )
1818} ) ) ;
1919
20- describe ( 'user.controller > auth management' , ( ) => {
20+ describe ( 'user.controller > auth management > updateSettings (email, username, password) ' , ( ) => {
2121 let request : any ;
22+ let requestBody : UpdateSettingsRequestBody ;
2223 let response : any ;
2324 let next : MockNext ;
2425 const fixedTime = 100000000 ;
@@ -69,13 +70,31 @@ describe('user.controller > auth management', () => {
6970
7071 // the below tests match the current logic, but logic can be improved
7172 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+
7282 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' ,
7687 comparePassword : jest . fn ( ) . mockResolvedValue ( true )
7788 } ) ;
7889
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+
7998 beforeEach ( ( ) => {
8099 User . findById = jest . fn ( ) . mockResolvedValue ( startingUser ) ;
81100
@@ -85,40 +104,145 @@ describe('user.controller > auth management', () => {
85104 ( generateToken as jest . Mock ) . mockResolvedValue ( 'token12343' ) ;
86105 } ) ;
87106
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
94121 } ) ;
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
100132 } ) ;
101133 } ) ;
102134
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+ } ) ;
106141 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+ } ) ;
109155 } ) ;
110156 } ) ;
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
111160 describe ( 'and when there is a currentPassword in the request' , ( ) => {
112161 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+ } ) ;
116185 } ) ;
117186 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+ } ) ;
120229 } ) ;
121230 } ) ;
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+ } ) ;
122246 } ) ;
123247 } ) ;
124248} ) ;
0 commit comments