3636import org .springframework .beans .factory .NoSuchBeanDefinitionException ;
3737
3838import com .cloud .projects .dao .ProjectDao ;
39+ import com .cloud .projects .dao .ProjectInvitationDao ;
40+ import com .cloud .resourcelimit .CheckedReservation ;
41+ import com .cloud .user .Account ;
42+ import com .cloud .user .AccountManager ;
43+ import com .cloud .user .AccountVO ;
44+ import com .cloud .user .ResourceLimitService ;
45+ import com .cloud .user .User ;
46+ import com .cloud .user .dao .AccountDao ;
3947import com .cloud .utils .component .ComponentContext ;
48+ import com .cloud .utils .db .Transaction ;
49+ import com .cloud .utils .db .TransactionCallback ;
4050
4151
4252@ RunWith (MockitoJUnitRunner .class )
@@ -49,6 +59,24 @@ public class ProjectManagerImplTest {
4959 @ Mock
5060 ProjectDao projectDao ;
5161
62+ @ Mock
63+ ProjectInvitationDao projectInvitationDao ;
64+
65+ @ Mock
66+ ProjectAccountDao projectAccountDao ;
67+
68+ @ Mock
69+ AccountDao accountDao ;
70+
71+ @ Mock
72+ ResourceLimitService resourceLimitMgr ;
73+
74+ @ Mock
75+ ReservationDao reservationDao ;
76+
77+ @ Mock
78+ AccountManager accountManager ;
79+
5280 List <ProjectVO > updateProjects ;
5381
5482 @ Before
@@ -317,6 +345,182 @@ public void unassignAccountFromProjectTestWithAdminRole() {
317345 Mockito .verify (projectAccountDao ).remove (2L );
318346 }
319347
348+ @ Test
349+ public void cleanupProjectTestWithAccountAddedToProject () throws Exception {
350+ ProjectVO project = mockProject (100L , 1L );
351+ AccountVO projectAccount = mockAccount (1L );
352+ AccountVO callerAccount = mockAccount (2L );
353+ ProjectAccountVO addedAccount = mockProjectAccountWithIds (10L , 100L , 10L , Role .Regular );
354+
355+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
356+ Mockito .doReturn (true ).when (projectManager ).unassignAccountFromProject (100L , 10L );
357+ Mockito .when (projectAccountDao .listByProjectId (100L ))
358+ .thenReturn (List .of (addedAccount ));
359+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (true );
360+
361+ try (MockedStatic <Transaction > mockedTransaction = Mockito .mockStatic (Transaction .class )) {
362+ mockedTransaction .when (() -> Transaction .execute (Mockito .any (TransactionCallback .class )))
363+ .thenAnswer (invocation -> {
364+ TransactionCallback <?> callback = invocation .getArgument (0 );
365+ return callback .doInTransaction (null );
366+ });
367+
368+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
369+
370+ Assert .assertTrue (result );
371+ Mockito .verify (accountManager ).deleteAccount (projectAccount , 1L , callerAccount );
372+ Mockito .verify (projectAccountDao ).listByProjectId (100L );
373+ Mockito .verify (projectInvitationDao ).cleanupInvitations (100L );
374+ }
375+ }
376+
377+ @ Test
378+ public void cleanupProjectTestWithUserAddedToProject () throws Exception {
379+ ProjectVO project = mockProject (100L , 1L );
380+ AccountVO projectAccount = mockAccount (1L );
381+ AccountVO callerAccount = mockAccount (2L );
382+ ProjectAccountVO userMapping = mockProjectAccountWithIds (11L , 100L , 20L , Role .Admin );
383+
384+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
385+ Mockito .doReturn (true ).when (projectManager ).unassignAccountFromProject (100L , 20L );
386+ Mockito .when (projectAccountDao .listByProjectId (100L ))
387+ .thenReturn (List .of (userMapping ));
388+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (true );
389+
390+ try (MockedStatic <Transaction > mockedTransaction = Mockito .mockStatic (Transaction .class )) {
391+ mockedTransaction .when (() -> Transaction .execute (Mockito .any (TransactionCallback .class )))
392+ .thenAnswer (invocation -> {
393+ TransactionCallback <?> callback = invocation .getArgument (0 );
394+ return callback .doInTransaction (null );
395+ });
396+
397+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
398+
399+ Assert .assertTrue (result );
400+ Mockito .verify (accountManager ).deleteAccount (projectAccount , 1L , callerAccount );
401+ Mockito .verify (projectAccountDao ).listByProjectId (100L );
402+ Mockito .verify (projectInvitationDao ).cleanupInvitations (100L );
403+ }
404+ }
405+
406+ @ Test
407+ public void cleanupProjectTestWithMultipleAccountsAndUsersRemoved () throws Exception {
408+ ProjectVO project = mockProject (100L , 1L );
409+ AccountVO projectAccount = mockAccount (1L );
410+ AccountVO callerAccount = mockAccount (2L );
411+ ProjectAccountVO account1 = mockProjectAccountWithIds (10L , 100L , 10L , Role .Regular );
412+ ProjectAccountVO user1 = mockProjectAccountWithIds (11L , 100L , 20L , Role .Admin );
413+
414+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
415+ Mockito .doReturn (true ).when (projectManager ).unassignAccountFromProject (100L , 10L );
416+ Mockito .doReturn (true ).when (projectManager ).unassignAccountFromProject (100L , 20L );
417+ Mockito .when (projectAccountDao .listByProjectId (100L ))
418+ .thenReturn (List .of (account1 , user1 ));
419+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (true );
420+
421+ try (MockedStatic <Transaction > mockedTransaction = Mockito .mockStatic (Transaction .class )) {
422+ mockedTransaction .when (() -> Transaction .execute (Mockito .any (TransactionCallback .class )))
423+ .thenAnswer (invocation -> {
424+ TransactionCallback <?> callback = invocation .getArgument (0 );
425+ return callback .doInTransaction (null );
426+ });
427+
428+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
429+
430+ Assert .assertTrue (result );
431+ Mockito .verify (accountManager ).deleteAccount (projectAccount , 1L , callerAccount );
432+ Mockito .verify (projectAccountDao ).listByProjectId (100L );
433+ Mockito .verify (projectInvitationDao ).cleanupInvitations (100L );
434+ Mockito .verify (projectManager ).unassignAccountFromProject (100L , 10L );
435+ Mockito .verify (projectManager ).unassignAccountFromProject (100L , 20L );
436+ }
437+ }
438+
439+ @ Test
440+ public void cleanupProjectTestAccountDeletionFailure () throws Exception {
441+ ProjectVO project = mockProject (100L , 1L );
442+ AccountVO projectAccount = mockAccount (1L );
443+ AccountVO callerAccount = mockAccount (2L );
444+
445+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
446+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (false );
447+
448+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
449+
450+ Assert .assertFalse (result );
451+ Mockito .verify (accountManager ).deleteAccount (projectAccount , 1L , callerAccount );
452+ Mockito .verify (projectAccountDao , Mockito .never ()).listByProjectId (Mockito .anyLong ());
453+ Mockito .verify (projectInvitationDao , Mockito .never ()).cleanupInvitations (Mockito .anyLong ());
454+ }
455+
456+ @ Test
457+ public void cleanupProjectTestUnassignAccountFailure () throws Exception {
458+ ProjectVO project = mockProject (100L , 1L );
459+ AccountVO projectAccount = mockAccount (1L );
460+ AccountVO callerAccount = mockAccount (2L );
461+ ProjectAccountVO addedAccount = mockProjectAccountWithIds (10L , 100L , 10L , Role .Regular );
462+
463+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
464+ Mockito .doReturn (false ).when (projectManager ).unassignAccountFromProject (100L , 10L );
465+ Mockito .when (projectAccountDao .listByProjectId (100L ))
466+ .thenReturn (List .of (addedAccount ));
467+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (true );
468+
469+ try (MockedStatic <Transaction > mockedTransaction = Mockito .mockStatic (Transaction .class )) {
470+ mockedTransaction .when (() -> Transaction .execute (Mockito .any (TransactionCallback .class )))
471+ .thenAnswer (invocation -> {
472+ TransactionCallback <?> callback = invocation .getArgument (0 );
473+ return callback .doInTransaction (null );
474+ });
475+
476+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
477+
478+ Assert .assertFalse (result );
479+ Mockito .verify (projectManager ).unassignAccountFromProject (100L , 10L );
480+ Mockito .verify (projectInvitationDao ).cleanupInvitations (100L );
481+ }
482+ }
483+
484+ @ Test
485+ public void cleanupProjectTestNoAccountsOrUsersToRemove () throws Exception {
486+ ProjectVO project = mockProject (100L , 1L );
487+ AccountVO projectAccount = mockAccount (1L );
488+ AccountVO callerAccount = mockAccount (2L );
489+
490+ Mockito .when (accountDao .findById (1L )).thenReturn (projectAccount );
491+ Mockito .when (projectAccountDao .listByProjectId (100L ))
492+ .thenReturn (Collections .emptyList ());
493+ Mockito .when (accountManager .deleteAccount (projectAccount , 1L , callerAccount )).thenReturn (true );
494+
495+ try (MockedStatic <Transaction > mockedTransaction = Mockito .mockStatic (Transaction .class )) {
496+ mockedTransaction .when (() -> Transaction .execute (Mockito .any (TransactionCallback .class )))
497+ .thenAnswer (invocation -> {
498+ TransactionCallback <?> callback = invocation .getArgument (0 );
499+ return callback .doInTransaction (null );
500+ });
501+
502+ boolean result = invokeCleanupProject (project , callerAccount , 1L );
503+
504+ Assert .assertTrue (result );
505+ Mockito .verify (accountManager ).deleteAccount (projectAccount , 1L , callerAccount );
506+ Mockito .verify (projectAccountDao ).listByProjectId (100L );
507+ Mockito .verify (projectInvitationDao ).cleanupInvitations (100L );
508+ }
509+ }
510+
511+ private boolean invokeCleanupProject (ProjectVO project , Account caller , long callerUserId ) throws Exception {
512+ var method = ProjectManagerImpl .class .getDeclaredMethod ("cleanupProject" , Project .class , AccountVO .class , Long .class );
513+ method .setAccessible (true );
514+ return (boolean ) method .invoke (projectManager , project , caller , callerUserId );
515+ }
516+
517+ private ProjectVO mockProject (long id , long projectAccountId ) {
518+ ProjectVO project = Mockito .mock (ProjectVO .class );
519+ Mockito .when (project .getId ()).thenReturn (id );
520+ Mockito .when (project .getProjectAccountId ()).thenReturn (projectAccountId );
521+ return project ;
522+ }
523+
320524 private User mockUser (long id , long accountId ) {
321525 User user = Mockito .mock (User .class );
322526 Mockito .when (user .getId ()).thenReturn (id );
@@ -336,4 +540,13 @@ private ProjectAccountVO mockProjectAccount(long id, Role role) {
336540 Mockito .when (projectAccount .getAccountRole ()).thenReturn (role );
337541 return projectAccount ;
338542 }
543+
544+ private ProjectAccountVO mockProjectAccountWithIds (long id , long projectId , long accountId , Role role ) {
545+ ProjectAccountVO projectAccount = Mockito .mock (ProjectAccountVO .class );
546+ Mockito .lenient ().when (projectAccount .getId ()).thenReturn (id );
547+ Mockito .lenient ().when (projectAccount .getProjectId ()).thenReturn (projectId );
548+ Mockito .lenient ().when (projectAccount .getAccountId ()).thenReturn (accountId );
549+ Mockito .lenient ().when (projectAccount .getAccountRole ()).thenReturn (role );
550+ return projectAccount ;
551+ }
339552}
0 commit comments