package unit import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/middleware" "github.com/break/junhong_cmp_fiber/tests/testutils" ) // TestDataPermissionScope_RootUser 测试 root 用户跳过数据权限过滤 func TestDataPermissionScope_RootUser(t *testing.T) { db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) accountStore := postgres.NewAccountStore(db, redisClient) ctx := context.Background() // 创建 root 用户 rootUser := &model.Account{ Username: "root_user", Phone: "13800000000", Password: "hashed_password", UserType: constants.UserTypeRoot, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(rootUser).Error) // 创建测试数据表(模拟业务表) type TestData struct { ID uint `gorm:"primarykey"` Name string OwnerID uint ShopID uint } require.NoError(t, db.AutoMigrate(&TestData{})) // 插入测试数据(不同的 owner_id 和 shop_id) testData := []TestData{ {Name: "data1", OwnerID: 1, ShopID: 100}, {Name: "data2", OwnerID: 2, ShopID: 200}, {Name: "data3", OwnerID: 3, ShopID: 300}, } require.NoError(t, db.Create(&testData).Error) // 设置 root 用户上下文 ctxWithRoot := middleware.SetUserContext(ctx, rootUser.ID, constants.UserTypeRoot, 100) // 查询(应该返回所有数据,不过滤) var results []TestData err := db.WithContext(ctxWithRoot). Scopes(postgres.DataPermissionScope(ctxWithRoot, accountStore)). Find(&results).Error require.NoError(t, err) assert.Len(t, results, 3, "root 用户应该看到所有数据") } // TestDataPermissionScope_NormalUser 测试普通用户数据权限过滤 func TestDataPermissionScope_NormalUser(t *testing.T) { db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) accountStore := postgres.NewAccountStore(db, redisClient) ctx := context.Background() // 创建账号层级: A -> B accountA := &model.Account{ Username: "user_a", Phone: "13800000001", Password: "hashed_password", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(accountA).Error) shopIDA := uint(100) accountA.ShopID = &shopIDA require.NoError(t, db.Save(accountA).Error) accountB := &model.Account{ Username: "user_b", Phone: "13800000002", Password: "hashed_password", UserType: constants.UserTypeAgent, ParentID: &accountA.ID, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(accountB).Error) shopIDB := uint(100) accountB.ShopID = &shopIDB require.NoError(t, db.Save(accountB).Error) // 创建测试数据表 type TestData struct { ID uint `gorm:"primarykey"` Name string OwnerID uint ShopID uint } require.NoError(t, db.AutoMigrate(&TestData{})) // 插入测试数据 testData := []TestData{ {Name: "data_a", OwnerID: accountA.ID, ShopID: 100}, // A 的数据 {Name: "data_b", OwnerID: accountB.ID, ShopID: 100}, // B 的数据 {Name: "data_c", OwnerID: 999, ShopID: 100}, // 其他用户数据(同店铺) {Name: "data_d", OwnerID: accountA.ID, ShopID: 200}, // A 的数据(不同店铺) } require.NoError(t, db.Create(&testData).Error) // A 登录查询(应该看到 A 和 B 的数据,同店铺) ctxWithA := middleware.SetUserContext(ctx, accountA.ID, constants.UserTypePlatform, 100) var resultsA []TestData err := db.WithContext(ctxWithA). Scopes(postgres.DataPermissionScope(ctxWithA, accountStore)). Find(&resultsA).Error require.NoError(t, err) assert.Len(t, resultsA, 2, "A 应该看到自己和下级 B 的数据") // B 登录查询(只能看到自己的数据) ctxWithB := middleware.SetUserContext(ctx, accountB.ID, constants.UserTypeAgent, 100) var resultsB []TestData err = db.WithContext(ctxWithB). Scopes(postgres.DataPermissionScope(ctxWithB, accountStore)). Find(&resultsB).Error require.NoError(t, err) assert.Len(t, resultsB, 1, "B 只能看到自己的数据") assert.Equal(t, "data_b", resultsB[0].Name) } // TestDataPermissionScope_ShopIsolation 测试店铺隔离 func TestDataPermissionScope_ShopIsolation(t *testing.T) { db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) accountStore := postgres.NewAccountStore(db, redisClient) ctx := context.Background() // 创建两个账号(同一层级,不同店铺) shopID100 := uint(100) accountA := &model.Account{ Username: "user_a", Phone: "13800000001", Password: "hashed_password", UserType: constants.UserTypePlatform, ShopID: &shopID100, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(accountA).Error) shopID200 := uint(200) accountB := &model.Account{ Username: "user_b", Phone: "13800000002", Password: "hashed_password", UserType: constants.UserTypePlatform, ShopID: &shopID200, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(accountB).Error) // 创建测试数据表 type TestData struct { ID uint `gorm:"primarykey"` Name string OwnerID uint ShopID uint } require.NoError(t, db.AutoMigrate(&TestData{})) // 插入测试数据 testData := []TestData{ {Name: "data_shop100", OwnerID: accountA.ID, ShopID: 100}, {Name: "data_shop200", OwnerID: accountB.ID, ShopID: 200}, } require.NoError(t, db.Create(&testData).Error) // A 登录查询(只能看到店铺 100 的数据) ctxWithA := middleware.SetUserContext(ctx, accountA.ID, constants.UserTypePlatform, 100) var resultsA []TestData err := db.WithContext(ctxWithA). Scopes(postgres.DataPermissionScope(ctxWithA, accountStore)). Find(&resultsA).Error require.NoError(t, err) assert.Len(t, resultsA, 1, "A 只能看到店铺 100 的数据") assert.Equal(t, "data_shop100", resultsA[0].Name) // B 登录查询(只能看到店铺 200 的数据) ctxWithB := middleware.SetUserContext(ctx, accountB.ID, constants.UserTypePlatform, 200) var resultsB []TestData err = db.WithContext(ctxWithB). Scopes(postgres.DataPermissionScope(ctxWithB, accountStore)). Find(&resultsB).Error require.NoError(t, err) assert.Len(t, resultsB, 1, "B 只能看到店铺 200 的数据") assert.Equal(t, "data_shop200", resultsB[0].Name) } // TestDataPermissionScope_NoUserContext 测试无用户上下文时不过滤 func TestDataPermissionScope_NoUserContext(t *testing.T) { db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) accountStore := postgres.NewAccountStore(db, redisClient) ctx := context.Background() // 创建测试数据表 type TestData struct { ID uint `gorm:"primarykey"` Name string OwnerID uint ShopID uint } require.NoError(t, db.AutoMigrate(&TestData{})) // 插入测试数据 testData := []TestData{ {Name: "data1", OwnerID: 1, ShopID: 100}, {Name: "data2", OwnerID: 2, ShopID: 200}, } require.NoError(t, db.Create(&testData).Error) // 使用没有用户信息的上下文查询(不过滤,可能是系统任务) var results []TestData err := db.WithContext(ctx). Scopes(postgres.DataPermissionScope(ctx, accountStore)). Find(&results).Error require.NoError(t, err) assert.Len(t, results, 0, "无用户上下文时应该返回空数据(根据 scopes.go 的实现)") } // TestDataPermissionScope_ErrorHandling 测试查询下级 ID 失败时的降级策略 func TestDataPermissionScope_ErrorHandling(t *testing.T) { db, redisClient := testutils.SetupTestDB(t) defer testutils.TeardownTestDB(t, db, redisClient) accountStore := postgres.NewAccountStore(db, redisClient) ctx := context.Background() // 创建测试账号 accountA := &model.Account{ Username: "user_a", Phone: "13800000001", Password: "hashed_password", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, Creator: 1, Updater: 1, } require.NoError(t, db.Create(accountA).Error) shopIDA := uint(100) accountA.ShopID = &shopIDA require.NoError(t, db.Save(accountA).Error) // 创建测试数据表 type TestData struct { ID uint `gorm:"primarykey"` Name string OwnerID uint ShopID uint } require.NoError(t, db.AutoMigrate(&TestData{})) // 插入测试数据 testData := []TestData{ {Name: "data_a", OwnerID: accountA.ID, ShopID: 100}, {Name: "data_b", OwnerID: 999, ShopID: 100}, } require.NoError(t, db.Create(&testData).Error) // 关闭 Redis 连接以模拟错误(递归查询失败) redisClient.Close() // 使用 A 的上下文查询(降级策略:只返回自己的数据) ctxWithA := middleware.SetUserContext(ctx, accountA.ID, constants.UserTypePlatform, 100) var resultsA []TestData err := db.WithContext(ctxWithA). Scopes(postgres.DataPermissionScope(ctxWithA, accountStore)). Find(&resultsA).Error require.NoError(t, err) // 降级策略应该只返回自己的数据 assert.Len(t, resultsA, 1, "查询下级 ID 失败时,应该降级为只返回自己的数据") assert.Equal(t, "data_a", resultsA[0].Name) }