360 lines
8.9 KiB
Go
360 lines
8.9 KiB
Go
|
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)
|
|||
|
}
|
|||
|
}
|