package usecase import ( "sync" "testing" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/permission" "go.mongodb.org/mongo-driver/bson/primitive" ) // TestGeneratePermissionTree 測試 GeneratePermissionTree 函數的建立樹功能 func TestGeneratePermissionTree(t *testing.T) { // 準備測試資料 // p1 為根節點(Parent 為空) p1ID := primitive.NewObjectID() p1 := entity.Permission{ ID: p1ID, Parent: "", Name: "A", } // p2 的父節點為 p1 p2ID := primitive.NewObjectID() p2 := entity.Permission{ ID: p2ID, Parent: p1ID.Hex(), Name: "B", } // p3 為另一個根節點(Parent 為空) p3ID := primitive.NewObjectID() p3 := entity.Permission{ ID: p3ID, Parent: "", Name: "C", } // p4 的 Parent 填寫一個不存在的 id,預期會掛在 dummy root 下 p4ID := primitive.NewObjectID() p4 := entity.Permission{ ID: p4ID, Parent: "nonexistent", Name: "D", } permissions := []entity.Permission{p1, p2, p3, p4} // 建立樹 tree := GeneratePermissionTree(permissions) // 驗證 dummy root 下的子節點 // 預期 p1、p3、p4 均掛在 dummy root 下 if len(tree.root.Children) != 3 { t.Errorf("expected 3 children under dummy root, got %d", len(tree.root.Children)) } // 驗證 p1 的節點是否正確 nodeP1, ok := tree.nodes[p1ID.Hex()] if !ok { t.Errorf("node for permission A (p1) not found") } else { // p1 應該有一個子節點 p2 if len(nodeP1.Children) != 1 { t.Errorf("expected node A to have 1 child, got %d", len(nodeP1.Children)) } else { if nodeP1.Children[0].Data.ID != p2ID { t.Errorf("expected node A child to be permission B (p2), got %v", nodeP1.Children[0].Data.ID.Hex()) } } } // 驗證 tree.names 的對應關係 checkNames := []struct { name string expectedID string }{ {"A", p1ID.Hex()}, {"B", p2ID.Hex()}, {"C", p3ID.Hex()}, {"D", p4ID.Hex()}, } for _, cn := range checkNames { ids, ok := tree.names[cn.name] if !ok { t.Errorf("name mapping for %s not found", cn.name) continue } if len(ids) != 1 || ids[0] != cn.expectedID { t.Errorf("expected name mapping for %s to be [%s], got %v", cn.name, cn.expectedID, ids) } } } func TestGetNode(t *testing.T) { // 建立一個測試用的 PermissionTree tree := &PermissionTree{ nodes: make(map[string]*PermissionNode), names: make(map[string][]string), } id := primitive.NewObjectID() // 建立一個測試節點,ID 為 "testID" perm := entity.Permission{ ID: id, Name: "Test Permission", } node := &PermissionNode{ Data: perm, } // 將測試節點插入 tree 的 nodes map tree.mu.Lock() tree.nodes["testID"] = node tree.mu.Unlock() // 測試 getNode 返回存在的節點 got := tree.getNode("testID") if got == nil { t.Error("Expected to find node with id 'testID', but got nil") } else if got.Data.ID.Hex() != id.Hex() { t.Errorf("Expected node ID 'testID', got '%s'", got.Data.ID) } // 測試對不存在的 id,應回傳 nil gotNil := tree.getNode("nonexistent") if gotNil != nil { t.Errorf("Expected nil for non-existent node, got %+v", gotNil) } } func TestPut(t *testing.T) { // 建立一個 PermissionTree,並初始化 dummy root tree := &PermissionTree{ nodes: make(map[string]*PermissionNode), names: make(map[string][]string), } // 建立 dummy root 節點,其 ID 為一個隨機 ObjectID,但不參與 mapping dummyRootPerm := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", Name: "root", } tree.root = &PermissionNode{ Data: dummyRootPerm, Children: make([]*PermissionNode, 0), } // 測試 1:放入一筆 Parent 為空的節點,預期掛在 dummy root 下 permA := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", // 無父節點 Name: "A", } tree.put(permA) nodeA := tree.getNode(permA.ID.Hex()) if nodeA == nil { t.Errorf("Expected to find node A in tree.nodes") } if nodeA.Parent != tree.root { t.Errorf("Expected node A's parent to be dummy root, got %v", nodeA.Parent.Data.Name) } if ids, ok := tree.names["A"]; !ok || len(ids) != 1 || ids[0] != permA.ID.Hex() { t.Errorf("Expected tree.names for 'A' to contain %s, got %v", permA.ID.Hex(), tree.names["A"]) } // 測試 2:放入一筆 Parent 為存在節點的節點 // 先放入父節點 permB permB := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", // 掛在 dummy root 下 Name: "B", } tree.put(permB) nodeB := tree.getNode(permB.ID.Hex()) if nodeB == nil { t.Errorf("Expected to find node B in tree.nodes") } // 再放入子節點 permC,其 Parent 為 permB.ID.Hex() permC := entity.Permission{ ID: primitive.NewObjectID(), Parent: permB.ID.Hex(), Name: "C", } tree.put(permC) nodeC := tree.getNode(permC.ID.Hex()) if nodeC == nil { t.Errorf("Expected to find node C in tree.nodes") } if nodeC.Parent != nodeB { t.Errorf("Expected node C's parent to be node B") } // 驗證 nodeB 的 Children 是否包含 nodeC found := false for _, child := range nodeB.Children { if child.Data.ID == permC.ID { found = true break } } if !found { t.Errorf("Expected node B's children to contain node C") } } func TestFilterOpenNodes(t *testing.T) { // 建立一個 PermissionTree,初始化 nodes 與 names,並建立 dummy root 節點 tree := &PermissionTree{ nodes: make(map[string]*PermissionNode), names: make(map[string][]string), mu: sync.RWMutex{}, } // 建立 dummy root 節點 dummyRootPerm := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", Name: "root", Status: permission.Open, // dummy root 狀態不影響結果 } tree.root = &PermissionNode{ Data: dummyRootPerm, Children: make([]*PermissionNode, 0), } // 建立測試節點 // Node A:Open, leaf permA := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", // 無父節點 → 掛在 dummy root 下 Name: "A", Status: permission.Open, } tree.put(permA) // Node B:Open, non-leaf permB := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", // 掛在 dummy root 下 Name: "B", Status: permission.Open, } tree.put(permB) // Node B1:Open, leaf, Parent = B permB1 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permB.ID.Hex(), Name: "B1", Status: permission.Open, } tree.put(permB1) // Node B2:Closed, leaf, Parent = B permB2 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permB.ID.Hex(), Name: "B2", Status: permission.Close, } tree.put(permB2) // Node C:Open, non-leaf,但其子節點皆 Closed → C 不會展開 permC := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", // 掛在 dummy root 下 Name: "C", Status: permission.Close, } tree.put(permC) // Node C1:Closed, leaf, Parent = C permC1 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permC.ID.Hex(), Name: "C1", Status: permission.Open, } tree.put(permC1) // Node C2:Closed, leaf, Parent = C permC2 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permC.ID.Hex(), Name: "C2", Status: permission.Open, } tree.put(permC2) // Node D:Closed, leaf, 無父節點 permD := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", Name: "D", Status: permission.Close, } tree.put(permD) // Node E:Closed, leaf, 無父節點 permE := entity.Permission{ ID: primitive.NewObjectID(), Parent: "", Name: "E", Status: permission.Open, } tree.put(permE) // Node E1:Closed, leaf, Parent = E permE1 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permE.ID.Hex(), Name: "E1", Status: permission.Close, } tree.put(permE1) // Node E2:Closed, leaf, Parent = E permE2 := entity.Permission{ ID: primitive.NewObjectID(), Parent: permE.ID.Hex(), Name: "E2", Status: permission.Close, } tree.put(permE2) // 執行 filterOpenNodes openNodes, err := tree.filterOpenNodes() if err != nil { t.Fatalf("filterOpenNodes returned error: %v", err) } // 預期結果: // - Node A 應該包含(open 且為葉節點) // - Node B 應該包含(open 且其子節點 B1 為 open) // - Node B1 應該包含(open, leaf) // - Node B2 不包含(closed) // - Node C 不包含(closed) // - Node C1, C2 不包含(C Node Close) // - Node D 不包含(closed) // - Node E 包含(open) // - Node E1, E2 不包含(本身Close) expectedIDs := map[string]bool{ permA.ID.Hex(): true, permB.ID.Hex(): true, permB1.ID.Hex(): true, permE.ID.Hex(): true, } // 檢查結果是否只包含預期的節點 for id, perm := range openNodes { if !expectedIDs[id] { t.Errorf("Unexpected node in openNodes: id=%s, name=%s", id, perm.Name) } delete(expectedIDs, id) } if len(expectedIDs) != 0 { t.Errorf("Expected nodes not found in openNodes: %v", expectedIDs) } }