package repository import ( "backend/pkg/permission/domain" "backend/pkg/permission/domain/entity" "backend/pkg/permission/domain/permission" domainRepo "backend/pkg/permission/domain/repository" "context" "fmt" "github.com/alicebob/miniredis/v2" "github.com/zeromicro/go-zero/core/stores/redis" "testing" "time" mgo "backend/pkg/library/mongo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeromicro/go-zero/core/stores/cache" "go.mongodb.org/mongo-driver/v2/bson" ) func setupPermissionRepo(db string) (domainRepo.PermissionRepository, func(), error) { h, p, tearDown, err := startMongoContainer() if err != nil { return nil, nil, err } s, _ := miniredis.Run() conf := &mgo.Conf{ Schema: mongoSchema, Host: fmt.Sprintf("%s:%s", h, p), Database: db, MaxStaleness: 300, MaxPoolSize: 100, MinPoolSize: 100, MaxConnIdleTime: 300, Compressors: []string{}, EnableStandardReadWriteSplitMode: false, ConnectTimeoutMs: 3000, } cacheConf := cache.CacheConf{ cache.NodeConf{ RedisConf: redis.RedisConf{ Host: s.Addr(), Type: redis.NodeType, }, Weight: 100, }, } cacheOpts := []cache.Option{ cache.WithExpiry(1000 * time.Microsecond), cache.WithNotFoundExpiry(1000 * time.Microsecond), } param := PermissionRepositoryParam{ Conf: conf, CacheConf: cacheConf, CacheOpts: cacheOpts, } repo := NewPermissionRepository(param) _, _ = repo.Index20251009001UP(context.Background()) return repo, tearDown, nil } func TestPermissionRepository_FindOne(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testPerm := &entity.Permission{ ID: bson.NewObjectID(), Name: "test.permission", State: domain.RecordActive, Type: permission.TypeBackend, } testPerm.CreateTime = time.Now().Unix() testPerm.UpdateTime = testPerm.CreateTime // 插入測試數據 _, err = repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, testPerm) require.NoError(t, err) tests := []struct { name string id string wantErr error check func(*testing.T, *entity.Permission) }{ { name: "找到存在的權限", id: testPerm.ID.Hex(), wantErr: nil, check: func(t *testing.T, perm *entity.Permission) { assert.Equal(t, testPerm.Name, perm.Name) assert.Equal(t, testPerm.State, perm.State) }, }, { name: "無效的 ObjectID", id: "invalid-id", wantErr: ErrInvalidObjectID, check: nil, }, { name: "不存在的權限", id: bson.NewObjectID().Hex(), wantErr: ErrNotFound, check: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { perm, err := repo.FindOne(ctx, tt.id) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, perm) } else { assert.NoError(t, err) assert.NotNil(t, perm) if tt.check != nil { tt.check(t, perm) } } }) } } func TestPermissionRepository_FindByName(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testPerms := []*entity.Permission{ { ID: bson.NewObjectID(), Name: "user.list", State: domain.RecordActive, Type: permission.TypeBackend, }, { ID: bson.NewObjectID(), Name: "user.create", State: domain.RecordActive, Type: permission.TypeBackend, }, } for _, perm := range testPerms { perm.CreateTime = time.Now().Unix() perm.UpdateTime = perm.CreateTime _, err := repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, perm) require.NoError(t, err) } tests := []struct { name string permName string wantErr error wantName string }{ { name: "找到存在的權限", permName: "user.list", wantErr: nil, wantName: "user.list", }, { name: "找到另一個權限", permName: "user.create", wantErr: nil, wantName: "user.create", }, { name: "不存在的權限", permName: "user.delete", wantErr: ErrNotFound, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { perm, err := repo.FindByName(ctx, tt.permName) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, perm) } else { assert.NoError(t, err) assert.NotNil(t, perm) assert.Equal(t, tt.wantName, perm.Name) } }) } } func TestPermissionRepository_GetByNames(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testPerms := []*entity.Permission{ {ID: bson.NewObjectID(), Name: "user.list", State: domain.RecordActive}, {ID: bson.NewObjectID(), Name: "user.create", State: domain.RecordActive}, {ID: bson.NewObjectID(), Name: "user.update", State: domain.RecordActive}, } for _, perm := range testPerms { perm.CreateTime = time.Now().Unix() perm.UpdateTime = perm.CreateTime _, err := repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, perm) require.NoError(t, err) } tests := []struct { name string names []string wantCount int wantErr error }{ { name: "找到多個權限", names: []string{"user.list", "user.create"}, wantCount: 2, wantErr: nil, }, { name: "找到單一權限", names: []string{"user.update"}, wantCount: 1, wantErr: nil, }, { name: "找到所有權限", names: []string{"user.list", "user.create", "user.update"}, wantCount: 3, wantErr: nil, }, { name: "部分存在的權限", names: []string{"user.list", "user.delete"}, wantCount: 1, wantErr: nil, }, { name: "不存在的權限", names: []string{"admin.super"}, wantCount: 0, wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { perms, err := repo.GetByNames(ctx, tt.names) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) } else { assert.NoError(t, err) assert.Len(t, perms, tt.wantCount) } }) } } func TestPermissionRepository_FindByHTTP(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testPerms := []*entity.Permission{ { ID: bson.NewObjectID(), Name: "user.list", HTTPPath: "/api/users", HTTPMethod: "GET", State: domain.RecordActive, }, { ID: bson.NewObjectID(), Name: "user.create", HTTPPath: "/api/users", HTTPMethod: "POST", State: domain.RecordActive, }, } for _, perm := range testPerms { perm.CreateTime = time.Now().Unix() perm.UpdateTime = perm.CreateTime _, err := repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, perm) require.NoError(t, err) } tests := []struct { name string path string method string wantName string wantErr error }{ { name: "找到 GET 權限", path: "/api/users", method: "GET", wantName: "user.list", wantErr: nil, }, { name: "找到 POST 權限", path: "/api/users", method: "POST", wantName: "user.create", wantErr: nil, }, { name: "不存在的路徑", path: "/api/admin", method: "GET", wantErr: ErrNotFound, }, { name: "不存在的方法", path: "/api/users", method: "DELETE", wantErr: ErrNotFound, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { perm, err := repo.FindByHTTP(ctx, tt.path, tt.method) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, perm) } else { assert.NoError(t, err) assert.NotNil(t, perm) assert.Equal(t, tt.wantName, perm.Name) assert.Equal(t, tt.path, perm.HTTPPath) assert.Equal(t, tt.method, perm.HTTPMethod) } }) } } func TestPermissionRepository_List(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 parent := &entity.Permission{ ID: bson.NewObjectID(), ParentID: bson.ObjectID{}, Name: "user", State: domain.RecordActive, Type: permission.TypeBackend, } parent.CreateTime = time.Now().Unix() parent.UpdateTime = parent.CreateTime child := &entity.Permission{ ID: bson.NewObjectID(), ParentID: parent.ID, Name: "user.list", State: domain.RecordActive, Type: permission.TypeBackend, } child.CreateTime = time.Now().Unix() child.UpdateTime = child.CreateTime inactiveChild := &entity.Permission{ ID: bson.NewObjectID(), ParentID: parent.ID, Name: "user.delete", State: domain.RecordInactive, Type: permission.TypeBackend, } inactiveChild.CreateTime = time.Now().Unix() inactiveChild.UpdateTime = inactiveChild.CreateTime for _, perm := range []*entity.Permission{parent, child, inactiveChild} { _, err := repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, perm) require.NoError(t, err) } tests := []struct { name string filter domainRepo.PermissionFilter wantCount int wantNames []string }{ { name: "列出所有權限", filter: domainRepo.PermissionFilter{}, wantCount: 3, }, { name: "只列出啟用的權限", filter: domainRepo.PermissionFilter{ Status: func() *permission.RecordState { s := domain.RecordActive return &s }(), }, wantCount: 2, wantNames: []string{"user", "user.list"}, }, { name: "只列出停用的權限", filter: domainRepo.PermissionFilter{ Status: func() *permission.RecordState { s := domain.RecordInactive return &s }(), }, wantCount: 1, wantNames: []string{"user.delete"}, }, { name: "按類型過濾", filter: domainRepo.PermissionFilter{ Type: func() *permission.Type { t := permission.TypeBackend return &t }(), }, wantCount: 3, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { perms, err := repo.List(ctx, tt.filter) assert.NoError(t, err) assert.Len(t, perms, tt.wantCount) if len(tt.wantNames) > 0 { names := make([]string, len(perms)) for i, p := range perms { names[i] = p.Name } for _, wantName := range tt.wantNames { assert.Contains(t, names, wantName) } } }) } } func TestPermissionRepository_ListActive(t *testing.T) { repo, tearDown, err := setupPermissionRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 activePerms := []*entity.Permission{ {ID: bson.NewObjectID(), Name: "user.list", State: domain.RecordActive}, {ID: bson.NewObjectID(), Name: "user.create", State: domain.RecordActive}, } inactivePerms := []*entity.Permission{ {ID: bson.NewObjectID(), Name: "user.delete", State: domain.RecordInactive}, {ID: bson.NewObjectID(), Name: "user.admin", State: domain.RecordDeleted}, } allPerms := append(activePerms, inactivePerms...) for _, perm := range allPerms { perm.CreateTime = time.Now().Unix() perm.UpdateTime = perm.CreateTime _, err := repo.(*PermissionRepository).DB.GetClient().InsertOne(ctx, perm) require.NoError(t, err) } t.Run("只返回啟用的權限", func(t *testing.T) { perms, err := repo.ListActive(ctx) assert.NoError(t, err) assert.Len(t, perms, 2) for _, perm := range perms { assert.Equal(t, domain.RecordActive, perm.State) } }) }