package model import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/mysql" "gorm.io/gorm" ) // setupOrgTestDB 创建机构测试数据库(使用MySQL) func setupOrgTestDB(t *testing.T) *gorm.DB { t.Helper() dsn := "root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) require.NoError(t, err) err = db.AutoMigrate(&Organization{}, &UserOrganization{}, &User{}, &Role{}, &Menu{}, &RoleMenu{}) require.NoError(t, err) // 清理测试数据(仅清理非种子数据,避免影响运行中的后端) db.Exec("SET FOREIGN_KEY_CHECKS = 0") db.Exec("TRUNCATE TABLE role_menu") db.Exec("TRUNCATE TABLE user_organization") db.Exec("TRUNCATE TABLE organization") // 不 TRUNCATE user/role/menu 表,因为后端种子数据在其中 // 仅删除测试创建的记录(通过后续测试自行管理) db.Exec("DELETE FROM role WHERE is_system = 0") db.Exec("DELETE FROM menu WHERE name LIKE '测试%'") db.Exec("DELETE FROM user WHERE username LIKE 'test_%'") db.Exec("SET FOREIGN_KEY_CHECKS = 1") return db } // createTestOrg 创建测试机构 func createTestOrg(t *testing.T, db *gorm.DB) *Organization { t.Helper() now := time.Now() org := &Organization{ Name: "测试机构", Code: "test_org", Leader: "张三", Phone: "13800138000", Email: "org@example.com", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now, } err := db.Create(org).Error require.NoError(t, err) return org } // ==================== Organization Tests ==================== // TestOrgInsert 测试插入机构 func TestOrgInsert(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() org := &Organization{ Name: "新机构", Code: "new_org", Leader: "李四", Phone: "13900139000", Email: "new@example.com", SortOrder: 5, Status: 1, } id, err := OrgInsert(ctx, db, org) require.NoError(t, err) assert.Greater(t, id, int64(0)) assert.Equal(t, id, org.Id) // 验证数据已保存 saved, err := OrgFindOne(ctx, db, id) require.NoError(t, err) assert.Equal(t, "新机构", saved.Name) assert.Equal(t, "new_org", saved.Code) assert.Equal(t, "李四", saved.Leader) } // TestOrgFindOne 测试根据ID查询机构 func TestOrgFindOne(t *testing.T) { db := setupOrgTestDB(t) org := createTestOrg(t, db) ctx := context.Background() found, err := OrgFindOne(ctx, db, org.Id) require.NoError(t, err) require.NotNil(t, found) assert.Equal(t, org.Id, found.Id) assert.Equal(t, "测试机构", found.Name) assert.Equal(t, "test_org", found.Code) } // TestOrgFindOne_NotFound 测试查询不存在的机构 func TestOrgFindOne_NotFound(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() found, err := OrgFindOne(ctx, db, 99999) require.Error(t, err) require.Nil(t, found) assert.Equal(t, ErrNotFound, err) } // TestOrgFindOneByCode 测试根据编码查询机构 func TestOrgFindOneByCode(t *testing.T) { db := setupOrgTestDB(t) org := createTestOrg(t, db) ctx := context.Background() found, err := OrgFindOneByCode(ctx, db, "test_org") require.NoError(t, err) require.NotNil(t, found) assert.Equal(t, org.Id, found.Id) assert.Equal(t, "测试机构", found.Name) } // TestOrgFindAll 测试查询所有启用的机构 func TestOrgFindAll(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() // 创建3个机构:2个启用,1个禁用 now := time.Now() orgs := []Organization{ {Name: "机构A", Code: "org_a", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "机构B", Code: "org_b", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "机构C", Code: "org_c", SortOrder: 3, Status: 1, CreatedAt: now, UpdatedAt: now}, } for i := range orgs { err := db.Create(&orgs[i]).Error require.NoError(t, err) } // 将机构C设为禁用(status=0是零值,GORM Create时会使用default:1,需要单独更新) db.Model(&orgs[2]).Update("status", 0) // 查询所有启用的机构 result, err := OrgFindAll(ctx, db) require.NoError(t, err) assert.Len(t, result, 2) // 验证按 sort_order 排序 assert.Equal(t, "机构B", result[0].Name) // sort_order=1 assert.Equal(t, "机构A", result[1].Name) // sort_order=2 } // TestOrgUpdate 测试更新机构 func TestOrgUpdate(t *testing.T) { db := setupOrgTestDB(t) org := createTestOrg(t, db) ctx := context.Background() // 修改机构数据 org.Name = "更新后的机构" org.Leader = "王五" err := OrgUpdate(ctx, db, org) require.NoError(t, err) // 验证更新结果 updated, err := OrgFindOne(ctx, db, org.Id) require.NoError(t, err) assert.Equal(t, "更新后的机构", updated.Name) assert.Equal(t, "王五", updated.Leader) } // TestOrgDelete 测试删除机构 func TestOrgDelete(t *testing.T) { db := setupOrgTestDB(t) org := createTestOrg(t, db) orgId := org.Id ctx := context.Background() err := OrgDelete(ctx, db, orgId) require.NoError(t, err) // 验证机构已被删除 _, err = OrgFindOne(ctx, db, orgId) require.Error(t, err) assert.Equal(t, ErrNotFound, err) } // TestOrgHasChildren 测试检查是否有子机构 func TestOrgHasChildren(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建父机构 parent := &Organization{ Name: "父机构", Code: "parent_org", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now, } err := db.Create(parent).Error require.NoError(t, err) // 创建子机构 child := &Organization{ ParentId: parent.Id, Name: "子机构", Code: "child_org", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now, } err = db.Create(child).Error require.NoError(t, err) // 创建无子机构的孤立机构 orphan := &Organization{ Name: "孤立机构", Code: "orphan_org", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now, } err = db.Create(orphan).Error require.NoError(t, err) // 验证父机构有子机构 hasChildren, err := OrgHasChildren(ctx, db, parent.Id) require.NoError(t, err) assert.True(t, hasChildren) // 验证孤立机构没有子机构 hasChildren, err = OrgHasChildren(ctx, db, orphan.Id) require.NoError(t, err) assert.False(t, hasChildren) } // ==================== UserOrganization Tests ==================== // TestUserOrgInsert 测试插入用户-机构关联 func TestUserOrgInsert(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建测试用户 user := &User{ Username: "testuser", Email: "test@example.com", Password: "encryptedpassword", Status: 1, CreatedAt: now, UpdatedAt: now, } err := db.Create(user).Error require.NoError(t, err) // 创建测试机构 org := createTestOrg(t, db) // 创建测试角色 role := &Role{ Name: "测试角色", Code: "test_role", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now, } err = db.Create(role).Error require.NoError(t, err) // 插入用户-机构关联 uo := &UserOrganization{ UserId: user.Id, OrgId: org.Id, RoleId: role.Id, } id, err := UserOrgInsert(ctx, db, uo) require.NoError(t, err) assert.Greater(t, id, int64(0)) assert.Equal(t, id, uo.Id) // 验证数据已保存 saved, err := UserOrgFindOne(ctx, db, user.Id, org.Id) require.NoError(t, err) assert.Equal(t, user.Id, saved.UserId) assert.Equal(t, org.Id, saved.OrgId) assert.Equal(t, role.Id, saved.RoleId) } // TestUserOrgFindByUserId 测试根据用户ID查询关联 func TestUserOrgFindByUserId(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建测试用户 user := &User{ Username: "testuser", Email: "test@example.com", Password: "encryptedpassword", Status: 1, CreatedAt: now, UpdatedAt: now, } err := db.Create(user).Error require.NoError(t, err) // 创建2个机构 org1 := &Organization{Name: "机构1", Code: "org_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} org2 := &Organization{Name: "机构2", Code: "org_2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(org1).Error require.NoError(t, err) err = db.Create(org2).Error require.NoError(t, err) // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(role).Error require.NoError(t, err) // 创建2条用户-机构关联 uo1 := &UserOrganization{UserId: user.Id, OrgId: org1.Id, RoleId: role.Id} uo2 := &UserOrganization{UserId: user.Id, OrgId: org2.Id, RoleId: role.Id} _, err = UserOrgInsert(ctx, db, uo1) require.NoError(t, err) _, err = UserOrgInsert(ctx, db, uo2) require.NoError(t, err) // 查询用户的所有机构关联 list, err := UserOrgFindByUserId(ctx, db, user.Id) require.NoError(t, err) assert.Len(t, list, 2) } // TestUserOrgFindByOrgId 测试根据机构ID查询关联 func TestUserOrgFindByOrgId(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建机构 org := createTestOrg(t, db) // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role).Error require.NoError(t, err) // 创建2个用户 user1 := &User{Username: "user1", Email: "user1@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now} user2 := &User{Username: "user2", Email: "user2@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(user1).Error require.NoError(t, err) err = db.Create(user2).Error require.NoError(t, err) // 创建用户-机构关联 uo1 := &UserOrganization{UserId: user1.Id, OrgId: org.Id, RoleId: role.Id} uo2 := &UserOrganization{UserId: user2.Id, OrgId: org.Id, RoleId: role.Id} _, err = UserOrgInsert(ctx, db, uo1) require.NoError(t, err) _, err = UserOrgInsert(ctx, db, uo2) require.NoError(t, err) // 查询机构的所有成员 list, err := UserOrgFindByOrgId(ctx, db, org.Id) require.NoError(t, err) assert.Len(t, list, 2) } // TestUserOrgUpdate 测试更新用户-机构关联 func TestUserOrgUpdate(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建用户 user := &User{Username: "testuser", Email: "test@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(user).Error require.NoError(t, err) // 创建机构 org := createTestOrg(t, db) // 创建2个角色 role1 := &Role{Name: "角色1", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} role2 := &Role{Name: "角色2", Code: "role_2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(role1).Error require.NoError(t, err) err = db.Create(role2).Error require.NoError(t, err) // 创建关联(使用角色1) uo := &UserOrganization{UserId: user.Id, OrgId: org.Id, RoleId: role1.Id} _, err = UserOrgInsert(ctx, db, uo) require.NoError(t, err) // 更新角色为角色2 uo.RoleId = role2.Id err = UserOrgUpdate(ctx, db, uo) require.NoError(t, err) // 验证更新结果 updated, err := UserOrgFindOne(ctx, db, user.Id, org.Id) require.NoError(t, err) assert.Equal(t, role2.Id, updated.RoleId) } // TestUserOrgDelete 测试删除用户-机构关联 func TestUserOrgDelete(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建用户 user := &User{Username: "testuser", Email: "test@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(user).Error require.NoError(t, err) // 创建机构 org := createTestOrg(t, db) // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(role).Error require.NoError(t, err) // 创建关联 uo := &UserOrganization{UserId: user.Id, OrgId: org.Id, RoleId: role.Id} _, err = UserOrgInsert(ctx, db, uo) require.NoError(t, err) // 删除关联 err = UserOrgDelete(ctx, db, user.Id, org.Id) require.NoError(t, err) // 验证关联已被删除 _, err = UserOrgFindOne(ctx, db, user.Id, org.Id) require.Error(t, err) assert.Equal(t, ErrNotFound, err) } // TestUserOrgCountByOrgId 测试统计机构成员数 func TestUserOrgCountByOrgId(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建机构 org := createTestOrg(t, db) // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role).Error require.NoError(t, err) // 创建3个用户并关联到机构 for i := 1; i <= 3; i++ { user := &User{ Username: "user" + string(rune('0'+i)), Email: "user" + string(rune('0'+i)) + "@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now, } err := db.Create(user).Error require.NoError(t, err) uo := &UserOrganization{UserId: user.Id, OrgId: org.Id, RoleId: role.Id} _, err = UserOrgInsert(ctx, db, uo) require.NoError(t, err) } // 统计成员数 count, err := UserOrgCountByOrgId(ctx, db, org.Id) require.NoError(t, err) assert.Equal(t, int64(3), count) } // ==================== RoleMenu Tests ==================== // TestRoleMenuSetForRole 测试全量设置角色的菜单 func TestRoleMenuSetForRole(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role).Error require.NoError(t, err) // 创建3个菜单 menus := []Menu{ {Name: "菜单1", Path: "/1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "菜单2", Path: "/2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "菜单3", Path: "/3", SortOrder: 3, Status: 1, CreatedAt: now, UpdatedAt: now}, } for i := range menus { err := db.Create(&menus[i]).Error require.NoError(t, err) } // 设置角色菜单(菜单1和菜单2) menuIds := []int64{menus[0].Id, menus[1].Id} err = RoleMenuSetForRole(ctx, db, role.Id, menuIds) require.NoError(t, err) // 验证 foundIds, err := RoleMenuFindByRoleId(ctx, db, role.Id) require.NoError(t, err) assert.Len(t, foundIds, 2) assert.Contains(t, foundIds, menus[0].Id) assert.Contains(t, foundIds, menus[1].Id) // 重新设置(替换为菜单2和菜单3) menuIds2 := []int64{menus[1].Id, menus[2].Id} err = RoleMenuSetForRole(ctx, db, role.Id, menuIds2) require.NoError(t, err) // 验证替换结果 foundIds2, err := RoleMenuFindByRoleId(ctx, db, role.Id) require.NoError(t, err) assert.Len(t, foundIds2, 2) assert.Contains(t, foundIds2, menus[1].Id) assert.Contains(t, foundIds2, menus[2].Id) } // TestRoleMenuFindByRoleId 测试获取角色的菜单ID列表 func TestRoleMenuFindByRoleId(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role).Error require.NoError(t, err) // 创建菜单 menu1 := &Menu{Name: "菜单1", Path: "/1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} menu2 := &Menu{Name: "菜单2", Path: "/2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(menu1).Error require.NoError(t, err) err = db.Create(menu2).Error require.NoError(t, err) // 设置角色菜单 err = RoleMenuSetForRole(ctx, db, role.Id, []int64{menu1.Id, menu2.Id}) require.NoError(t, err) // 查询角色的菜单ID列表 menuIds, err := RoleMenuFindByRoleId(ctx, db, role.Id) require.NoError(t, err) assert.Len(t, menuIds, 2) assert.Contains(t, menuIds, menu1.Id) assert.Contains(t, menuIds, menu2.Id) } // TestRoleMenuFindByRoleIds 测试获取多个角色的菜单ID列表(去重) func TestRoleMenuFindByRoleIds(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建2个角色 role1 := &Role{Name: "角色1", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} role2 := &Role{Name: "角色2", Code: "role_2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role1).Error require.NoError(t, err) err = db.Create(role2).Error require.NoError(t, err) // 创建3个菜单 menus := []Menu{ {Name: "菜单1", Path: "/1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "菜单2", Path: "/2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now}, {Name: "菜单3", Path: "/3", SortOrder: 3, Status: 1, CreatedAt: now, UpdatedAt: now}, } for i := range menus { err := db.Create(&menus[i]).Error require.NoError(t, err) } // 角色1 -> 菜单1, 菜单2 err = RoleMenuSetForRole(ctx, db, role1.Id, []int64{menus[0].Id, menus[1].Id}) require.NoError(t, err) // 角色2 -> 菜单2, 菜单3(菜单2重复) err = RoleMenuSetForRole(ctx, db, role2.Id, []int64{menus[1].Id, menus[2].Id}) require.NoError(t, err) // 查询多个角色的菜单ID(去重) menuIds, err := RoleMenuFindByRoleIds(ctx, db, []int64{role1.Id, role2.Id}) require.NoError(t, err) assert.Len(t, menuIds, 3) // 菜单1, 菜单2, 菜单3(去重后3个) assert.Contains(t, menuIds, menus[0].Id) assert.Contains(t, menuIds, menus[1].Id) assert.Contains(t, menuIds, menus[2].Id) } // TestRoleMenuDeleteByRoleId 测试删除角色的所有菜单关联 func TestRoleMenuDeleteByRoleId(t *testing.T) { db := setupOrgTestDB(t) ctx := context.Background() now := time.Now() // 创建角色 role := &Role{Name: "角色", Code: "role_1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} err := db.Create(role).Error require.NoError(t, err) // 创建菜单 menu1 := &Menu{Name: "菜单1", Path: "/1", SortOrder: 1, Status: 1, CreatedAt: now, UpdatedAt: now} menu2 := &Menu{Name: "菜单2", Path: "/2", SortOrder: 2, Status: 1, CreatedAt: now, UpdatedAt: now} err = db.Create(menu1).Error require.NoError(t, err) err = db.Create(menu2).Error require.NoError(t, err) // 设置角色菜单 err = RoleMenuSetForRole(ctx, db, role.Id, []int64{menu1.Id, menu2.Id}) require.NoError(t, err) // 删除角色的所有菜单关联 err = RoleMenuDeleteByRoleId(ctx, db, role.Id) require.NoError(t, err) // 验证菜单关联已被删除 menuIds, err := RoleMenuFindByRoleId(ctx, db, role.Id) require.NoError(t, err) assert.Empty(t, menuIds) }