feat: implement tenant-side creator audit feature and update related tests and documentation
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -216,6 +216,27 @@ func (s *TenantTestSuite) Test_ReviewJoin() {
|
||||
So(err, ShouldBeNil)
|
||||
So(req.Status, ShouldEqual, string(consts.TenantJoinRequestStatusRejected))
|
||||
})
|
||||
|
||||
Convey("should deny review when request belongs to another tenant", func() {
|
||||
tenantA, _, _, reqA := setup()
|
||||
|
||||
ownerB := &models.User{Username: "owner_review_cross_b", Phone: "13900009991"}
|
||||
So(models.UserQuery.WithContext(ctx).Create(ownerB), ShouldBeNil)
|
||||
tenantB := &models.Tenant{Name: "Tenant Review B", Code: "tenant_review_cross_b", UserID: ownerB.ID, Status: consts.TenantStatusVerified}
|
||||
So(models.TenantQuery.WithContext(ctx).Create(tenantB), ShouldBeNil)
|
||||
|
||||
err := Tenant.ReviewJoin(ctx, tenantB.ID, ownerB.ID, reqA.ID, &tenant_dto.TenantJoinReviewForm{Action: "approve"})
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
var appErr *errorx.AppError
|
||||
So(errors.As(err, &appErr), ShouldBeTrue)
|
||||
So(appErr.Code, ShouldEqual, errorx.ErrForbidden.Code)
|
||||
|
||||
reqReload, err := models.TenantJoinRequestQuery.WithContext(ctx).Where(models.TenantJoinRequestQuery.ID.Eq(reqA.ID)).First()
|
||||
So(err, ShouldBeNil)
|
||||
So(reqReload.TenantID, ShouldEqual, tenantA.ID)
|
||||
So(reqReload.Status, ShouldEqual, string(consts.TenantJoinRequestStatusPending))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -352,54 +373,132 @@ func (s *TenantTestSuite) Test_ListMembersAndRemove() {
|
||||
So(err, ShouldBeNil)
|
||||
So(exists, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("should deny removing member from another tenant", s.T(), func() {
|
||||
ctx := s.T().Context()
|
||||
database.Truncate(ctx, s.DB,
|
||||
models.TableNameTenantUser,
|
||||
models.TableNameTenant,
|
||||
models.TableNameUser,
|
||||
)
|
||||
|
||||
ownerA := &models.User{Username: "owner_remove_a", Phone: "13900007771"}
|
||||
ownerB := &models.User{Username: "owner_remove_b", Phone: "13900007772"}
|
||||
memberA := &models.User{Username: "member_remove_a", Phone: "13900007773"}
|
||||
So(models.UserQuery.WithContext(ctx).Create(ownerA, ownerB, memberA), ShouldBeNil)
|
||||
|
||||
tenantA := &models.Tenant{Name: "Tenant Remove A", Code: "tenant_remove_cross_a", UserID: ownerA.ID, Status: consts.TenantStatusVerified}
|
||||
tenantB := &models.Tenant{Name: "Tenant Remove B", Code: "tenant_remove_cross_b", UserID: ownerB.ID, Status: consts.TenantStatusVerified}
|
||||
So(models.TenantQuery.WithContext(ctx).Create(tenantA, tenantB), ShouldBeNil)
|
||||
|
||||
memberLinkA := &models.TenantUser{
|
||||
TenantID: tenantA.ID,
|
||||
UserID: memberA.ID,
|
||||
Role: types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
|
||||
Status: consts.UserStatusVerified,
|
||||
}
|
||||
So(models.TenantUserQuery.WithContext(ctx).Create(memberLinkA), ShouldBeNil)
|
||||
|
||||
err := Tenant.RemoveMember(ctx, tenantB.ID, ownerB.ID, memberLinkA.ID)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
var appErr *errorx.AppError
|
||||
So(errors.As(err, &appErr), ShouldBeTrue)
|
||||
So(appErr.Code, ShouldEqual, errorx.ErrForbidden.Code)
|
||||
|
||||
exists, err := models.TenantUserQuery.WithContext(ctx).Where(models.TenantUserQuery.ID.Eq(memberLinkA.ID)).Exists()
|
||||
So(err, ShouldBeNil)
|
||||
So(exists, ShouldBeTrue)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TenantTestSuite) Test_ListInvitesAndDisable() {
|
||||
Convey("ListInvites and DisableInvite", s.T(), func() {
|
||||
ctx := s.T().Context()
|
||||
|
||||
database.Truncate(ctx, s.DB,
|
||||
models.TableNameTenantInvite,
|
||||
models.TableNameTenant,
|
||||
models.TableNameUser,
|
||||
)
|
||||
Convey("should list and disable invite in same tenant", func() {
|
||||
database.Truncate(ctx, s.DB,
|
||||
models.TableNameTenantInvite,
|
||||
models.TableNameTenant,
|
||||
models.TableNameUser,
|
||||
)
|
||||
|
||||
owner := &models.User{Username: "owner_invite", Phone: "13900002003"}
|
||||
_ = models.UserQuery.WithContext(ctx).Create(owner)
|
||||
owner := &models.User{Username: "owner_invite", Phone: "13900002003"}
|
||||
_ = models.UserQuery.WithContext(ctx).Create(owner)
|
||||
|
||||
tenant := &models.Tenant{
|
||||
Name: "Tenant Invite",
|
||||
UserID: owner.ID,
|
||||
Status: consts.TenantStatusVerified,
|
||||
}
|
||||
_ = models.TenantQuery.WithContext(ctx).Create(tenant)
|
||||
tenant := &models.Tenant{
|
||||
Name: "Tenant Invite",
|
||||
UserID: owner.ID,
|
||||
Status: consts.TenantStatusVerified,
|
||||
}
|
||||
_ = models.TenantQuery.WithContext(ctx).Create(tenant)
|
||||
|
||||
invite := &models.TenantInvite{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Code: "invite_list",
|
||||
Status: string(consts.TenantInviteStatusActive),
|
||||
MaxUses: 2,
|
||||
UsedCount: 0,
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
Remark: "测试邀请",
|
||||
}
|
||||
_ = models.TenantInviteQuery.WithContext(ctx).Create(invite)
|
||||
invite := &models.TenantInvite{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Code: "invite_list",
|
||||
Status: string(consts.TenantInviteStatusActive),
|
||||
MaxUses: 2,
|
||||
UsedCount: 0,
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
Remark: "测试邀请",
|
||||
}
|
||||
_ = models.TenantInviteQuery.WithContext(ctx).Create(invite)
|
||||
|
||||
res, err := Tenant.ListInvites(ctx, tenant.ID, owner.ID, &tenant_dto.TenantInviteListFilter{
|
||||
Pagination: requests.Pagination{Page: 1, Limit: 10},
|
||||
res, err := Tenant.ListInvites(ctx, tenant.ID, owner.ID, &tenant_dto.TenantInviteListFilter{
|
||||
Pagination: requests.Pagination{Page: 1, Limit: 10},
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
So(res.Total, ShouldEqual, 1)
|
||||
|
||||
err = Tenant.DisableInvite(ctx, tenant.ID, owner.ID, invite.ID)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
updated, err := models.TenantInviteQuery.WithContext(ctx).
|
||||
Where(models.TenantInviteQuery.ID.Eq(invite.ID)).
|
||||
First()
|
||||
So(err, ShouldBeNil)
|
||||
So(updated.Status, ShouldEqual, string(consts.TenantInviteStatusDisabled))
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
So(res.Total, ShouldEqual, 1)
|
||||
|
||||
err = Tenant.DisableInvite(ctx, tenant.ID, owner.ID, invite.ID)
|
||||
So(err, ShouldBeNil)
|
||||
Convey("should deny disabling invite from another tenant", func() {
|
||||
database.Truncate(ctx, s.DB,
|
||||
models.TableNameTenantInvite,
|
||||
models.TableNameTenant,
|
||||
models.TableNameUser,
|
||||
)
|
||||
|
||||
updated, err := models.TenantInviteQuery.WithContext(ctx).
|
||||
Where(models.TenantInviteQuery.ID.Eq(invite.ID)).
|
||||
First()
|
||||
So(err, ShouldBeNil)
|
||||
So(updated.Status, ShouldEqual, string(consts.TenantInviteStatusDisabled))
|
||||
ownerA := &models.User{Username: "owner_invite_a", Phone: "13900008881"}
|
||||
ownerB := &models.User{Username: "owner_invite_b", Phone: "13900008882"}
|
||||
So(models.UserQuery.WithContext(ctx).Create(ownerA, ownerB), ShouldBeNil)
|
||||
|
||||
tenantA := &models.Tenant{Name: "Tenant Invite A", Code: "tenant_invite_cross_a", UserID: ownerA.ID, Status: consts.TenantStatusVerified}
|
||||
tenantB := &models.Tenant{Name: "Tenant Invite B", Code: "tenant_invite_cross_b", UserID: ownerB.ID, Status: consts.TenantStatusVerified}
|
||||
So(models.TenantQuery.WithContext(ctx).Create(tenantA, tenantB), ShouldBeNil)
|
||||
|
||||
inviteA := &models.TenantInvite{
|
||||
TenantID: tenantA.ID,
|
||||
UserID: ownerA.ID,
|
||||
Code: "invite_cross_disable",
|
||||
Status: string(consts.TenantInviteStatusActive),
|
||||
MaxUses: 2,
|
||||
UsedCount: 0,
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
Remark: "跨租户禁用测试",
|
||||
}
|
||||
So(models.TenantInviteQuery.WithContext(ctx).Create(inviteA), ShouldBeNil)
|
||||
|
||||
err := Tenant.DisableInvite(ctx, tenantB.ID, ownerB.ID, inviteA.ID)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
var appErr *errorx.AppError
|
||||
So(errors.As(err, &appErr), ShouldBeTrue)
|
||||
So(appErr.Code, ShouldEqual, errorx.ErrForbidden.Code)
|
||||
|
||||
reloaded, err := models.TenantInviteQuery.WithContext(ctx).Where(models.TenantInviteQuery.ID.Eq(inviteA.ID)).First()
|
||||
So(err, ShouldBeNil)
|
||||
So(reloaded.Status, ShouldEqual, string(consts.TenantInviteStatusActive))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user