import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { SHOP_PAGE_DEFAULT_SIZE, SHOP_PAGE_MAX_SIZE, SHOP_PAGE_MIN_PAGE, emptyShopKeyPageResult, emptyShopPageResult, normalizeKeyListParams, normalizeShopListParams, toKeyPageQuery, toShopManagePageQuery, } from '../src/pages/shop/shop-dto.ts' test('test_task_081_shop_dto_normal_primary_path', () => { // 正常主路径:完整店铺筛选/分页归一后序列化为 Java camelCase 查询参数(page/pageSize/groupId/shopName)。 const params = normalizeShopListParams({ page: 2, pageSize: 20, groupId: 3, shopName: ' 主 店 ', }) assert.equal(params.page, 2) assert.equal(params.pageSize, 20) assert.equal(params.groupId, 3) assert.equal(params.shopName, '主 店') const query = toShopManagePageQuery(params) assert.equal(query.page, 2) assert.equal(query.pageSize, 20) assert.equal(query.groupId, 3) assert.equal(query.shopName, '主 店') }) test('test_task_081_shop_dto_normal_variant_input', () => { // 正常变体:店铺密钥列表只允许 page/pageSize,不携带分组/店铺名维度。 const params = normalizeKeyListParams({ page: 3, pageSize: 30 }) const query = toKeyPageQuery(params) assert.deepEqual(Object.keys(query).sort(), ['page', 'pageSize']) assert.equal(query.page, 3) assert.equal(query.pageSize, 30) // 店铺列表可选条件缺省时不下发对应字段。 const manageQuery = toShopManagePageQuery(normalizeShopListParams({ page: 1, pageSize: 15 })) assert.deepEqual(Object.keys(manageQuery).sort(), ['page', 'pageSize']) }) test('test_task_081_shop_dto_normal_repeated_operation_is_idempotent', () => { // 正常重复:归一不改输入、结果稳定;空结果默认值相互独立。 const input = { page: 2, pageSize: 25, groupId: 7, shopName: 'A店' } assert.deepEqual(normalizeShopListParams(input), normalizeShopListParams(input)) assert.equal(input.shopName, 'A店') const first = emptyShopPageResult() const second = emptyShopPageResult() first.items.push({} as never) assert.equal(second.items.length, 0, '每次空结果返回独立列表,避免污染') }) test('test_task_081_shop_dto_boundary_empty_input', () => { // 边界空值:空入参回默认分页(第 1 页/15 条),无可选筛选。 const params = normalizeShopListParams({}) assert.equal(params.page, SHOP_PAGE_MIN_PAGE) assert.equal(params.pageSize, SHOP_PAGE_DEFAULT_SIZE) assert.equal(params.groupId, null) assert.equal(params.shopName, undefined) const query = toShopManagePageQuery(params) assert.deepEqual(Object.keys(query).sort(), ['page', 'pageSize']) }) test('test_task_081_shop_dto_boundary_single_item', () => { // 边界单元素:页大小 1 保留为最小单行页;单行空结果 DTO 结构完整。 assert.equal(normalizeShopListParams({ page: 1, pageSize: 1 }).pageSize, 1) const key = emptyShopKeyPageResult() assert.deepEqual(key.items, []) assert.equal(key.total, 0) assert.equal(key.page, SHOP_PAGE_MIN_PAGE) assert.equal(key.pageSize, SHOP_PAGE_DEFAULT_SIZE) const shop = emptyShopPageResult() assert.deepEqual(shop.items, []) assert.equal(shop.total, 0) }) test('test_task_081_shop_dto_boundary_limit_or_missing_field', () => { // 边界上限:页码 <1 归 1;页大小超上限/非正被夹取;负分组 id 视为无筛选。 assert.equal(normalizeShopListParams({ page: 0, pageSize: 999 }).page, 1) assert.equal(normalizeShopListParams({ page: 0, pageSize: 999 }).pageSize, SHOP_PAGE_MAX_SIZE) assert.equal(normalizeShopListParams({ page: 0, pageSize: 0 }).pageSize, SHOP_PAGE_DEFAULT_SIZE) assert.equal(normalizeShopListParams({ page: 1, pageSize: 15, groupId: -5 }).groupId, null) const params = normalizeShopListParams({ page: 1, pageSize: 15, groupId: undefined }) assert.equal(params.shopName, undefined) }) test('test_task_081_shop_dto_invalid_input_rejected', () => { // 异常输入:非数字/NaN/空白筛选取缺省;字符串分组 id 不接受。 const params = normalizeShopListParams({ page: 'x' as never, pageSize: Number.NaN, groupId: '3' as never, shopName: ' ', }) assert.equal(params.page, SHOP_PAGE_MIN_PAGE) assert.equal(params.pageSize, SHOP_PAGE_DEFAULT_SIZE) assert.equal(params.groupId, null) assert.equal(params.shopName, undefined) }) test('test_task_081_shop_dto_dependency_failure_returns_actionable_message', () => { // 依赖失败/可操作:DTO 为纯逻辑、无框架/http;敏感字段与店铺/密钥边界有明确类型。 const mod = readSource('src/pages/shop/shop-dto.ts') assert.equal(/axios|http\.|from '\.\/http'|vue/.test(mod), false, 'DTO 模块保持纯逻辑') assert.match(mod, /export interface ShopKeyItem/) assert.match(mod, /export interface ShopSummary/) assert.match(mod, /export interface ShopCredential/) assert.match(mod, /export type SensitiveString/) assert.match(mod, /ziniaoToken\s*:/, '紫鸟令牌以敏感字段建模') assert.match(mod, /password\s*:\s*SensitiveString/, '凭证明文必须落在 SensitiveString') assert.match(mod, /passwordMasked\s*\?/, '列表行只有掩码密码') assert.equal(/group_id|created_by_id/.test(mod), false, '店铺接口为 camelCase,不应出现 snake 字段名') })