@@ -65,14 +65,16 @@ import {
6565 loginOrSignUpWithProvider ,
6666 sendEmail ,
6767 addSocialIdBase ,
68- doReleaseSocialId
68+ doReleaseSocialId ,
69+ getLastPasswordChangeEvent ,
70+ isPasswordChangedSince
6971} from '../utils'
7072// eslint-disable-next-line import/no-named-default
7173import platform , { getMetadata , PlatformError , Severity , Status } from '@hcengineering/platform'
7274import { decodeTokenVerbose , generateToken , TokenError } from '@hcengineering/server-token'
7375import { randomBytes } from 'crypto'
7476
75- import { type AccountDB , AccountEventType , type Workspace } from '../types'
77+ import { type AccountDB , type AccountEvent , AccountEventType , type Workspace } from '../types'
7678import { accountPlugin } from '../plugin'
7779
7880// Mock platform with minimum required functionality
@@ -514,6 +516,110 @@ describe('account utils', () => {
514516 expect ( verifyPassword ( password , hash , salt ) ) . toBe ( false )
515517 } )
516518 } )
519+
520+ describe ( 'getLastPasswordChangeEvent' , ( ) => {
521+ const mockDb = {
522+ accountEvent : {
523+ find : jest . fn ( ) as jest . MockedFunction < AccountDB [ 'accountEvent' ] [ 'find' ] >
524+ }
525+ } as unknown as AccountDB
526+
527+ beforeEach ( ( ) => {
528+ jest . clearAllMocks ( )
529+ } )
530+
531+ test ( 'should return most recent password change event when it exists' , async ( ) => {
532+ const accountUuid = 'test-account-uuid' as AccountUuid
533+ const now = Date . now ( )
534+ const mockEvent : AccountEvent = {
535+ accountUuid,
536+ eventType : AccountEventType . PASSWORD_CHANGED ,
537+ time : now
538+ }
539+
540+ ; ( mockDb . accountEvent . find as jest . Mock ) . mockResolvedValue ( [ mockEvent ] )
541+
542+ const result = await getLastPasswordChangeEvent ( mockDb , accountUuid )
543+
544+ expect ( result ) . toEqual ( mockEvent )
545+ expect ( mockDb . accountEvent . find ) . toHaveBeenCalledWith (
546+ { accountUuid, eventType : AccountEventType . PASSWORD_CHANGED } ,
547+ { time : 'descending' } ,
548+ 1
549+ )
550+ } )
551+
552+ test ( 'should return null when no password change events exist' , async ( ) => {
553+ const accountUuid = 'test-account-uuid' as AccountUuid
554+
555+ ; ( mockDb . accountEvent . find as jest . Mock ) . mockResolvedValue ( [ ] )
556+
557+ const result = await getLastPasswordChangeEvent ( mockDb , accountUuid )
558+
559+ expect ( result ) . toBeNull ( )
560+ } )
561+ } )
562+
563+ describe ( 'isPasswordChangedSince' , ( ) => {
564+ const mockDb = {
565+ accountEvent : {
566+ find : jest . fn ( ) as jest . MockedFunction < AccountDB [ 'accountEvent' ] [ 'find' ] >
567+ }
568+ } as unknown as AccountDB
569+
570+ beforeEach ( ( ) => {
571+ jest . clearAllMocks ( )
572+ } )
573+
574+ test ( 'should return true when password changed after given timestamp' , async ( ) => {
575+ const accountUuid = 'test-account-uuid' as AccountUuid
576+ const now = Date . now ( )
577+ const oneHourAgo = now - 1000 * 60 * 60 // 1 hour ago
578+ const halfHourAgo = now - 1000 * 60 * 30 // 30 min ago
579+
580+ const mockEvent : AccountEvent = {
581+ accountUuid,
582+ eventType : AccountEventType . PASSWORD_CHANGED ,
583+ time : halfHourAgo
584+ }
585+
586+ ; ( mockDb . accountEvent . find as jest . Mock ) . mockResolvedValue ( [ mockEvent ] )
587+
588+ const result = await isPasswordChangedSince ( mockDb , accountUuid , oneHourAgo )
589+
590+ expect ( result ) . toBe ( true )
591+ } )
592+
593+ test ( 'should return false when password changed before given timestamp' , async ( ) => {
594+ const accountUuid = 'test-account-uuid' as AccountUuid
595+ const now = Date . now ( )
596+ const oneMonthAgo = now - 1000 * 60 * 60 * 24 * 30 // 1 month ago
597+ const twoMonthsAgo = now - 1000 * 60 * 60 * 24 * 60 * 2 // 2 months ago
598+
599+ const mockEvent : AccountEvent = {
600+ accountUuid,
601+ eventType : AccountEventType . PASSWORD_CHANGED ,
602+ time : twoMonthsAgo
603+ }
604+
605+ ; ( mockDb . accountEvent . find as jest . Mock ) . mockResolvedValue ( [ mockEvent ] )
606+
607+ const result = await isPasswordChangedSince ( mockDb , accountUuid , oneMonthAgo )
608+
609+ expect ( result ) . toBe ( false )
610+ } )
611+
612+ test ( 'should return false when no password change events exist' , async ( ) => {
613+ const accountUuid = 'test-account-uuid' as AccountUuid
614+ const now = Date . now ( )
615+
616+ ; ( mockDb . accountEvent . find as jest . Mock ) . mockResolvedValue ( [ ] )
617+
618+ const result = await isPasswordChangedSince ( mockDb , accountUuid , now )
619+
620+ expect ( result ) . toBe ( false )
621+ } )
622+ } )
517623 } )
518624
519625 describe ( 'wrap' , ( ) => {
0 commit comments