task-93(店铺中心): 实现店铺凭证字段分离
新增 shop-credential-model.ts:凭证明文仅经凭证端点解码且落 SensitiveString,
与列表行(仅掩码)字段分离;shop-manage-api.ts 增加 fetchShopCredential
(GET /api/admin/shop-manages/{id}/credential?shop_name=…)。
TDD: task-93.test.ts 8 用例先 RED 后 GREEN。
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readSource } from './helpers.ts'
|
||||
import { toShopCredential } from '../src/pages/shop/shop-credential-model.ts'
|
||||
import { toShopSummary } from '../src/pages/shop/shop-manage-model.ts'
|
||||
|
||||
test('test_task_093_credential_separation_normal_primary_path', () => {
|
||||
// 正常主路径:凭证明文只能经凭证解码产生,且落在 SensitiveString 字段。
|
||||
const credential = toShopCredential({
|
||||
id: 5,
|
||||
groupId: 3,
|
||||
groupName: '华东组',
|
||||
shopName: 'BlueWave',
|
||||
mallName: 'M',
|
||||
znUsername: 'robot',
|
||||
account: 'a@b.c',
|
||||
password: 'plain-pw',
|
||||
})
|
||||
assert.ok(credential)
|
||||
assert.equal(credential.id, 5)
|
||||
assert.equal(credential.shopName, 'BlueWave')
|
||||
assert.equal(credential.account, 'a@b.c')
|
||||
assert.equal(credential.password, 'plain-pw')
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_normal_variant_input', () => {
|
||||
// 正常变体:凭证端点解码对象结构与列表行独立,不混入列表摘要。
|
||||
const row = toShopSummary({ id: 5, shopName: 'BlueWave', password: 'plain-pw', passwordMasked: '******' })
|
||||
assert.equal(row!.account, '')
|
||||
assert.equal('password' in row!, false, '列表行不带密码明文字段')
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_repeated_is_idempotent', () => {
|
||||
// 正常重复:凭证解码稳定、不改输入。
|
||||
const payload = { id: 1, shopName: 'A', account: 'x', password: 'p' }
|
||||
assert.deepEqual(toShopCredential(payload), toShopCredential(payload))
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_boundary_empty_input', () => {
|
||||
// 边界空值:无密码凭证无法解码(避免误把空当明文)。
|
||||
assert.equal(toShopCredential({ id: 1, shopName: 'A', account: 'x', password: '' }), null)
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_boundary_single_item', () => {
|
||||
// 边界单元素:单店铺凭证解码字段完整。
|
||||
const credential = toShopCredential({ id: 9, shopName: 'S', account: 'a', password: 'p', mallName: 'M' })
|
||||
assert.ok(credential)
|
||||
assert.equal(credential.groupId, null)
|
||||
assert.equal(credential.mallName, 'M')
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_boundary_limit_or_missing_field', () => {
|
||||
// 边界上限/缺字段:凭证明文不为空时必须校验原样保留,缺 id 视为无效。
|
||||
assert.equal(toShopCredential('garbage'), null)
|
||||
assert.equal(toShopCredential({ shopName: 'no id', password: 'p' }), null)
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_invalid_input_rejected', () => {
|
||||
// 异常输入:无 id/无明文凭证解码为 null,明确拒绝进入通用对象。
|
||||
assert.equal(toShopCredential({ id: 1, shopName: 'A' }), null)
|
||||
assert.equal(toShopCredential(null), null)
|
||||
})
|
||||
|
||||
test('test_task_093_credential_separation_dependency_failure_returns_actionable_message', () => {
|
||||
// 依赖失败/取凭证走 adapter:凭证模型纯逻辑;adapter GET /{id}/credential + SensitiveString。
|
||||
const model = readSource('src/pages/shop/shop-credential-model.ts')
|
||||
assert.equal(/axios|http\.|vue/.test(model), false, '凭证模型保持纯逻辑')
|
||||
assert.match(model, /SensitiveString/)
|
||||
const api = readSource('src/pages/shop/shop-manage-api.ts')
|
||||
assert.match(api, /fetchShopCredential/)
|
||||
assert.match(api, /\/credential/)
|
||||
})
|
||||
Reference in New Issue
Block a user