@@ -277,6 +277,215 @@ func (s *membershipIntegrationTestSuite) TestCreateMembership() {
277277 })
278278}
279279
280+ func (s * membershipIntegrationTestSuite ) TestDeleteCleanup () {
281+ ctx := context .Background ()
282+
283+ // Create users
284+ user , err := s .User .UpsertByEmail (ctx , "cleanup-test@example.com" , nil )
285+ s .NoError (err )
286+ adminUser , err := s .User .UpsertByEmail (ctx , "admin-user@example.com" , nil )
287+ s .NoError (err )
288+
289+ // Create an organization
290+ org , err := s .Organization .CreateWithRandomName (ctx )
291+ s .NoError (err )
292+
293+ // Add users to organization with different roles
294+ membershipUser , err := s .Membership .Create (ctx , org .ID , user .ID , biz .WithCurrentMembership ())
295+ s .NoError (err )
296+ _ , err = s .Membership .Create (ctx , org .ID , adminUser .ID , biz .WithMembershipRole (authz .RoleAdmin ), biz .WithCurrentMembership ())
297+ s .NoError (err )
298+
299+ // Create a project in the organization
300+ project , err := s .Project .Create (ctx , org .ID , "test-cleanup-project" )
301+ s .NoError (err )
302+
303+ // Add user to the project
304+ projectRef := & biz.IdentityReference {ID : & project .ID }
305+ projectOpts := & biz.AddMemberToProjectOpts {
306+ ProjectReference : projectRef ,
307+ UserEmail : "cleanup-test@example.com" ,
308+ RequesterID : uuid .MustParse (adminUser .ID ),
309+ Role : authz .RoleProjectViewer ,
310+ }
311+ _ , err = s .Project .AddMemberToProject (ctx , uuid .MustParse (org .ID ), projectOpts )
312+ s .NoError (err )
313+
314+ // Create a group in the organization
315+ userUUID := uuid .MustParse (user .ID )
316+ group , err := s .Group .Create (ctx , uuid .MustParse (org .ID ), "test-cleanup-group" , "Group for cleanup testing" , & userUUID )
317+ s .NoError (err )
318+
319+ // Create another user to add to the group
320+ otherUser , err := s .User .UpsertByEmail (ctx , "other-member@example.com" , nil )
321+ s .NoError (err )
322+ _ , err = s .Membership .Create (ctx , org .ID , otherUser .ID )
323+ s .NoError (err )
324+
325+ // Add other user to the group
326+ groupRef := & biz.IdentityReference {ID : & group .ID }
327+ groupOpts := & biz.AddMemberToGroupOpts {
328+ IdentityReference : groupRef ,
329+ UserEmail : "other-member@example.com" ,
330+ RequesterID : uuid .MustParse (user .ID ),
331+ Maintainer : false ,
332+ }
333+ _ , err = s .Group .AddMemberToGroup (ctx , uuid .MustParse (org .ID ), groupOpts )
334+ s .NoError (err )
335+
336+ // Verify initial state
337+ s .Run ("verify initial state" , func () {
338+ // Verify user is in the project
339+ members , count , err := s .Project .ListMembers (ctx , uuid .MustParse (org .ID ), projectRef , nil )
340+ s .NoError (err )
341+ s .Equal (1 , count )
342+ s .Equal (1 , len (members ))
343+ s .Equal (user .ID , members [0 ].User .ID )
344+
345+ // Verify user is in the group as maintainer
346+ groupMembers , groupCount , err := s .Group .ListMembers (ctx , uuid .MustParse (org .ID ), & biz.ListMembersOpts {
347+ IdentityReference : groupRef ,
348+ }, nil )
349+ s .NoError (err )
350+ s .Equal (2 , groupCount ) // User + otherUser
351+ userFound := false
352+ for _ , member := range groupMembers {
353+ if member .User .ID == user .ID {
354+ s .True (member .Maintainer )
355+ userFound = true
356+ break
357+ }
358+ }
359+ s .True (userFound , "User should be found in the group as a maintainer" )
360+ })
361+
362+ // Delete the user's membership
363+ s .Run ("delete user membership" , func () {
364+ err := s .Membership .LeaveAndDeleteOrg (ctx , user .ID , membershipUser .ID .String ())
365+ s .NoError (err )
366+
367+ // Check that the organization still exists (since there's still admin user)
368+ _ , err = s .Organization .FindByID (ctx , org .ID )
369+ s .NoError (err )
370+
371+ // Verify user is removed from project
372+ projectMembers , projectCount , err := s .Project .ListMembers (ctx , uuid .MustParse (org .ID ), projectRef , nil )
373+ s .NoError (err )
374+ s .Equal (0 , projectCount )
375+ s .Equal (0 , len (projectMembers ))
376+
377+ // Verify user is removed from group but other member remains
378+ groupMembers , groupCount , err := s .Group .ListMembers (ctx , uuid .MustParse (org .ID ), & biz.ListMembersOpts {
379+ IdentityReference : groupRef ,
380+ }, nil )
381+ s .NoError (err )
382+ s .Equal (1 , groupCount ) // Only otherUser should remain
383+ s .Equal (1 , len (groupMembers ))
384+ s .Equal (otherUser .ID , groupMembers [0 ].User .ID )
385+ s .False (groupMembers [0 ].Maintainer )
386+
387+ // Verify group membership has been decremented
388+ updatedGroup , err := s .Group .Get (ctx , uuid .MustParse (org .ID ), & biz.IdentityReference {ID : & group .ID })
389+ s .NoError (err )
390+ s .Equal (1 , updatedGroup .MemberCount )
391+ })
392+ }
393+
394+ func (s * membershipIntegrationTestSuite ) TestDeleteWithGroups () {
395+ ctx := context .Background ()
396+
397+ // Create a user
398+ user , err := s .User .UpsertByEmail (ctx , "groups-test@example.com" , nil )
399+ s .NoError (err )
400+ userUUID := uuid .MustParse (user .ID )
401+
402+ // Create an organization
403+ org , err := s .Organization .CreateWithRandomName (ctx )
404+ s .NoError (err )
405+ orgUUID := uuid .MustParse (org .ID )
406+
407+ // Add user to organization
408+ membershipUser , err := s .Membership .Create (ctx , org .ID , user .ID , biz .WithMembershipRole (authz .RoleAdmin ), biz .WithCurrentMembership ())
409+ s .NoError (err )
410+
411+ // Create multiple groups with the user as maintainer
412+ group1 , err := s .Group .Create (ctx , orgUUID , "group-1" , "First group" , & userUUID )
413+ s .NoError (err )
414+ group2 , err := s .Group .Create (ctx , orgUUID , "group-2" , "Second group" , & userUUID )
415+ s .NoError (err )
416+
417+ // Add the groups to a project
418+ pr , err := s .Project .Create (ctx , org .ID , "test-groups-project" )
419+ s .NoError (err )
420+ projectRef := & biz.IdentityReference {ID : & pr .ID }
421+
422+ // Add group1 to project
423+ groupProjectOpts := & biz.AddMemberToProjectOpts {
424+ ProjectReference : projectRef ,
425+ GroupReference : & biz.IdentityReference {ID : & group1 .ID },
426+ RequesterID : userUUID ,
427+ Role : authz .RoleProjectAdmin ,
428+ }
429+ _ , err = s .Project .AddMemberToProject (ctx , orgUUID , groupProjectOpts )
430+ s .NoError (err )
431+
432+ // Verify initial state
433+ s .Run ("verify initial state with groups" , func () {
434+ // Check user is a maintainer in both groups
435+ group1Members , _ , err := s .Group .ListMembers (ctx , orgUUID , & biz.ListMembersOpts {
436+ IdentityReference : & biz.IdentityReference {ID : & group1 .ID },
437+ }, nil )
438+ s .NoError (err )
439+ s .Equal (1 , len (group1Members ))
440+ s .Equal (user .ID , group1Members [0 ].User .ID )
441+ s .True (group1Members [0 ].Maintainer )
442+
443+ group2Members , _ , err := s .Group .ListMembers (ctx , orgUUID , & biz.ListMembersOpts {
444+ IdentityReference : & biz.IdentityReference {ID : & group2 .ID },
445+ }, nil )
446+ s .NoError (err )
447+ s .Equal (1 , len (group2Members ))
448+ s .Equal (user .ID , group2Members [0 ].User .ID )
449+ s .True (group2Members [0 ].Maintainer )
450+
451+ // Check group1 is in the project
452+ projectMembers , _ , err := s .Project .ListMembers (ctx , orgUUID , projectRef , nil )
453+ s .NoError (err )
454+ s .Equal (1 , len (projectMembers ))
455+ s .Equal (group1 .ID , projectMembers [0 ].Group .ID )
456+ })
457+
458+ // Delete the user's membership
459+ s .Run ("delete user membership with groups" , func () {
460+ err := s .Membership .LeaveAndDeleteOrg (ctx , user .ID , membershipUser .ID .String ())
461+ s .NoError (err )
462+
463+ // The organization should be deleted since this was the only user
464+ _ , err = s .Organization .FindByID (ctx , org .ID )
465+ s .True (biz .IsNotFound (err ), "Organization should be deleted" )
466+
467+ // All groups should be soft-deleted
468+ _ , err = s .Group .Get (ctx , orgUUID , & biz.IdentityReference {ID : & group1 .ID })
469+ s .True (biz .IsNotFound (err ), "Group 1 should be deleted" )
470+
471+ _ , err = s .Group .Get (ctx , orgUUID , & biz.IdentityReference {ID : & group2 .ID })
472+ s .True (biz .IsNotFound (err ), "Group 2 should be deleted" )
473+
474+ // The project should be deleted
475+ _ , err = s .Project .FindProjectByReference (ctx , org .ID , & biz.IdentityReference {ID : projectRef .ID })
476+ s .True (biz .IsNotFound (err ), "Project should be deleted" )
477+
478+ // Verify group memberships are marked as deleted
479+ group1Mem , group1Err := s .Repos .GroupRepo .FindGroupMembershipByGroupAndID (ctx , group1 .ID , userUUID )
480+ s .True (biz .IsNotFound (group1Err ))
481+ s .Nil (group1Mem )
482+
483+ group2Mem , group2Err := s .Repos .GroupRepo .FindGroupMembershipByGroupAndID (ctx , group2 .ID , userUUID )
484+ s .True (biz .IsNotFound (group2Err ))
485+ s .Nil (group2Mem )
486+ })
487+ }
488+
280489// Run the tests
281490func TestMembershipUseCase (t * testing.T ) {
282491 suite .Run (t , new (membershipIntegrationTestSuite ))
0 commit comments