fix(admin): complete IAM identity status columns
This commit is contained in:
@@ -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\)/)
|
||||
})
|
||||
Reference in New Issue
Block a user