fix(admin): complete IAM identity status columns

This commit is contained in:
2026-10-30 12:00:16 +08:00
parent aab0babd7e
commit a676d0936b
2 changed files with 248 additions and 5 deletions
@@ -0,0 +1,156 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const ts = require('typescript')
const adminRoot = process.env.IDENTITY_UI_ROOT || path.resolve(import.meta.dirname, '..')
const file = path.join(adminRoot, 'src/views/permission/identity/index.vue')
const source = readFileSync(file, 'utf8')
const script = source.match(/<script setup lang="ts">([\s\S]*?)<\/script>/)?.[1] || ''
function load(names, scope = {}) {
const ast = ts.createSourceFile(
'identity.ts',
script,
ts.ScriptTarget.ES2022,
true,
ts.ScriptKind.TS
)
const found = ast.statements.filter(
(node) => ts.isFunctionDeclaration(node) && names.includes(node.name?.text)
)
assert.equal(found.length, names.length, 'all requested functions exist in the actual SFC')
const output = ts.transpileModule(found.map((node) => node.getText(ast)).join('\n'), {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }
}).outputText
return new Function(...Object.keys(scope), output + '\nreturn {' + names.join(',') + '}')(
...Object.values(scope)
)
}
test('adds exactly the requested department/access/check-time columns and retains native pagination', () => {
for (const label of ['部门', '应用访问状态', '最近校验时间'])
assert.ok(source.includes(`label="${label}"`), label)
assert.match(source, /const query = reactive\(\{ q: '', state: '', page: 1, pageSize: 20 \}\)/)
assert.match(source, /query\.page = 1/)
assert.match(source, /v-model:current-page="query.page"/)
const actions = source.split('<el-table-column label="操作"')[1].split('</el-table>')[0]
assert.equal((actions.match(/type="primary"/g) || []).length, 2)
assert.match(actions, /<el-dropdown/)
})
test('department strings distinguish unassigned from unavailable data', () => {
const { departmentText } = load(['departmentText'])
assert.equal(departmentText({ dept_names: ['销售一部', '运营部'] }), '销售一部、运营部')
assert.equal(departmentText({ dept_names: [] }), '未分配')
assert.equal(departmentText({}), '未知')
assert.equal(departmentText({ dept_names: null }), '未知')
assert.equal(departmentText({ dept_names: [42] }), '未知')
})
test('authorization is never inferred from verified binding or enabled local account', () => {
const { applicationAccess } = load(['applicationAccess'])
const row = { disable: 0, iam: { state: 'verified' } }
assert.equal(applicationAccess(row).text, '未知')
assert.equal(
applicationAccess({ ...row, iam: { state: 'verified', access: { allowed: true } } }).text,
'已授权'
)
assert.equal(
applicationAccess({ ...row, iam: { state: 'verified', access: { allowed: false } } }).text,
'未授权'
)
assert.equal(
applicationAccess({ ...row, iam: { state: 'verified', access: { allowed: 'true' } } }).text,
'未知'
)
assert.equal(
applicationAccess({ ...row, iam: { state: 'unknown', access: { allowed: true } } }).text,
'未知'
)
})
test('unlinked, revoked, service failure and unknown remain distinct without stale allow=true', () => {
const { applicationAccess } = load(['applicationAccess'])
assert.equal(applicationAccess({ iam: { state: 'unlinked' } }).text, '未关联')
assert.equal(
applicationAccess({ iam: { state: 'revoked', access: { allowed: false } } }).text,
'未关联'
)
assert.equal(
applicationAccess({
iam: { state: 'unknown', access: { allowed: null, reason: 'SERVICE_UNAVAILABLE' } }
}).text,
'服务异常'
)
assert.equal(
applicationAccess({
iam: { state: 'verified', access: { allowed: true, reason: 'SERVICE_UNAVAILABLE' } }
}).text,
'服务异常'
)
assert.equal(
applicationAccess({
iam: { state: 'verified', access: { allowed: false, reason: 'NOT_LINKED' } }
}).text,
'未关联'
)
assert.equal(applicationAccess({ iam: { state: 'unknown' } }).text, '未知')
for (const [reason, expected] of Object.entries({
NOT_AUTHORIZED: '未授权',
ACCOUNT_DISABLED: 'IAM账号停用',
BINDING_CONFLICT: '关联冲突',
ACCOUNT_MISMATCH: '身份不匹配'
})) {
assert.equal(
applicationAccess({ iam: { state: 'unknown', access: { allowed: null, reason } } })
.text,
expected
)
assert.equal(
applicationAccess({ iam: { state: 'verified', access: { allowed: true, reason } } })
.text,
expected
)
}
})
test('Unix seconds display Beijing time and absent/invalid timestamps never become a fake recent check', () => {
const { checkedAtText } = load(['checkedAtText'])
assert.equal(checkedAtText(1700000000), '2023/11/15 06:13:20')
assert.equal(checkedAtText('1700000000'), checkedAtText(1700000000))
for (const value of [null, undefined, 0, '', '0']) assert.equal(checkedAtText(value), '未校验')
for (const value of [true, 'invalid', -1, Infinity, 1700000000000])
assert.equal(checkedAtText(value), '未知')
})
test('disabled writes are guarded even if an event handler is invoked directly', async () => {
let notices = 0,
posts = 0
const drawerError = { value: '' }
const scope = {
ElMessage: { warning: () => notices++ },
selected: { value: { disable: 1 } },
drawerError,
iamPost: async () => {
posts++
}
}
const { isLocalDisabled, open, submit } = load(['isLocalDisabled', 'open', 'submit'], scope)
for (const disable of [1, true, '1']) assert.equal(isLocalDisabled({ disable }), true)
for (const disable of [0, false, '0']) assert.equal(isLocalDisabled({ disable }), false)
open({ disable: 1 }, 'bind')
await submit()
assert.equal(notices, 1)
assert.equal(posts, 0)
assert.match(drawerError.value, /停用/)
})
test('all row write actions are disabled while audit remains readable with an explanation', () => {
const actions = source.split('<el-table-column label="操作"')[1].split('</el-table>')[0]
assert.ok((actions.match(/isLocalDisabled\(row\)/g) || []).length >= 5)
assert.match(actions, /command="audit">审计记录/)
assert.match(actions, /停用.*审计/)
assert.match(source, /isLocalDisabled\(selected\)/)
})