mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
491 lines
11 KiB
Go
491 lines
11 KiB
Go
package workspaces_testing
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
"databasus-backend/internal/features/audit_logs"
|
|
users_dto "databasus-backend/internal/features/users/dto"
|
|
users_enums "databasus-backend/internal/features/users/enums"
|
|
users_middleware "databasus-backend/internal/features/users/middleware"
|
|
users_services "databasus-backend/internal/features/users/services"
|
|
users_testing "databasus-backend/internal/features/users/testing"
|
|
workspaces_dto "databasus-backend/internal/features/workspaces/dto"
|
|
workspaces_models "databasus-backend/internal/features/workspaces/models"
|
|
workspaces_repositories "databasus-backend/internal/features/workspaces/repositories"
|
|
)
|
|
|
|
func CreateTestRouter(controllers ...ControllerInterface) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
v1 := router.Group("/api/v1")
|
|
protected := v1.Group("").Use(users_middleware.AuthMiddleware(users_services.GetUserService()))
|
|
|
|
for _, controller := range controllers {
|
|
if routerGroup, ok := protected.(*gin.RouterGroup); ok {
|
|
controller.RegisterRoutes(routerGroup)
|
|
}
|
|
}
|
|
|
|
audit_logs.SetupDependencies()
|
|
|
|
return router
|
|
}
|
|
|
|
func CreateTestWorkspace(
|
|
name string,
|
|
owner *users_dto.SignInResponseDTO,
|
|
router *gin.Engine,
|
|
) *workspaces_models.Workspace {
|
|
workspace, _ := CreateTestWorkspaceViaAPI(name, owner, router)
|
|
return workspace
|
|
}
|
|
|
|
func CreateTestWorkspaceViaAPI(
|
|
name string,
|
|
owner *users_dto.SignInResponseDTO,
|
|
router *gin.Engine,
|
|
) (*workspaces_models.Workspace, string) {
|
|
return createTestWorkspaceViaAPI(name, owner, router, true)
|
|
}
|
|
|
|
func CreateTestWorkspaceViaAPIWithoutSettingsChange(
|
|
name string,
|
|
owner *users_dto.SignInResponseDTO,
|
|
router *gin.Engine,
|
|
) (*workspaces_models.Workspace, string) {
|
|
return createTestWorkspaceViaAPI(name, owner, router, false)
|
|
}
|
|
|
|
func createTestWorkspaceViaAPI(
|
|
name string,
|
|
owner *users_dto.SignInResponseDTO,
|
|
router *gin.Engine,
|
|
enableMemberCreation bool,
|
|
) (*workspaces_models.Workspace, string) {
|
|
if enableMemberCreation {
|
|
users_testing.EnableMemberWorkspaceCreation()
|
|
defer users_testing.ResetSettingsToDefaults()
|
|
}
|
|
|
|
request := workspaces_dto.CreateWorkspaceRequestDTO{Name: name}
|
|
w := MakeAPIRequest(router, "POST", "/api/v1/workspaces", "Bearer "+owner.Token, request)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic(
|
|
fmt.Sprintf(
|
|
"Failed to create workspace. Status: %d, Body: %s",
|
|
w.Code,
|
|
w.Body.String(),
|
|
),
|
|
)
|
|
}
|
|
|
|
var response workspaces_dto.WorkspaceResponseDTO
|
|
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
workspace := &workspaces_models.Workspace{
|
|
ID: response.ID,
|
|
Name: response.Name,
|
|
}
|
|
|
|
return workspace, owner.Token
|
|
}
|
|
|
|
func CreateTestWorkspaceWithToken(
|
|
name string,
|
|
token string,
|
|
router *gin.Engine,
|
|
) (*workspaces_models.Workspace, string) {
|
|
return createTestWorkspaceWithToken(name, token, router, true)
|
|
}
|
|
|
|
func CreateTestWorkspaceWithTokenWithoutSettingsChange(
|
|
name string,
|
|
token string,
|
|
router *gin.Engine,
|
|
) (*workspaces_models.Workspace, string) {
|
|
return createTestWorkspaceWithToken(name, token, router, false)
|
|
}
|
|
|
|
func createTestWorkspaceWithToken(
|
|
name string,
|
|
token string,
|
|
router *gin.Engine,
|
|
enableMemberCreation bool,
|
|
) (*workspaces_models.Workspace, string) {
|
|
if enableMemberCreation {
|
|
users_testing.EnableMemberWorkspaceCreation()
|
|
defer users_testing.ResetSettingsToDefaults()
|
|
}
|
|
|
|
request := workspaces_dto.CreateWorkspaceRequestDTO{Name: name}
|
|
w := MakeAPIRequest(router, "POST", "/api/v1/workspaces", "Bearer "+token, request)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic(
|
|
fmt.Sprintf(
|
|
"Failed to create workspace. Status: %d, Body: %s",
|
|
w.Code,
|
|
w.Body.String(),
|
|
),
|
|
)
|
|
}
|
|
|
|
var response workspaces_dto.WorkspaceResponseDTO
|
|
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
workspace := &workspaces_models.Workspace{
|
|
ID: response.ID,
|
|
Name: response.Name,
|
|
}
|
|
|
|
return workspace, token
|
|
}
|
|
|
|
func AddMemberToWorkspace(
|
|
workspace *workspaces_models.Workspace,
|
|
member *users_dto.SignInResponseDTO,
|
|
role users_enums.WorkspaceRole,
|
|
ownerToken string,
|
|
router *gin.Engine,
|
|
) {
|
|
request := workspaces_dto.AddMemberRequestDTO{
|
|
Email: member.Email,
|
|
Role: role,
|
|
}
|
|
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"POST",
|
|
"/api/v1/workspaces/memberships/"+workspace.ID.String()+"/members",
|
|
"Bearer "+ownerToken,
|
|
request,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to add member to workspace via API: " + w.Body.String())
|
|
}
|
|
}
|
|
|
|
func AddMemberToWorkspaceViaOwner(
|
|
workspace *workspaces_models.Workspace,
|
|
member *users_dto.SignInResponseDTO,
|
|
role users_enums.WorkspaceRole,
|
|
router *gin.Engine,
|
|
) {
|
|
membershipRepo := &workspaces_repositories.MembershipRepository{}
|
|
workspaceMembers, err := membershipRepo.GetWorkspaceMembers(workspace.ID)
|
|
if err != nil {
|
|
panic("Failed to get workspace members: " + err.Error())
|
|
}
|
|
|
|
var ownerToken string
|
|
for _, m := range workspaceMembers {
|
|
if m.Role == users_enums.WorkspaceRoleOwner {
|
|
userService := users_services.GetUserService()
|
|
|
|
owner, err := userService.GetUserByID(m.UserID)
|
|
if err != nil {
|
|
panic("Failed to get owner user: " + err.Error())
|
|
}
|
|
|
|
tokenResponse, err := userService.GenerateAccessToken(owner)
|
|
if err != nil {
|
|
panic("Failed to generate owner token: " + err.Error())
|
|
}
|
|
|
|
ownerToken = tokenResponse.Token
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if ownerToken == "" {
|
|
panic("No workspace owner found")
|
|
}
|
|
|
|
AddMemberToWorkspace(workspace, member, role, ownerToken, router)
|
|
}
|
|
|
|
func InviteMemberToWorkspace(
|
|
workspace *workspaces_models.Workspace,
|
|
email string,
|
|
role users_enums.WorkspaceRole,
|
|
inviterToken string,
|
|
router *gin.Engine,
|
|
) *workspaces_dto.AddMemberResponseDTO {
|
|
request := workspaces_dto.AddMemberRequestDTO{
|
|
Email: email,
|
|
Role: role,
|
|
}
|
|
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"POST",
|
|
"/api/v1/workspaces/memberships/"+workspace.ID.String()+"/members",
|
|
"Bearer "+inviterToken,
|
|
request,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to invite member to workspace via API: " + w.Body.String())
|
|
}
|
|
|
|
var response workspaces_dto.AddMemberResponseDTO
|
|
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &response
|
|
}
|
|
|
|
func ChangeMemberRole(
|
|
workspace *workspaces_models.Workspace,
|
|
memberUserID uuid.UUID,
|
|
newRole users_enums.WorkspaceRole,
|
|
changerToken string,
|
|
router *gin.Engine,
|
|
) {
|
|
request := workspaces_dto.ChangeMemberRoleRequestDTO{
|
|
Role: newRole,
|
|
}
|
|
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"PUT",
|
|
fmt.Sprintf(
|
|
"/api/v1/workspaces/memberships/%s/members/%s/role",
|
|
workspace.ID.String(),
|
|
memberUserID.String(),
|
|
),
|
|
"Bearer "+changerToken,
|
|
request,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to change member role via API: " + w.Body.String())
|
|
}
|
|
}
|
|
|
|
func RemoveMemberFromWorkspace(
|
|
workspace *workspaces_models.Workspace,
|
|
memberUserID uuid.UUID,
|
|
removerToken string,
|
|
router *gin.Engine,
|
|
) {
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"DELETE",
|
|
fmt.Sprintf(
|
|
"/api/v1/workspaces/memberships/%s/members/%s",
|
|
workspace.ID.String(),
|
|
memberUserID.String(),
|
|
),
|
|
"Bearer "+removerToken,
|
|
nil,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to remove member from workspace via API: " + w.Body.String())
|
|
}
|
|
}
|
|
|
|
func GetWorkspaceMembers(
|
|
workspace *workspaces_models.Workspace,
|
|
requesterToken string,
|
|
router *gin.Engine,
|
|
) *workspaces_dto.GetMembersResponseDTO {
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"GET",
|
|
"/api/v1/workspaces/memberships/"+workspace.ID.String()+"/members",
|
|
"Bearer "+requesterToken,
|
|
nil,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to get workspace members via API: " + w.Body.String())
|
|
}
|
|
|
|
var response workspaces_dto.GetMembersResponseDTO
|
|
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &response
|
|
}
|
|
|
|
func UpdateWorkspace(
|
|
workspace *workspaces_models.Workspace,
|
|
updateData *workspaces_models.Workspace,
|
|
updaterToken string,
|
|
router *gin.Engine,
|
|
) *workspaces_models.Workspace {
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"PUT",
|
|
"/api/v1/workspaces/"+workspace.ID.String(),
|
|
"Bearer "+updaterToken,
|
|
updateData,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to update workspace via API: " + w.Body.String())
|
|
}
|
|
|
|
var response workspaces_models.Workspace
|
|
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &response
|
|
}
|
|
|
|
func DeleteWorkspace(
|
|
workspace *workspaces_models.Workspace,
|
|
deleterToken string,
|
|
router *gin.Engine,
|
|
) {
|
|
w := MakeAPIRequest(
|
|
router,
|
|
"DELETE",
|
|
"/api/v1/workspaces/"+workspace.ID.String(),
|
|
"Bearer "+deleterToken,
|
|
nil,
|
|
)
|
|
|
|
if w.Code != http.StatusOK {
|
|
panic("Failed to delete workspace via API: " + w.Body.String())
|
|
}
|
|
}
|
|
|
|
func RemoveTestWorkspace(workspace *workspaces_models.Workspace, router *gin.Engine) {
|
|
membershipRepo := &workspaces_repositories.MembershipRepository{}
|
|
workspaceMembers, err := membershipRepo.GetWorkspaceMembers(workspace.ID)
|
|
if err != nil {
|
|
// Workspace might already be deleted or doesn't exist, silently return
|
|
return
|
|
}
|
|
|
|
if len(workspaceMembers) == 0 {
|
|
// No members found, workspace might have been deleted, silently return
|
|
return
|
|
}
|
|
|
|
var ownerToken string
|
|
for _, m := range workspaceMembers {
|
|
if m.Role == users_enums.WorkspaceRoleOwner {
|
|
userService := users_services.GetUserService()
|
|
|
|
owner, err := userService.GetUserByID(m.UserID)
|
|
if err != nil {
|
|
// Owner user not found, workspace might be in inconsistent state, try direct deletion
|
|
_ = RemoveTestWorkspaceDirect(workspace.ID)
|
|
return
|
|
}
|
|
|
|
tokenResponse, err := userService.GenerateAccessToken(owner)
|
|
if err != nil {
|
|
// Cannot generate token, try direct deletion
|
|
_ = RemoveTestWorkspaceDirect(workspace.ID)
|
|
return
|
|
}
|
|
|
|
ownerToken = tokenResponse.Token
|
|
break
|
|
}
|
|
}
|
|
|
|
if ownerToken == "" {
|
|
// No owner found, try direct deletion
|
|
_ = RemoveTestWorkspaceDirect(workspace.ID)
|
|
return
|
|
}
|
|
|
|
DeleteWorkspace(workspace, ownerToken, router)
|
|
}
|
|
|
|
func MakeAPIRequest(
|
|
router *gin.Engine,
|
|
method, url, authToken string,
|
|
body any,
|
|
) *httptest.ResponseRecorder {
|
|
var requestBody *bytes.Buffer
|
|
if body != nil {
|
|
bodyJSON, err := json.Marshal(body)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
requestBody = bytes.NewBuffer(bodyJSON)
|
|
} else {
|
|
requestBody = bytes.NewBuffer(nil)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(context.Background(), method, url, requestBody)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if body != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
if authToken != "" {
|
|
req.Header.Set("Authorization", authToken)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
return w
|
|
}
|
|
|
|
// CreateTestWorkspaceDirect creates a workspace directly without using HTTP API
|
|
func CreateTestWorkspaceDirect(
|
|
name string,
|
|
ownerID uuid.UUID,
|
|
) (*workspaces_models.Workspace, error) {
|
|
repo := &workspaces_repositories.WorkspaceRepository{}
|
|
membershipRepo := &workspaces_repositories.MembershipRepository{}
|
|
|
|
workspace := &workspaces_models.Workspace{
|
|
Name: name,
|
|
}
|
|
|
|
err := repo.CreateWorkspace(workspace)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create owner membership
|
|
membership := &workspaces_models.WorkspaceMembership{
|
|
WorkspaceID: workspace.ID,
|
|
UserID: ownerID,
|
|
Role: users_enums.WorkspaceRoleOwner,
|
|
}
|
|
|
|
err = membershipRepo.CreateMembership(membership)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return workspace, nil
|
|
}
|
|
|
|
// RemoveTestWorkspaceDirect removes a workspace directly without using HTTP API
|
|
func RemoveTestWorkspaceDirect(workspaceID uuid.UUID) error {
|
|
repo := &workspaces_repositories.WorkspaceRepository{}
|
|
return repo.DeleteWorkspace(workspaceID)
|
|
}
|