import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { filterShopKeyRows, normalizeShopKeyFilter, shopKeyFilterActive, shopKeyPageInfo, type ShopKeyPageInfo, } from '../src/pages/shop/shop-keys-list-state.ts' import type { ShopKeyItem } from '../src/pages/shop/shop-dto.ts' function key(id: number, remarkName: string, ziniaoAccountName = ''): ShopKeyItem { return { id, remarkName, ziniaoAccountName, ziniaoToken: '', ipWhitelistStatus: 'UNKNOWN' } } test('test_task_083_shop_key_filter_page_normal_primary_path', () => { // 正常主路径:关键词过滤加载行 + 分页信息派生。 const rows = [key(1, '主店'), key(2, '副店'), key(3, 'blue_agent 备用')] const filtered = filterShopKeyRows(rows, normalizeShopKeyFilter({ keyword: ' 副店 ' })) assert.equal(filtered.length, 1) assert.equal(filtered[0].id, 2) const info: ShopKeyPageInfo = shopKeyPageInfo(2, 15, 30) assert.equal(info.page, 2) assert.equal(info.pageSize, 15) assert.equal(info.totalPages, 2) assert.equal(info.from, 16) assert.equal(info.to, 30) assert.equal(info.hasPrev, true) assert.equal(info.hasNext, false) }) test('test_task_083_shop_key_filter_page_normal_variant_input', () => { // 正常变体:关键词可命中紫鸟账号名,大小写不敏感。 const rows = [key(1, 'A店', 'BlueAgent'), key(2, 'B店', 'red')] const filtered = filterShopKeyRows(rows, { keyword: 'blue' }) assert.equal(filtered.length, 1) assert.equal(filtered[0].id, 1) assert.equal(shopKeyFilterActive({ keyword: 'blue' }), true) }) test('test_task_083_shop_key_filter_page_normal_repeated_is_idempotent', () => { // 正常重复:过滤不修改原列表、输入状态稳定。 const rows = [key(1, '主店'), key(2, '副店')] const filter = { keyword: ' 主 店 ' } const first = filterShopKeyRows(rows, normalizeShopKeyFilter(filter)) const second = filterShopKeyRows(rows, normalizeShopKeyFilter(filter)) assert.deepEqual(first, second) assert.equal(filter.keyword, ' 主 店 ', '不改调用方状态') assert.equal(rows.length, 2, '不改原列表') const info = shopKeyPageInfo(2, 15, 45) assert.deepEqual(info, shopKeyPageInfo(2, 15, 45)) }) test('test_task_083_shop_key_filter_page_boundary_empty_input', () => { // 边界空值:空关键词不过滤(返回全部加载行);空状态分页信息不崩溃。 const rows = [key(1, 'A'), key(2, 'B')] const all = filterShopKeyRows(rows, normalizeShopKeyFilter({})) assert.equal(all.length, 2) const info = shopKeyPageInfo(1, 15, 0) assert.equal(info.totalPages, 1) assert.equal(info.page, 1) assert.equal(info.from, 0) assert.equal(info.to, 0) assert.equal(info.hasPrev, false) assert.equal(info.hasNext, false) }) test('test_task_083_shop_key_filter_page_boundary_single_item', () => { // 边界单元素:单行列表与单页总数。 const rows = [key(1, '唯一店')] const filtered = filterShopKeyRows(rows, { keyword: '唯一' }) assert.equal(filtered.length, 1) const info = shopKeyPageInfo(1, 15, 1) assert.equal(info.totalPages, 1) assert.equal(info.to, 1) }) test('test_task_083_shop_key_filter_page_boundary_limit_or_missing_field', () => { // 边界上限/缺字段:页码/页大小钳制;页大小越界归上限;关键字空白视为无筛选。 const info = shopKeyPageInfo(0, 999, 50) assert.equal(info.page, 1) assert.equal(info.pageSize, 100) assert.equal(info.totalPages, 1) const late = shopKeyPageInfo(99, 15, 30) assert.equal(late.page, 2, '请求页码超过末页应钳到末页') assert.equal(filterShopKeyRows([key(1, 'A')], normalizeShopKeyFilter({ keyword: ' ' })).length, 1) }) test('test_task_083_shop_key_filter_page_invalid_input_rejected', () => { // 异常输入:非数字/NaN 页码回默认;非字符串关键词按无筛选处理。 const info = shopKeyPageInfo(Number.NaN, Number.NaN, 10) assert.equal(info.page, 1) assert.equal(info.pageSize, 15) assert.equal(shopKeyFilterActive(normalizeShopKeyFilter({ keyword: 3 as never })), false) }) test('test_task_083_shop_key_filter_page_dependency_failure_returns_actionable_message', () => { // 依赖失败/可操作:列表状态为纯逻辑、无框架/http;复用 DTO 常量与类型。 const mod = readSource('src/pages/shop/shop-keys-list-state.ts') assert.equal(/axios|http\.|vue/.test(mod), false, '列表状态保持纯逻辑') assert.match(mod, /SHOP_PAGE_DEFAULT_SIZE/) assert.match(mod, /ShopKeyItem/) })