task-2(壳层/路由): 冻结 /admin-vue/ base 与 History 路由契约为单一事实源

新增 src/config/app.ts(APP_BASE_PATH/ensureAppBasePath/joinAdminPath),
vite.config base、router createWebHistory、AdminLayout logo 路径统一消费常量,
禁止散落字面量;8 用例覆盖归一化/幂等/空段/单段/非法输入与字面量单源。
This commit is contained in:
2026-09-05 12:13:19 +08:00
parent ec7d56d566
commit 6f0357e41c
6 changed files with 338 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import {
APP_BASE_PATH,
APP_HISTORY_MODE,
ensureAppBasePath,
joinAdminPath,
} from '../src/config/app.ts'
import { resolveWorkspaceRoot, walk } from '../src/config/workspace.ts'
const ROOT = resolveWorkspaceRoot()
function sourceFiles(): string[] {
return walk(ROOT)
}
function literalBaseOccurrences(file: string): number {
const content = readFileSync(join(ROOT, file), 'utf8')
const matches = content.match(/['"]\/admin-vue\/['"]/g)
return matches ? matches.length : 0
}
test('test_task_002_history_base_contract_normal_primary_path', () => {
// 正常主路径:冻结契约常量可被路由/构建直接消费。
assert.equal(APP_BASE_PATH, '/admin-vue/')
assert.equal(APP_HISTORY_MODE, 'history')
assert.equal(joinAdminPath('account', 'users'), '/admin-vue/account/users')
const router = readFileSync(join(ROOT, 'src/router/index.ts'), 'utf8')
assert.match(router, /createWebHistory\(APP_BASE_PATH\)/)
})
test('test_task_002_history_base_contract_normal_variant_input', () => {
// 正常变体:无尾斜杠/带尾斜杠/单段带斜杠的等价输入得到同一结果。
assert.equal(ensureAppBasePath('/admin-vue'), APP_BASE_PATH)
assert.equal(ensureAppBasePath('/admin-vue/'), APP_BASE_PATH)
assert.equal(joinAdminPath('account/users'), joinAdminPath('account', 'users'))
})
test('test_task_002_history_base_contract_normal_repeated_operation_is_idempotent', () => {
// 正常重复:归一化与拼接幂等、不依赖可变状态。
assert.equal(ensureAppBasePath(ensureAppBasePath('/admin-vue')), APP_BASE_PATH)
assert.equal(joinAdminPath('account', 'users'), joinAdminPath('account', 'users'))
const once = joinAdminPath('shop-center', 'list')
const twice = joinAdminPath('shop-center', 'list')
assert.equal(once, twice)
})
test('test_task_002_history_base_contract_boundary_empty_input', () => {
// 边界空值:无段拼接回到基准根路径;空 base 被拒绝并带语义错误。
assert.equal(joinAdminPath(), APP_BASE_PATH)
assert.throws(() => ensureAppBasePath(''), /base 路径非法/)
})
test('test_task_002_history_base_contract_boundary_single_item', () => {
// 边界单元素:单个路由段生成单一绝对地址。
assert.equal(joinAdminPath('users'), '/admin-vue/users')
assert.ok(APP_BASE_PATH.endsWith('/'))
assert.ok(!APP_BASE_PATH.includes('//'))
})
test('test_task_002_history_base_contract_boundary_limit_or_missing_field', () => {
// 边界上限:段内多余斜杠与首尾空白被收敛,不产生双斜杠或空格。
const path = joinAdminPath(' /account/ ', ' users/ ')
assert.equal(path, '/admin-vue/account/users')
assert.equal((APP_BASE_PATH.match(/\//g) || []).length, 2, '基准路径只含一个目录段')
})
test('test_task_002_history_base_contract_invalid_input_rejected', () => {
// 异常输入:缺 / 前缀、含内部双斜杠、含 .. 越权段均被拒绝并给出消息。
assert.throws(() => ensureAppBasePath('admin-vue'), /base 路径非法/)
assert.throws(() => ensureAppBasePath('//admin-vue/'), /base 路径非法/)
assert.throws(() => joinAdminPath('..', 'x'), /不允许出现 \.\./)
assert.throws(() => joinAdminPath('/../etc'), /不允许出现 \.\./)
})
test('test_task_002_history_base_contract_dependency_failure_returns_actionable_message', () => {
// 依赖失败:非法 base 值(undefined)返回可操作错误;base 字面量必须单一来源。
assert.throws(() => ensureAppBasePath(undefined as unknown as string), (e: Error) => {
return /base 路径非法/.test(e.message)
})
const offenders = sourceFiles()
.map((file) => ({ file, count: literalBaseOccurrences(file) }))
.filter(({ file, count }) => count > 0)
assert.deepEqual(
offenders.map((o) => o.file),
['src/config/app.ts'],
`'/admin-vue/' 字面量必须只出现在单一事实源 app.ts: ${JSON.stringify(offenders)}`,
)
})