package repository import ( "context" "fmt" "testing" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/permission" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/repository" mgo "code.30cm.net/digimon/library-go/mongo" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson/primitive" ) func SetupTestPermissionRepository(db string) (repository.PermissionRepository, func(), error) { h, p, tearDown, err := startMongoContainer() if err != nil { return nil, nil, err } conf := &mgo.Conf{ Schema: Schema, Host: fmt.Sprintf("%s:%s", h, p), Database: db, MaxStaleness: 300, MaxPoolSize: 100, MinPoolSize: 100, MaxConnIdleTime: 300, Compressors: []string{}, EnableStandardReadWriteSplitMode: false, ConnectTimeoutMs: 3000, } param := PermissionRepositoryParam{ Conf: conf, } repo := NewPermissionRepository(param) _, _ = repo.Index20250214UP(context.Background()) return repo, tearDown, nil } // 測試 Insert func TestPermissionRepository_Insert(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() testCases := []struct { name string input entity.Permission expectErr bool }{ { name: "成功插入", input: entity.Permission{ Name: "test-permission", HTTPMethod: "GET", HTTPPath: "/test", Status: 1, Type: permission.BackendUser, }, expectErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := repo.Insert(context.Background(), tc.input) if tc.expectErr { assert.NotNil(t, err, "應該要返回錯誤") } else { assert.Nil(t, err, "不應該返回錯誤") } }) } } func TestPermissionRepository_Update(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() // 先插入一條測試數據 existingPermission := entity.Permission{ Name: "original-permission", HTTPMethod: "GET", HTTPPath: "/original", Status: 1, Type: permission.BackendUser, } err = repo.Insert(context.Background(), existingPermission) assert.Nil(t, err, "插入初始數據失敗") // 取得剛插入的 ID found, err := repo.FindOne(context.Background(), repository.PermissionQuery{ HTTPMethod: existingPermission.HTTPMethod, HTTPPath: existingPermission.HTTPPath, }) assert.Nil(t, err, "應該能找到插入的權限") id := found.ID.Hex() testCases := []struct { name string id string updateReq repository.UpdatePermission expectErr bool }{ { name: "成功更新名稱", id: id, updateReq: repository.UpdatePermission{ Name: ToPointer("updated-name"), }, expectErr: false, }, { name: "成功更新 HTTP 方法", id: id, updateReq: repository.UpdatePermission{ HTTPMethod: ToPointer("PUT"), }, expectErr: false, }, { name: "成功更新 HTTP 路徑", id: id, updateReq: repository.UpdatePermission{ HTTPPath: ToPointer("/updated-path"), }, expectErr: false, }, { name: "成功更新狀態", id: id, updateReq: repository.UpdatePermission{ Status: ToPointer(permission.Open), }, expectErr: false, }, { name: "成功更新多個欄位", id: id, updateReq: repository.UpdatePermission{ Name: ToPointer("multi-updated"), HTTPMethod: ToPointer("PATCH"), HTTPPath: ToPointer("/multi-update"), Status: ToPointer(permission.Close), }, expectErr: false, }, { name: "更新失敗 - 錯誤的 ObjectID", id: "invalid", updateReq: repository.UpdatePermission{ Name: ToPointer("invalid-update"), }, expectErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := repo.Update(context.Background(), tc.id, tc.updateReq) if tc.expectErr { assert.NotNil(t, err, "應該要返回錯誤") } else { assert.Nil(t, err, "不應該返回錯誤") // 確保更新後的資料正確 updated, err := repo.GetAll(context.Background(), nil) assert.Nil(t, err, "應該能找到更新後的權限") if tc.updateReq.Name != nil { assert.Equal(t, *tc.updateReq.Name, updated[0].Name, "名稱應該被更新") } if tc.updateReq.HTTPMethod != nil { assert.Equal(t, *tc.updateReq.HTTPMethod, updated[0].HTTPMethod, "HTTP 方法應該被更新") } if tc.updateReq.HTTPPath != nil { assert.Equal(t, *tc.updateReq.HTTPPath, updated[0].HTTPPath, "HTTP 路徑應該被更新") } if tc.updateReq.Status != nil { assert.Equal(t, *tc.updateReq.Status, updated[0].Status, "狀態應該被更新") } } }) } } // 測試 Delete 方法 func TestPermissionRepository_Delete(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() // 插入測試數據 permission := entity.Permission{ ID: primitive.NewObjectID(), Name: "delete-test", HTTPMethod: "DELETE", HTTPPath: "/test-delete", Status: 1, } err = repo.Insert(context.Background(), permission) assert.NoError(t, err, "插入測試數據時不應發生錯誤") testCases := []struct { name string id string expectErr bool }{ { name: "成功刪除存在的權限", id: permission.ID.Hex(), expectErr: false, }, { name: "刪除不存在的權限", id: primitive.NewObjectID().Hex(), expectErr: false, // MongoDB 刪除不存在的 ID 仍然不會報錯 }, { name: "刪除無效的 ObjectID", id: "invalid-object-id", expectErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := repo.Delete(context.Background(), tc.id) if tc.expectErr { assert.Error(t, err, "應該返回錯誤") } else { assert.NoError(t, err, "不應該返回錯誤") } }) } } // 測試 GetAll 方法 func TestPermissionRepository_GetAll(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() // 插入測試數據 testPermissions := []entity.Permission{ { ID: primitive.NewObjectID(), Name: "read", HTTPMethod: "GET", HTTPPath: "/read", Status: permission.Open, }, { ID: primitive.NewObjectID(), Name: "write", HTTPMethod: "POST", HTTPPath: "/write", Status: permission.Close, }, } for _, p := range testPermissions { err := repo.Insert(context.Background(), p) assert.NoError(t, err, "插入測試數據時不應該發生錯誤") } testCases := []struct { name string status *permission.Status expectLen int expectErr bool }{ { name: "查詢所有權限", status: nil, expectLen: 2, expectErr: false, }, { name: "查詢開啟的權限", status: ToPointer(permission.Open), expectLen: 1, expectErr: false, }, { name: "查詢關閉的權限", status: ToPointer(permission.Close), expectLen: 1, expectErr: false, }, { name: "查詢不存在的權限狀態", status: ToPointer(permission.Status(-1)), expectLen: 0, expectErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := repo.GetAll(context.Background(), tc.status) if tc.expectErr { assert.Error(t, err, "應該返回錯誤") } else { assert.NoError(t, err, "不應該返回錯誤") assert.Len(t, result, tc.expectLen, "查詢結果數量不符合預期") } }) } } // 測試 GetAllIntoIDMap 方法 func TestPermissionRepository_GetAllIntoIDMap(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() // 插入測試數據 testPermissions := []entity.Permission{ { ID: primitive.NewObjectID(), Name: "read", HTTPMethod: "GET", HTTPPath: "/read", Status: permission.Open, }, { ID: primitive.NewObjectID(), Name: "write", HTTPMethod: "POST", HTTPPath: "/write", Status: permission.Close, }, } for _, p := range testPermissions { err := repo.Insert(context.Background(), p) assert.NoError(t, err, "插入測試數據時不應該發生錯誤") } testCases := []struct { name string status *permission.Status expectLen int expectErr bool }{ { name: "查詢所有權限並轉為 Map", status: nil, expectLen: 2, expectErr: false, }, { name: "查詢開啟的權限並轉為 Map", status: ToPointer(permission.Open), expectLen: 1, expectErr: false, }, { name: "查詢關閉的權限並轉為 Map", status: ToPointer(permission.Close), expectLen: 1, expectErr: false, }, { name: "查詢不存在的權限狀態並轉為 Map", status: ToPointer(permission.Status(-1)), expectLen: 0, expectErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := repo.GetAllIntoIDMap(context.Background(), tc.status) if tc.expectErr { assert.Error(t, err, "應該返回錯誤") } else { assert.NoError(t, err, "不應該返回錯誤") assert.Len(t, result, tc.expectLen, "查詢結果數量不符合預期") // 確保 Map 的 key 是權限名稱 for key, permission := range result { assert.Equal(t, key, permission.Name, "Map 的 Key 應該與 Name 相符") } } }) } } func TestPermissionRepository_FindByNames(t *testing.T) { repo, tearDown, err := SetupTestPermissionRepository("testDB") assert.NoError(t, err) defer tearDown() // 測試數據 testPermissions := []entity.Permission{ { ID: primitive.NewObjectID(), Name: "read-data", HTTPMethod: "GET", HTTPPath: "/data", Status: 1, }, { ID: primitive.NewObjectID(), Name: "write-data", HTTPMethod: "POST", HTTPPath: "/data", Status: 1, }, } // 插入測試數據 for _, perm := range testPermissions { err := repo.Insert(context.Background(), perm) assert.NoError(t, err) } testCases := []struct { name string input []string expectLen int expectErr bool }{ { name: "成功查詢單個名稱", input: []string{"read-data"}, expectLen: 1, expectErr: false, }, { name: "成功查詢多個名稱", input: []string{"read-data", "write-data"}, expectLen: 2, expectErr: false, }, { name: "查詢名稱不存在時應返回空結果", input: []string{"unknown-permission"}, expectLen: 0, expectErr: false, }, { name: "當查詢發生錯誤時,應返回錯誤", input: nil, // 無效查詢 expectLen: 0, expectErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, err := repo.FindByNames(context.Background(), tc.input) if tc.expectErr { assert.Error(t, err, "應該返回錯誤") } else { assert.NoError(t, err, "不應該返回錯誤") assert.Len(t, result, tc.expectLen, "返回的數據長度應符合預期") } }) } } func ToPointer[T any](v T) *T { return &v }