const assert = require('assert'); const { normalizeNonNegativeInteger, parseBooleanLike } = require('../utils/coerce'); const { buildDeviceName, detectDeviceTypeFromUserAgent, inferPlatformFromUserAgent, normalizeClientIp, normalizeClientType, resolveClientType, sanitizeDeviceText } = require('../utils/device'); const { parseDownloadTrafficLine, parseDownloadTrafficLogTime } = require('../utils/oss-log-parser'); const { isPathInside, normalizeVirtualPath } = require('../utils/path-safety'); const { isPathWithinShare, isShareIpAllowed, isValidShareCode, parseShareIpWhitelist } = require('../utils/share'); const { buildHttpDownloadUrl, sanitizeHttpBaseUrl } = require('../utils/url'); const { compareLooseVersion, normalizeReleaseNotes, normalizeSha256, normalizeVersion } = require('../utils/version'); const results = { passed: 0, failed: 0 }; function test(name, fn) { try { fn(); results.passed += 1; console.log(` [PASS] ${name}`); } catch (error) { results.failed += 1; console.error(` [FAIL] ${name}: ${error.message}`); } } console.log('\n========== 领域纯函数测试 ==========\n'); test('版本号规范化与宽松比较', () => { assert.strictEqual(normalizeVersion(' v1.2.3 '), 'v1.2.3'); assert.strictEqual(normalizeVersion('', '1.0.0'), '1.0.0'); assert.strictEqual(compareLooseVersion('v1.10.0', '1.9.9'), 1); assert.strictEqual(compareLooseVersion('1.2', '1.2.0'), 0); assert.strictEqual(compareLooseVersion('1.2.0', '2.0.0'), -1); }); test('发布说明转义换行被还原', () => { assert.strictEqual(normalizeReleaseNotes('first\\r\\nsecond\\n'), 'first\nsecond'); }); test('SHA256 只接受完整十六进制摘要', () => { assert.strictEqual(normalizeSha256(' A'.repeat(64)), ''); assert.strictEqual(normalizeSha256('A'.repeat(64)), 'a'.repeat(64)); assert.strictEqual(normalizeSha256('a'.repeat(63)), ''); }); test('非负整数与布尔配置规范化', () => { assert.strictEqual(normalizeNonNegativeInteger(12.9), 12); assert.strictEqual(normalizeNonNegativeInteger(-1, 4), 4); assert.strictEqual(parseBooleanLike('YES'), true); assert.strictEqual(parseBooleanLike('off', true), false); assert.strictEqual(parseBooleanLike('invalid', true), true); }); test('物理路径边界不接受兄弟目录', () => { const parent = require('path').resolve('/tmp/root'); assert.strictEqual(isPathInside(parent, require('path').join(parent, 'child')), true); assert.strictEqual(isPathInside(parent, require('path').resolve('/tmp/root-other')), false); }); test('虚拟路径统一分隔符并拒绝遍历和空字节', () => { assert.strictEqual(normalizeVirtualPath('folder\\file.txt'), '/folder/file.txt'); assert.strictEqual(normalizeVirtualPath('/folder/../secret'), null); assert.strictEqual(normalizeVirtualPath('/folder/%2e%2e/secret'), null); assert.strictEqual(normalizeVirtualPath('/folder/%2500.txt'), null); assert.strictEqual(normalizeVirtualPath('/'), '/'); }); test('HTTP 基础地址拒绝危险协议并移除查询参数', () => { assert.strictEqual(sanitizeHttpBaseUrl('javascript:alert(1)'), null); assert.strictEqual(sanitizeHttpBaseUrl('https://example.test/base/?token=x#hash'), 'https://example.test/base'); }); test('下载地址逐段编码且不继承查询参数', () => { assert.strictEqual( buildHttpDownloadUrl('https://example.test/base?token=x', '/目录/a b.txt'), 'https://example.test/base/%E7%9B%AE%E5%BD%95/a%20b.txt' ); }); test('OSS 日志时间正确应用时区', () => { const date = parseDownloadTrafficLogTime('[27/Jul/2026:12:34:56 +0800]'); assert.strictEqual(date.toISOString(), '2026-07-27T04:34:56.000Z'); assert.strictEqual(parseDownloadTrafficLogTime('[31/Feb/2026:12:00:00 +0800]'), null); assert.strictEqual(parseDownloadTrafficLogTime('[27/Jul/2026:12:00:00 +2460]'), null); }); test('OSS 下载日志解析用户、对象键、字节和时间', () => { const line = '1.2.3.4 - - [27/Jul/2026:12:34:56 +0800] "GET /user_42/folder/a%20b.txt?signature=x HTTP/1.1" 206 1024 "-" "ua"'; const result = parseDownloadTrafficLine(line); assert.strictEqual(result.userId, 42); assert.strictEqual(result.bytes, 1024); assert.strictEqual(result.objectKey, 'user_42/folder/a b.txt'); assert.strictEqual(result.eventAt.toISOString(), '2026-07-27T04:34:56.000Z'); }); test('OSS 日志只统计成功 GET 下载', () => { const fallback = new Date('2026-01-01T00:00:00Z'); assert.strictEqual(parseDownloadTrafficLine('"HEAD /user_1/a HTTP/1.1" 200 10', fallback), null); assert.strictEqual(parseDownloadTrafficLine('"GET /user_1/a HTTP/1.1" 404 10', fallback), null); assert.strictEqual(parseDownloadTrafficLine('"GET /public/a HTTP/1.1" 200 10', fallback), null); assert.strictEqual(parseDownloadTrafficLine('"GET /user_1/a HTTP/1.1" 200 -', fallback), null); }); test('分享码与分享路径严格限定', () => { assert.strictEqual(isValidShareCode('Abc123'), true); assert.strictEqual(isValidShareCode('../bad'), false); assert.strictEqual(isPathWithinShare('/folder/a.txt', { share_type: 'folder', share_path: '/folder' }), true); assert.strictEqual(isPathWithinShare('/folder-other/a.txt', { share_type: 'folder', share_path: '/folder' }), false); assert.strictEqual(isPathWithinShare('/folder/a.txt', { share_type: 'file', share_path: '/folder/a.txt' }), true); assert.strictEqual(isPathWithinShare('/folder/b.txt', { share_type: 'file', share_path: '/folder/a.txt' }), false); }); test('IP 白名单支持分隔符、精确匹配与前缀规则', () => { const whitelist = parseShareIpWhitelist('127.0.0.1, 10.0.*;192.168.1.5'); assert.deepStrictEqual(whitelist, ['127.0.0.1', '10.0.*', '192.168.1.5']); assert.strictEqual(isShareIpAllowed('10.0.2.3', whitelist), true); assert.strictEqual(isShareIpAllowed('192.168.1.6', whitelist), false); assert.strictEqual(isShareIpAllowed('', whitelist), false); assert.strictEqual(isShareIpAllowed('', []), true); }); test('客户端 IP 与设备类型被稳定规范化', () => { assert.strictEqual(normalizeClientIp('::ffff:127.0.0.1'), '127.0.0.1'); assert.strictEqual(normalizeClientIp('::1'), '127.0.0.1'); assert.strictEqual(detectDeviceTypeFromUserAgent('Mozilla/5.0 iPhone'), 'mobile'); assert.strictEqual(inferPlatformFromUserAgent('Mozilla/5.0 (Windows NT 10.0)'), 'Windows'); assert.strictEqual(normalizeClientType(' DESKTOP '), 'desktop'); assert.strictEqual(normalizeClientType('bot'), ''); assert.strictEqual(resolveClientType('', 'wanwan-cloud-desktop/1.0'), 'desktop'); }); test('设备名称限制长度并按客户端类型回退', () => { assert.strictEqual(sanitizeDeviceText(' abc ', 2), 'ab'); assert.strictEqual(buildDeviceName({ clientType: 'mobile', platform: 'iOS' }), '移动端浏览器 · iOS'); assert.strictEqual(buildDeviceName({ clientType: 'web', deviceName: ' 我的设备 ', platform: 'Windows' }), '我的设备'); }); console.log('\n========================================'); console.log('测试总结'); console.log('========================================'); console.log(`通过: ${results.passed}`); console.log(`失败: ${results.failed}`); process.exit(results.failed > 0 ? 1 : 0);