package usecase import ( "ark-permission/internal/domain" "ark-permission/internal/domain/usecase" "ark-permission/internal/entity" ers "code.30cm.net/wanderland/library-go/errors" "fmt" "github.com/stretchr/testify/assert" "reflect" "testing" ) func TestNewPermissionTree(t *testing.T) { tests := []struct { name string want *PermissionTree }{ { name: "ok", want: &PermissionTree{ root: &usecase.Permission{ ID: 0, Name: "root", Children: []*usecase.Permission{}, }, nodes: map[int64]*usecase.Permission{0: { ID: 0, Name: "root", Children: []*usecase.Permission{}, }}, // 根節點也加入 nodes 記錄 paths: make(map[int64][]int), names: make(map[string][]int64), ids: make(map[int64]string), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tree := NewPermissionTree() // 驗證 Root 的值 assert.Equal(t, tree.root.ID, tt.want.root.ID) assert.Equal(t, tree.root.Name, tt.want.root.Name) assert.Equal(t, len(tree.root.Children), len(tt.want.root.Children)) assert.Equal(t, len(tree.nodes), len(tt.want.nodes)) assert.Equal(t, len(tree.paths), len(tt.want.paths)) assert.Equal(t, len(tree.ids), len(tt.want.ids)) }) } } // 測試 AddPermission 函數 func TestAddPermission(t *testing.T) { tree := NewPermissionTree() tests := []struct { name string parentID int64 permission entity.Permission expectedError error }{ { name: "ok", parentID: 0, permission: entity.Permission{ID: 2, Name: "new_permission", HTTPPath: "/new", HTTPMethod: "POST"}, }, { name: "Invalid Parent ID", parentID: 99, // 無效的 parentID permission: entity.Permission{ID: 3, Name: "invalid_parent", HTTPPath: "/invalid", HTTPMethod: "GET"}, expectedError: ers.ResourceNotFound("failed to find ID 99"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tree.AddPermission(tt.parentID, tt.permission) // 錯誤檢查 if !reflect.DeepEqual(err, tt.expectedError) { t.Errorf("expected error %v, got %v", tt.expectedError, err) } if tt.expectedError == nil { // 檢查是否已正確插入到 nodes 中 node, ok := tree.nodes[tt.permission.ID] if !ok { t.Errorf("expected permission with ID %d to be in nodes map", tt.permission.ID) } if node.Name != tt.permission.Name { t.Errorf("expected permission name %s, got %s", tt.permission.Name, node.Name) } // 檢查父節點的子節點是否正確加入 parentNode, _ := tree.FindPermissionByID(tt.parentID) found := false for _, child := range parentNode.Children { if child.ID == tt.permission.ID { found = true break } } if !found { t.Errorf("expected permission ID %d to be child of parent ID %d", tt.permission.ID, tt.parentID) } } }) } } // 測試 buildNodePath 函數 func TestBuildNodePath(t *testing.T) { // ======== 準備測試 ======== tree := NewPermissionTree() // 插入一些節點 err := tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission"}) assert.NoError(t, err) // ======== 準備測試 ======== tests := []struct { name string nodeID int64 expectedPath []int }{ { name: "Grandchild Permission Path", nodeID: 4, expectedPath: []int{0, 0, 0}, // 根 -> 子 -> 孫節點的索引 }, { name: "Child Permission 1 Path", nodeID: 2, expectedPath: []int{0, 0}, // 根 -> 子節點的索引 }, { name: "Child Permission 2 Path", nodeID: 3, expectedPath: []int{0, 1}, // 根 -> 子節點的索引 }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 查找要測試的節點 node, err := tree.FindPermissionByID(tt.nodeID) if err != nil { t.Fatalf("failed to find node with ID %d: %v", tt.nodeID, err) } // 構建節點的完整路徑 // Grandchild Permission Path 測試孫節點的完整路徑,這應該包含根節點和子節點的索引,結果為 [0, 0],表示它是根節點的第一個子節點的第一個子節點。 // Child Permission 1 Path 測試子節點 1 的路徑,應該是 [0],表示它是根節點的第一個子節點。 // Child Permission 2 Path 測試子節點 2 的路徑,應該是 [1],表示它是根節點的第二個子節點。 tree.buildNodePath(node, tt.nodeID) // 從 paths 中獲取構建好的路徑 resultPath, ok := tree.paths[tt.nodeID] if !ok { t.Fatalf("path not found for node ID %d", tt.nodeID) } // 比較結果路徑與預期路徑 if !reflect.DeepEqual(resultPath, tt.expectedPath) { t.Errorf("expected path %v, got %v", tt.expectedPath, resultPath) } }) } } // 測試 GetAllParentPermissionIDs 函數 func TestGetAllParentPermissionIDs(t *testing.T) { tree := NewPermissionTree() // ======== 準備測試 ======== // 添加節點 err := tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(0, entity.Permission{ID: 6, Name: "root_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(6, entity.Permission{ID: 6, Name: "child_permission_3"}) assert.NoError(t, err) // ======== 準備測試 ======== tests := []struct { name string permissions domain.Permissions expectedResult []int64 expectedError error }{ { name: "Valid permissions with open status", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedResult: []int64{0, 1, 2, 4}, // 根 -> 子 -> 孫節點 expectedError: nil, }, { name: "Valid multiple permissions with open status", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, }, expectedResult: []int64{0, 1, 2, 4, 0, 1, 3, 5}, // 多個權限 expectedError: nil, }, { name: "Permission name not found", permissions: domain.Permissions{ "unknown_permission": domain.PermissionStatusOpenCode, }, expectedResult: nil, expectedError: fmt.Errorf("permission with name unknown_permission not found"), }, { name: "Permission close by parent node", permissions: domain.Permissions{ "root_permission_2": domain.PermissionStatusCloseCode, }, expectedResult: nil, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tree.GetAllParentPermissionIDs(tt.permissions) // 檢查返回結果 if !reflect.DeepEqual(result, tt.expectedResult) { t.Errorf("expected %v, got %v", tt.expectedResult, result) } // 檢查錯誤 if (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) { t.Errorf("expected error %v, got %v", tt.expectedError, err) } }) } } // 測試 GetAllParentPermissionStatuses 函數 func TestGetAllParentPermissionStatuses(t *testing.T) { tree := NewPermissionTree() // 添加節點 err := tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) assert.NoError(t, err) tests := []struct { name string permissions domain.Permissions expectedResult domain.Permissions expectedError error }{ { name: "Valid permissions with open status", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedResult: domain.Permissions{ "root_permission": domain.PermissionStatusOpenCode, "child_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedError: nil, }, { name: "Multiple permissions with open status", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, }, expectedResult: domain.Permissions{ "root_permission": domain.PermissionStatusOpenCode, "child_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_1": domain.PermissionStatusOpenCode, "child_permission_2": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, }, expectedError: nil, }, { name: "Permission name not found", permissions: domain.Permissions{ "unknown_permission": domain.PermissionStatusOpenCode, }, expectedResult: nil, expectedError: fmt.Errorf("permission with name unknown_permission not found"), }, { name: "Closed permissions are ignored", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusCloseCode, }, expectedResult: domain.Permissions{}, expectedError: nil, }, { name: "Multiple permissions with close status", permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusCloseCode, }, expectedResult: domain.Permissions{}, expectedError: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tree.GetAllParentPermissionStatuses(tt.permissions) // 檢查返回結果 if !reflect.DeepEqual(result, tt.expectedResult) { t.Errorf("expected %v, got %v", tt.expectedResult, result) } // 檢查錯誤 if (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) { t.Errorf("expected error %v, got %v", tt.expectedError, err) } }) } } // 測試 isParentPermissionOpen 函數 func TestIsParentPermissionOpen(t *testing.T) { tree := NewPermissionTree() // 添加節點 err := tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) assert.NoError(t, err) tests := []struct { name string nodeID int64 permissions domain.Permissions expectedOpen bool }{ { name: "Parent has open child permission", nodeID: 2, permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedOpen: true, }, { name: "Parent has no open child permissions", nodeID: 2, permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusCloseCode, }, expectedOpen: false, }, { name: "Parent with multiple children, one open", nodeID: 2, permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusCloseCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, }, expectedOpen: true, }, { name: "Parent with no child permissions in list", nodeID: 3, permissions: domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedOpen: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { node, err := tree.FindPermissionByID(tt.nodeID) if err != nil { t.Fatalf("failed to find node with ID %d: %v", tt.nodeID, err) } result := tree.isParentPermissionOpen(node, tt.permissions) if result != tt.expectedOpen { t.Errorf("expected %v, got %v", tt.expectedOpen, result) } }) } } // 測試 GetRolePermissionTree 函數 func TestGetRolePermissionTree(t *testing.T) { tree := NewPermissionTree() // 添加節點 err := tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) assert.NoError(t, err) err = tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) assert.NoError(t, err) err = tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) assert.NoError(t, err) // 測試數據 tests := []struct { name string rolePermissions []entity.RolePermission expectedResult domain.Permissions expectedError error }{ { name: "Single role permission with parent and child", rolePermissions: []entity.RolePermission{ {PermissionID: 4}, // grandchild_permission_1 }, expectedResult: domain.Permissions{ "root_permission": domain.PermissionStatusOpenCode, "child_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedError: nil, }, { name: "Multiple role permissions with parents and children", rolePermissions: []entity.RolePermission{ {PermissionID: 4}, // grandchild_permission_1 {PermissionID: 5}, // grandchild_permission_2 }, expectedResult: domain.Permissions{ "root_permission": domain.PermissionStatusOpenCode, "child_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_1": domain.PermissionStatusOpenCode, "child_permission_2": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, }, expectedError: nil, }, { name: "Role permission with no children", rolePermissions: []entity.RolePermission{ {PermissionID: 2}, // child_permission_1 }, expectedResult: domain.Permissions{ "root_permission": domain.PermissionStatusOpenCode, "child_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_1": domain.PermissionStatusOpenCode, }, expectedError: nil, }, { name: "Role permission not found", rolePermissions: []entity.RolePermission{ {PermissionID: 99}, // non-existent permission }, expectedResult: nil, expectedError: fmt.Errorf("permission with ID %d not found", 99), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tree.GetRolePermissionTree(tt.rolePermissions) // 檢查返回結果 if !reflect.DeepEqual(result, tt.expectedResult) { t.Errorf("expected %v, got %v", tt.expectedResult, result) } // 檢查錯誤 if (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) { t.Errorf("expected error %v, got %v", tt.expectedError, err) } }) } } func BenchmarkGetAllParentPermissionIDs(b *testing.B) { tree := NewPermissionTree() // 添加節點 tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) permissions := domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = tree.GetAllParentPermissionIDs(permissions) } } func BenchmarkGetAllParentPermissionStatuses(b *testing.B) { tree := NewPermissionTree() // 添加節點 tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) permissions := domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusOpenCode, } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = tree.GetAllParentPermissionStatuses(permissions) } } func BenchmarkIsParentPermissionOpen(b *testing.B) { tree := NewPermissionTree() // 添加節點 tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) tree.AddPermission(2, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) permissions := domain.Permissions{ "grandchild_permission_1": domain.PermissionStatusOpenCode, "grandchild_permission_2": domain.PermissionStatusCloseCode, } node, _ := tree.FindPermissionByID(2) b.ResetTimer() for i := 0; i < b.N; i++ { _ = tree.isParentPermissionOpen(node, permissions) } } func BenchmarkGetRolePermissionTree(b *testing.B) { tree := NewPermissionTree() // 添加節點 tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) tree.AddPermission(3, entity.Permission{ID: 5, Name: "grandchild_permission_2"}) rolePermissions := []entity.RolePermission{ {PermissionID: 4}, // grandchild_permission_1 {PermissionID: 5}, // grandchild_permission_2 } b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = tree.GetRolePermissionTree(rolePermissions) } } func BenchmarkBuildNodePath(b *testing.B) { tree := NewPermissionTree() // 添加節點 tree.AddPermission(0, entity.Permission{ID: 1, Name: "root_permission"}) tree.AddPermission(1, entity.Permission{ID: 2, Name: "child_permission_1"}) tree.AddPermission(1, entity.Permission{ID: 3, Name: "child_permission_2"}) tree.AddPermission(2, entity.Permission{ID: 4, Name: "grandchild_permission_1"}) node, _ := tree.FindPermissionByID(4) b.ResetTimer() for i := 0; i < b.N; i++ { tree.buildNodePath(node, 4) } }