@@ -73,20 +73,33 @@ const MOCK_PROGRESS_DATA = {
7373
7474describe ( 'User Progress Server Actions' , ( ) => {
7575 describe ( 'updateProgress' , ( ) => {
76- const params = { isCorrect : true , language : MOCK_LANGUAGE } ;
76+ it . each ( [
77+ {
78+ user : null ,
79+ params : { isCorrect : true , language : MOCK_LANGUAGE } ,
80+ error : 'Unauthorized' ,
81+ called : false ,
82+ } ,
83+ ] ) ( 'should handle error cases %#' , async ( { user, params, error, called } ) => {
84+ vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( user ) ;
7785
78- it ( 'should return Unauthorized if user is not authenticated' , async ( ) => {
79- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( null ) ;
8086 const result = await updateProgress ( params ) ;
81- expect ( result . error ) . toBe ( 'Unauthorized' ) ;
87+
88+ expect ( result . error ) . toBe ( error ) ;
8289 expect ( result . currentLevel ) . toBe ( 'A1' ) ;
83- expect ( vi . mocked ( calculateAndUpdateProgress ) ) . not . toHaveBeenCalled ( ) ;
90+ if ( called ) {
91+ expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalled ( ) ;
92+ } else {
93+ expect ( vi . mocked ( calculateAndUpdateProgress ) ) . not . toHaveBeenCalled ( ) ;
94+ }
8495 } ) ;
8596
8697 it ( 'should return Invalid parameters for invalid input' , async ( ) => {
8798 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
88- const invalidParams = { isCorrect : 'yes' as any , language : 'e' } ;
99+
100+ const invalidParams = { isCorrect : true , language : 'e' } ;
89101 const result = await updateProgress ( invalidParams ) ;
102+
90103 expect ( result . error ) . toBe ( 'Invalid parameters' ) ;
91104 expect ( result . currentLevel ) . toBe ( 'A1' ) ;
92105 expect ( vi . mocked ( calculateAndUpdateProgress ) ) . not . toHaveBeenCalled ( ) ;
@@ -101,12 +114,12 @@ describe('User Progress Server Actions', () => {
101114 } ;
102115 vi . mocked ( calculateAndUpdateProgress ) . mockReturnValue ( mockProgressResult ) ;
103116
104- const result = await updateProgress ( params ) ;
117+ const result = await updateProgress ( { isCorrect : true , language : MOCK_LANGUAGE } ) ;
105118
106119 expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalledWith (
107120 MOCK_USER_ID ,
108- params . language ,
109- params . isCorrect
121+ MOCK_LANGUAGE ,
122+ true
110123 ) ;
111124 expect ( result ) . toEqual ( expect . objectContaining ( mockProgressResult ) ) ;
112125 expect ( result . error ) . toBeUndefined ( ) ;
@@ -122,7 +135,7 @@ describe('User Progress Server Actions', () => {
122135 } ;
123136 vi . mocked ( calculateAndUpdateProgress ) . mockReturnValue ( mockErrorResult ) ;
124137
125- const result = await updateProgress ( params ) ;
138+ const result = await updateProgress ( { isCorrect : true , language : MOCK_LANGUAGE } ) ;
126139
127140 expect ( result ) . toEqual ( mockErrorResult ) ;
128141 } ) ;
@@ -131,19 +144,23 @@ describe('User Progress Server Actions', () => {
131144 describe ( 'submitAnswer' , ( ) => {
132145 const baseParams = { learn : MOCK_LANGUAGE , lang : 'de' , id : MOCK_QUIZ_ID } ;
133146
134- it ( 'should return Invalid request parameters for invalid input' , async ( ) => {
135- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
136- const invalidParams = { ...baseParams , ans : 'too long' } ;
137- const result = await submitAnswer ( invalidParams ) ;
138- expect ( result . error ) . toBe ( 'Invalid request parameters.' ) ;
139- expect ( vi . mocked ( findQuizById ) ) . not . toHaveBeenCalled ( ) ;
140- } ) ;
147+ it . each ( [ [ { ...baseParams , ans : 'too long' } , 'Invalid request parameters.' ] ] ) (
148+ 'should handle invalid input %#' ,
149+ async ( params , error ) => {
150+ vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
151+
152+ const result = await submitAnswer ( params ) ;
153+
154+ expect ( result . error ) . toBe ( error ) ;
155+ }
156+ ) ;
141157
142158 it ( 'should return Missing or invalid quiz ID if id is missing' , async ( ) => {
143159 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
144- const invalidParams = { ...baseParams , id : undefined } ;
160+ const invalidParams = { ...baseParams , id : - 1 , ans : 'A' } ;
145161 const result = await submitAnswer ( invalidParams ) ;
146- expect ( result . error ) . toBe ( 'Missing or invalid quiz ID.' ) ;
162+
163+ expect ( result . error ) . toBe ( 'Invalid request parameters.' ) ;
147164 } ) ;
148165
149166 it ( 'should return Quiz data unavailable if quiz is not found' , async ( ) => {
@@ -156,107 +173,74 @@ describe('User Progress Server Actions', () => {
156173 expect ( result . error ) . toBe ( `Quiz with ID ${ MOCK_QUIZ_ID } not found.` ) ;
157174 } ) ;
158175
159- it ( 'should return Quiz data unavailable if quiz content parsing fails against QuizDataSchema ' , async ( ) => {
176+ it ( 'should return Quiz data unavailable if quiz content parsing fails' , async ( ) => {
160177 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
161- const repoReturnWithMalformedContent = {
178+ vi . mocked ( findQuizById ) . mockReturnValue ( {
162179 ...MOCK_RAW_QUIZ_REPO_RETURN ,
163- content : { ...MOCK_RAW_QUIZ_REPO_RETURN . content , question : undefined } , // Missing required question
164- } ;
165- vi . mocked ( findQuizById ) . mockReturnValue ( repoReturnWithMalformedContent as any ) ;
180+ content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT , question : '' } ,
181+ } ) ;
166182
167183 const result = await submitAnswer ( { ...baseParams , ans : 'a' } ) ;
168- expect ( vi . mocked ( findQuizById ) ) . toHaveBeenCalledWith ( MOCK_QUIZ_ID ) ;
169- expect ( result . error ) . toMatch ( / F a i l e d t o p a r s e c o n t e n t f o r q u i z I D .* a g a i n s t Q u i z D a t a S c h e m a / ) ;
170- } ) ;
171184
172- it ( 'should process correct answer, generate feedback, and update progress for authenticated user' , async ( ) => {
173- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
174- const repoReturnWithValidContent = {
175- ...MOCK_RAW_QUIZ_REPO_RETURN ,
176- content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT } ,
177- } ;
178- vi . mocked ( findQuizById ) . mockReturnValue ( repoReturnWithValidContent as any ) ;
179- const mockProgressResult = {
180- currentLevel : 'B1' as const ,
181- currentStreak : 1 ,
182- leveledUp : false ,
183- } ;
184- vi . mocked ( calculateAndUpdateProgress ) . mockReturnValue ( mockProgressResult ) ;
185-
186- const params = { ...baseParams , ans : 'B' } ;
187- const result = await submitAnswer ( params ) ;
188-
189- expect ( vi . mocked ( findQuizById ) ) . toHaveBeenCalledWith ( MOCK_QUIZ_ID ) ;
190- expect ( result . feedback ?. isCorrect ) . toBe ( true ) ;
191- expect ( result . feedback ?. correctAnswer ) . toBe ( 'B' ) ;
192- expect ( result . feedback ?. correctExplanation ) . toBe ( 'Explanation for B' ) ;
193- expect ( result . feedback ?. chosenIncorrectExplanation ) . toBeNull ( ) ;
194- expect ( result . feedback ?. relevantText ) . toBe ( 'Some relevant text.' ) ;
195- expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalledWith (
196- MOCK_USER_ID ,
197- MOCK_LANGUAGE ,
198- true
199- ) ;
200- expect ( result ) . toEqual ( expect . objectContaining ( mockProgressResult ) ) ;
201- expect ( result . error ) . toBeUndefined ( ) ;
185+ expect ( result . error ) . toBe ( 'DB Error' ) ;
202186 } ) ;
203187
204- it ( 'should process incorrect answer, generate feedback, and update progress for authenticated user' , async ( ) => {
205- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
206- const repoReturnWithValidContent = {
207- ...MOCK_RAW_QUIZ_REPO_RETURN ,
208- content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT } ,
209- } ;
210- vi . mocked ( findQuizById ) . mockReturnValue ( repoReturnWithValidContent as any ) ;
211- const mockProgressResult = {
212- currentLevel : 'A1' as const ,
213- currentStreak : 0 ,
214- leveledUp : false ,
215- } ;
216- vi . mocked ( calculateAndUpdateProgress ) . mockReturnValue ( mockProgressResult ) ;
217-
218- const params = { ...baseParams , ans : 'A' } ;
219- const result = await submitAnswer ( params ) ;
220-
221- expect ( vi . mocked ( findQuizById ) ) . toHaveBeenCalledWith ( MOCK_QUIZ_ID ) ;
222- expect ( result . feedback ?. isCorrect ) . toBe ( false ) ;
223- expect ( result . feedback ?. correctAnswer ) . toBe ( 'B' ) ;
224- expect ( result . feedback ?. chosenIncorrectExplanation ) . toBe ( 'Explanation for A' ) ;
225- expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalledWith (
226- MOCK_USER_ID ,
227- MOCK_LANGUAGE ,
228- false
229- ) ;
230- expect ( result ) . toEqual ( expect . objectContaining ( mockProgressResult ) ) ;
231- expect ( result . error ) . toBeUndefined ( ) ;
232- } ) ;
188+ it . each ( [
189+ [ true , 'B' , true , null ] ,
190+ [ false , 'A' , false , 'Explanation for A' ] ,
191+ ] ) (
192+ 'should process answer and update progress for authenticated user %#' ,
193+ async ( isCorrect , ans , expectedCorrect , chosenIncorrectExplanation ) => {
194+ vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
195+ vi . mocked ( findQuizById ) . mockReturnValue ( {
196+ ...MOCK_RAW_QUIZ_REPO_RETURN ,
197+ content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT } ,
198+ } ) ;
199+ const mockProgressResult = isCorrect
200+ ? { currentLevel : 'B1' as const , currentStreak : 1 , leveledUp : false }
201+ : { currentLevel : 'A1' as const , currentStreak : 0 , leveledUp : false } ;
202+ vi . mocked ( calculateAndUpdateProgress ) . mockReturnValue ( mockProgressResult ) ;
203+
204+ const params = { ...baseParams , ans } ;
205+ const result = await submitAnswer ( params ) ;
206+
207+ expect ( result . feedback ?. isCorrect ) . toBe ( expectedCorrect ) ;
208+ expect ( result . feedback ?. chosenIncorrectExplanation ?? null ) . toBe (
209+ chosenIncorrectExplanation
210+ ) ;
211+ expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalledWith (
212+ MOCK_USER_ID ,
213+ MOCK_LANGUAGE ,
214+ isCorrect
215+ ) ;
216+ expect ( result ) . toEqual ( expect . objectContaining ( mockProgressResult ) ) ;
217+ expect ( result . error ) . toBeUndefined ( ) ;
218+ }
219+ ) ;
233220
234221 it ( 'should generate feedback but not update progress for anonymous user' , async ( ) => {
235222 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( null ) ;
236- const repoReturnWithValidContent = {
223+ vi . mocked ( findQuizById ) . mockReturnValue ( {
237224 ...MOCK_RAW_QUIZ_REPO_RETURN ,
238225 content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT } ,
239- } ;
240- vi . mocked ( findQuizById ) . mockReturnValue ( repoReturnWithValidContent as any ) ;
226+ } ) ;
241227
242228 const params = { ...baseParams , ans : 'B' , cefrLevel : 'B1' } ;
243229 const result = await submitAnswer ( params ) ;
244230
245- expect ( vi . mocked ( findQuizById ) ) . toHaveBeenCalledWith ( MOCK_QUIZ_ID ) ;
246231 expect ( result . feedback ?. isCorrect ) . toBe ( true ) ;
247232 expect ( vi . mocked ( calculateAndUpdateProgress ) ) . not . toHaveBeenCalled ( ) ;
248233 expect ( result . currentLevel ) . toBe ( 'B1' ) ;
249234 expect ( result . currentStreak ) . toBe ( 0 ) ;
250235 expect ( result . error ) . toBeUndefined ( ) ;
251236 } ) ;
252237
253- it ( 'should return error from calculateAndUpdateProgress if it fails during progress update ' , async ( ) => {
238+ it ( 'should return error from calculateAndUpdateProgress if it fails' , async ( ) => {
254239 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
255- const repoReturnWithValidContent = {
240+ vi . mocked ( findQuizById ) . mockReturnValue ( {
256241 ...MOCK_RAW_QUIZ_REPO_RETURN ,
257242 content : { ...MOCK_PARSED_QUIZ_DATA_CONTENT } ,
258- } ;
259- vi . mocked ( findQuizById ) . mockReturnValue ( repoReturnWithValidContent as any ) ;
243+ } ) ;
260244 const mockErrorResult = {
261245 currentLevel : 'A1' as const ,
262246 currentStreak : 0 ,
@@ -268,7 +252,6 @@ describe('User Progress Server Actions', () => {
268252 const params = { ...baseParams , ans : 'B' } ;
269253 const result = await submitAnswer ( params ) ;
270254
271- expect ( vi . mocked ( findQuizById ) ) . toHaveBeenCalledWith ( MOCK_QUIZ_ID ) ;
272255 expect ( result . feedback ?. isCorrect ) . toBe ( true ) ;
273256 expect ( vi . mocked ( calculateAndUpdateProgress ) ) . toHaveBeenCalledWith (
274257 MOCK_USER_ID ,
@@ -284,23 +267,20 @@ describe('User Progress Server Actions', () => {
284267 describe ( 'getProgress' , ( ) => {
285268 const params = { language : MOCK_LANGUAGE } ;
286269
287- it ( 'should return Unauthorized if user is not authenticated' , async ( ) => {
288- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( null ) ;
289- const result = await getProgress ( params ) ;
290- expect ( result . error ) . toBe ( 'Unauthorized: User not logged in.' ) ;
291- expect ( result . currentLevel ) . toBe ( 'A1' ) ;
292- expect ( result . currentStreak ) . toBe ( 0 ) ;
293- expect ( result ) . not . toHaveProperty ( 'leveledUp' ) ;
294- expect ( vi . mocked ( findUserProgress ) ) . not . toHaveBeenCalled ( ) ;
295- } ) ;
270+ it . each ( [
271+ [ null , { error : 'Unauthorized: User not logged in.' , currentLevel : 'A1' , currentStreak : 0 } ] ,
272+ [
273+ { dbId : MOCK_USER_ID } ,
274+ { error : 'Invalid parameters provided.' , currentLevel : 'A1' , currentStreak : 0 } ,
275+ ] ,
276+ ] ) ( 'should handle error cases %#' , async ( user , expected ) => {
277+ vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( user ) ;
296278
297- it ( 'should return Invalid parameters for invalid input' , async ( ) => {
298- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
299- const invalidParams = { language : 'e' } ;
300- const result = await getProgress ( invalidParams ) ;
301- expect ( result . error ) . toBe ( 'Invalid parameters provided.' ) ;
302- expect ( result . currentLevel ) . toBe ( 'A1' ) ;
303- expect ( result . currentStreak ) . toBe ( 0 ) ;
279+ const result = await getProgress ( user ? { language : 'e' } : params ) ;
280+
281+ expect ( result . error ) . toBe ( expected . error ) ;
282+ expect ( result . currentLevel ) . toBe ( expected . currentLevel ) ;
283+ expect ( result . currentStreak ) . toBe ( expected . currentStreak ) ;
304284 expect ( result ) . not . toHaveProperty ( 'leveledUp' ) ;
305285 expect ( vi . mocked ( findUserProgress ) ) . not . toHaveBeenCalled ( ) ;
306286 } ) ;
@@ -357,21 +337,16 @@ describe('User Progress Server Actions', () => {
357337 currentLevel : 'B1' ,
358338 } ;
359339
360- it ( 'should return Unauthorized if user is not authenticated' , async ( ) => {
361- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( null ) ;
362- const result = await submitFeedback ( params ) ;
363- expect ( result . success ) . toBe ( false ) ;
364- expect ( result . error ) . toBe ( 'Unauthorized' ) ;
365- expect ( vi . mocked ( findQuizById ) ) . not . toHaveBeenCalled ( ) ;
366- expect ( vi . mocked ( createFeedback ) ) . not . toHaveBeenCalled ( ) ;
367- } ) ;
340+ it . each ( [
341+ [ null , params , false , 'Unauthorized' ] ,
342+ [ { dbId : MOCK_USER_ID } , { ...params , quizId : - 5 } , false , 'Invalid parameters' ] ,
343+ ] ) ( 'should handle error cases %#' , async ( user , testParams , success , error ) => {
344+ vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( user ) ;
368345
369- it ( 'should return Invalid parameters for invalid input' , async ( ) => {
370- vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
371- const invalidParams = { ...params , quizId : - 5 } ;
372- const result = await submitFeedback ( invalidParams ) ;
373- expect ( result . success ) . toBe ( false ) ;
374- expect ( result . error ) . toBe ( 'Invalid parameters' ) ;
346+ const result = await submitFeedback ( testParams ) ;
347+
348+ expect ( result . success ) . toBe ( success ) ;
349+ expect ( result . error ) . toBe ( error ) ;
375350 expect ( vi . mocked ( findQuizById ) ) . not . toHaveBeenCalled ( ) ;
376351 expect ( vi . mocked ( createFeedback ) ) . not . toHaveBeenCalled ( ) ;
377352 } ) ;
@@ -408,13 +383,12 @@ describe('User Progress Server Actions', () => {
408383 expect ( result . error ) . toBeUndefined ( ) ;
409384 } ) ;
410385
411- it ( 'should handle optional userAnswer and isCorrect (passing undefined/boolean to repo) ' , async ( ) => {
386+ it ( 'should handle optional userAnswer and isCorrect' , async ( ) => {
412387 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
413388 vi . mocked ( findQuizById ) . mockReturnValue ( MOCK_RAW_QUIZ_REPO_RETURN as any ) ;
414389 vi . mocked ( createFeedback ) . mockReturnValue ( 1 ) ;
415390
416- const feedbackParams1 = { ...params , is_good : 0 , isCorrect : false } ;
417- await submitFeedback ( feedbackParams1 ) ;
391+ await submitFeedback ( { ...params , is_good : 0 , isCorrect : false } ) ;
418392 expect ( vi . mocked ( createFeedback ) ) . toHaveBeenCalledWith ( {
419393 quiz_id : MOCK_QUIZ_ID ,
420394 user_id : MOCK_USER_ID ,
@@ -423,8 +397,7 @@ describe('User Progress Server Actions', () => {
423397 is_correct : false ,
424398 } ) ;
425399
426- const feedbackParams2 = { ...params , is_good : 1 } ;
427- await submitFeedback ( feedbackParams2 ) ;
400+ await submitFeedback ( { ...params , is_good : 1 } ) ;
428401 expect ( vi . mocked ( createFeedback ) ) . toHaveBeenCalledWith ( {
429402 quiz_id : MOCK_QUIZ_ID ,
430403 user_id : MOCK_USER_ID ,
@@ -437,9 +410,8 @@ describe('User Progress Server Actions', () => {
437410 it ( 'should return error if repository createFeedback fails' , async ( ) => {
438411 vi . mocked ( getAuthenticatedSessionUser ) . mockResolvedValue ( { dbId : MOCK_USER_ID } ) ;
439412 vi . mocked ( findQuizById ) . mockReturnValue ( MOCK_RAW_QUIZ_REPO_RETURN as any ) ;
440- const repoError = new Error ( 'Insert failed' ) ;
441413 vi . mocked ( createFeedback ) . mockImplementation ( ( ) => {
442- throw repoError ;
414+ throw new Error ( 'Insert failed' ) ;
443415 } ) ;
444416
445417 const result = await submitFeedback ( params ) ;
0 commit comments