gongxiangp2/lib/__tests__/organization-material-permissions.test.ts

410 lines
14 KiB
TypeScript

import {
collectEmployeeOrganizationPaths,
mergeDepartmentAndInheritPacks,
collectOccupiedOrganizationPaths,
countPeopleOnOrganizationPaths,
countUniquePeopleOnOrganizationPaths,
describeDepartmentGroupStatus,
findNearestGroupMemberships,
findOrganizationPermissionStorageKey,
isUnderOrganizationPath,
normalizeOrganizationPath,
organizationPathChain,
parseOrganizationDepartmentIds,
personBelongsToOrganizationPath,
pruneOrganizationTree,
resolveEmployeeOrganizationPaths,
resolveOrganizationPermissionKey,
unionPermissionPacks,
} from '@/lib/organization-material-permissions'
describe('organization material permission keys', () => {
test('path chain goes from company to leaf', () => {
expect(
organizationPathChain('江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部'),
).toEqual([
'江苏简尚品牌管理有限公司',
'江苏简尚品牌管理有限公司/运营中心A',
'江苏简尚品牌管理有限公司/运营中心A/事业一部',
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
])
})
test('own path config wins over legacy short names', () => {
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A', [
'运营中心',
'江苏简尚品牌管理有限公司/运营中心A',
]),
).toEqual({
key: '江苏简尚品牌管理有限公司/运营中心A',
source: 'own',
})
})
test('运营中心A can reuse the old 运营中心 pack', () => {
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A', ['运营中心']),
).toEqual({
key: '运营中心',
source: 'legacy',
})
})
test('策划中心 and 摄影中心 use the old 视觉中心 pack even if they have their own rows', () => {
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/策划中心', [
'策划中心',
'摄影中心',
'视觉中心',
]),
).toEqual({
key: '视觉中心',
source: 'legacy',
})
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/摄影中心', [
'策划中心',
'摄影中心',
'视觉中心',
]),
).toEqual({
key: '视觉中心',
source: 'legacy',
})
})
test('卓禾运营 and 朴美运营 reuse the old 运营中心 pack', () => {
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A/卓禾运营', ['运营中心']),
).toEqual({
key: '运营中心',
source: 'legacy',
})
expect(
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心B/朴美运营', ['运营中心']),
).toEqual({
key: '运营中心',
source: 'legacy',
})
})
test('品牌中心 subtree uses the old 视觉中心 pack', () => {
expect(
findOrganizationPermissionStorageKey(
'江苏简尚品牌管理有限公司/品牌中心/品牌创意A部',
['视觉中心'],
),
).toEqual({
key: '视觉中心',
source: 'legacy',
})
expect(
resolveOrganizationPermissionKey(
'江苏简尚品牌管理有限公司/品牌中心/商品企划部/桌类',
['视觉中心'],
),
).toEqual({
key: '视觉中心',
source: 'inherit',
inheritFrom: '江苏简尚品牌管理有限公司/品牌中心/商品企划部',
})
})
test('employee org paths follow roster primary, extra posts and department ids', () => {
const company = '江苏简尚品牌管理有限公司'
const brand = `${company}/品牌中心`
const ops = `${company}/运营中心G`
const lookup = new Map([
['2', brand],
['9', ops],
])
expect(
resolveEmployeeOrganizationPaths(
{
departmentPath: brand,
departmentMemberships: [{ sourceDeptId: '9', departmentPath: ops }],
sourcePayload: { syncedFields: { departmentIds: '[2,9]' } },
},
lookup,
),
).toEqual([brand, ops])
expect(resolveEmployeeOrganizationPaths(null, lookup)).toEqual([])
})
test('permission tree drops revoked empty branches but keeps occupied ancestors', () => {
const occupied = collectOccupiedOrganizationPaths([
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
])
const pruned = pruneOrganizationTree(
[
{
path: '江苏简尚品牌管理有限公司',
children: [
{
path: '江苏简尚品牌管理有限公司/广州项目组',
children: [{ path: '江苏简尚品牌管理有限公司/广州项目组/A组', children: [] }],
},
{
path: '江苏简尚品牌管理有限公司/运营中心A',
children: [
{
path: '江苏简尚品牌管理有限公司/运营中心A/事业一部',
children: [
{ path: '江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部', children: [] },
],
},
],
},
],
},
],
occupied,
)
expect(pruned).toHaveLength(1)
expect(pruned[0].children.map((node) => node.path)).toEqual([
'江苏简尚品牌管理有限公司/运营中心A',
])
expect(pruned[0].children[0].children[0].children.map((node) => node.path)).toEqual([
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
])
})
test('leaf without own rows inherits the parent pack', () => {
expect(
resolveOrganizationPermissionKey(
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
['运营中心'],
),
).toEqual({
key: '运营中心',
source: 'inherit',
inheritFrom: '江苏简尚品牌管理有限公司/运营中心A',
})
})
})
describe('organization people counts', () => {
const company = '江苏简尚品牌管理有限公司'
const planning = `${company}/品牌中心/商品企划部`
const shelf = `${planning}/架类`
const support = `${company}/业务支持`
const visual = `${support}/视觉支持`
test('normalize keeps the original path, it does not join the prefix chain', () => {
expect(normalizeOrganizationPath(` ${company}/ 总经理室 / `)).toBe(`${company}/总经理室`)
})
test('a person belongs to their own dept and every ancestor, not a sibling', () => {
expect(isUnderOrganizationPath(shelf, planning)).toBe(true)
expect(isUnderOrganizationPath(shelf, company)).toBe(true)
expect(isUnderOrganizationPath(planning, planning)).toBe(true)
expect(isUnderOrganizationPath(planning, shelf)).toBe(false)
expect(isUnderOrganizationPath(visual, planning)).toBe(false)
expect(isUnderOrganizationPath(null, planning)).toBe(false)
})
test('tree numbers count each person once per ancestor, never roll children up again', () => {
const counts = countPeopleOnOrganizationPaths([shelf, visual, `${company}/人力行政中心`])
expect(counts.get(company)).toBe(3)
expect(counts.get(`${company}/品牌中心`)).toBe(1)
expect(counts.get(planning)).toBe(1)
expect(counts.get(shelf)).toBe(1)
expect(counts.get(support)).toBe(1)
expect(counts.get(visual)).toBe(1)
expect(counts.get(`${company}/人力行政中心`)).toBe(1)
})
test('clicking 商品企划部 includes the person sitting in 架类', () => {
const people = [
{ name: '涂涂', departmentPath: shelf },
{ name: '香樟', departmentPath: `${company}/品牌中心/品牌设计部` },
]
expect(
people.filter((person) => isUnderOrganizationPath(person.departmentPath, planning)).map((p) => p.name),
).toEqual(['涂涂'])
})
test('一人兼多个部门时,每个部门名单都有他,公司人数仍只计一次', () => {
const office = `${company}/总经理室`
const brand = `${company}/品牌中心`
const ops = `${company}/运营中心G`
const jiangling = collectEmployeeOrganizationPaths([office, brand, ops, office])
expect(jiangling).toEqual([office, brand, ops])
expect(personBelongsToOrganizationPath(jiangling, brand)).toBe(true)
expect(personBelongsToOrganizationPath(jiangling, office)).toBe(true)
expect(personBelongsToOrganizationPath(jiangling, `${company}/运营中心A`)).toBe(false)
const counts = countUniquePeopleOnOrganizationPaths([
jiangling,
[`${company}/人力行政中心`],
])
expect(counts.get(company)).toBe(2)
expect(counts.get(brand)).toBe(1)
expect(counts.get(office)).toBe(1)
expect(counts.get(`${company}/人力行政中心`)).toBe(1)
})
test('只保留最深任职,上级部门不另算一个职位', () => {
expect(
collectEmployeeOrganizationPaths([
`${company}/品牌中心`,
`${company}/品牌中心/品牌创意A部`,
`${company}/品牌中心`,
]),
).toEqual([`${company}/品牌中心/品牌创意A部`])
})
test('从自己往上找最近的权限组,一个部门只进一组', () => {
const leaf = `${company}/运营中心A/事业一部/品类十九部`
const ops = `${company}/运营中心A`
const memberships = [
{ departmentPath: ops, slug: 'operations', name: '运营' },
{ departmentPath: company, slug: 'all', name: '全员' },
]
expect(findNearestGroupMemberships(leaf, memberships)).toEqual({
attachedPath: ops,
items: [{ departmentPath: ops, slug: 'operations', name: '运营' }],
})
expect(findNearestGroupMemberships(`${company}/财务中心`, memberships)).toEqual({
attachedPath: company,
items: [{ departmentPath: company, slug: 'all', name: '全员' }],
})
expect(findNearestGroupMemberships(`${company}/人力行政中心`, [])).toBeNull()
})
test('分配部门时标明已在本组、已在别组、随上级或未进组', () => {
const ops = `${company}/运营中心A`
const leaf = `${company}/运营中心A/事业一部/品类十九部`
const memberships = [{ departmentPath: ops, slug: 'operations', name: '运营' }]
expect(describeDepartmentGroupStatus(ops, 'operations', memberships)).toMatchObject({
kind: 'own',
label: '已在本组',
})
expect(describeDepartmentGroupStatus(ops, 'visual', memberships)).toMatchObject({
kind: 'other',
label: '已在「运营」',
})
expect(describeDepartmentGroupStatus(leaf, 'visual', memberships)).toMatchObject({
kind: 'inherit',
label: '随 运营中心A · 运营',
})
expect(describeDepartmentGroupStatus(`${company}/财务中心`, 'visual', memberships)).toMatchObject({
kind: 'none',
label: '未进组',
})
})
test('多组合并时允许的取并集,不允许的不会把别组已经给的拿掉', () => {
const merged = unionPermissionPacks(
{
'3D场景': { canView: true, canDownload: true, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
},
{
'3D场景': { canView: false, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
详情模板: { canView: true, canDownload: true, canDownloadSource: true, canShare: true, canEditAssets: false, canAccessPrivate: false },
},
{
详情模板: { canView: true, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
},
)
expect(merged['3D场景']).toEqual({
canView: true,
canDownload: true,
canDownloadSource: false,
canShare: true,
canEditAssets: false,
canAccessPrivate: false,
})
expect(merged['详情模板']).toEqual({
canView: true,
canDownload: true,
canDownloadSource: true,
canShare: true,
canEditAssets: false,
canAccessPrivate: false,
})
})
test('用户继承叠在部门权限上,能给的加上,不能把部门已有的拿掉', () => {
const merged = mergeDepartmentAndInheritPacks(
{
'3D场景': { canView: true, canDownload: true, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
'任务-详情': { canView: true, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
},
{
'3D场景': { canView: false, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
详情模板: { canView: true, canDownload: true, canDownloadSource: true, canShare: true, canEditAssets: false, canAccessPrivate: false },
},
)
expect(merged['3D场景']).toEqual({
canView: true,
canDownload: true,
canDownloadSource: false,
canShare: true,
canEditAssets: false,
canAccessPrivate: false,
})
expect(merged['任务-详情']?.canDownload).toBe(false)
expect(merged['详情模板']).toEqual({
canView: true,
canDownload: true,
canDownloadSource: true,
canShare: true,
canEditAssets: false,
canAccessPrivate: false,
})
})
test('任务详情私密权限同样按并集合并,别的组不能把已给的权限抹掉', () => {
const merged = unionPermissionPacks(
{
'任务-详情': {
canView: true,
canDownload: true,
canDownloadSource: false,
canShare: true,
canEditAssets: false,
canAccessPrivate: true,
},
},
{
'任务-详情': {
canView: false,
canDownload: false,
canDownloadSource: false,
canShare: true,
canEditAssets: false,
canAccessPrivate: false,
},
},
)
expect(merged['任务-详情']).toEqual({
canView: true,
canDownload: true,
canDownloadSource: false,
canShare: true,
canEditAssets: false,
canAccessPrivate: true,
})
})
test('花名册部门 ID 列表会拆出全部任职部门', () => {
expect(parseOrganizationDepartmentIds('[22787301,909676348]')).toEqual(['22787301', '909676348'])
expect(parseOrganizationDepartmentIds('22802296|911666246|32221130')).toEqual([
'22802296',
'911666246',
'32221130',
])
})
})