import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import test from 'node:test'; import ts from 'typescript'; const source = readFileSync(new URL('../src/utils/network-errors.ts', import.meta.url), 'utf8'); const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText; const helpers = {}; new Function('exports', js)(helpers); test('App loopback configuration is explained without misclassifying remote hosts', () => { for (const origin of ['http://127.0.0.1:8888', 'https://localhost', 'http://[::1]:8888', 'https://dev.localhost']) { assert.equal(helpers.isLoopbackServer(origin), true); assert.match(helpers.networkFailureMessage({}, origin, true), /手机自身/); } for (const origin of ['https://im.bchongw.com', 'http://192.168.1.20:8888', 'https://localhost.example.com', 'https://127.0.0.1.example.com']) { assert.equal(helpers.isLoopbackServer(origin), false); } assert.doesNotMatch(helpers.networkFailureMessage({}, 'http://127.0.0.1:8888', false), /手机自身/); }); test('native network failures distinguish certificates, DNS and timeouts without exposing raw details', () => { for (const [errMsg, expected] of [ ['request:fail SSL handshake: Trust anchor not found', /证书/], ['request:fail java.net.UnknownHostException', /域名解析/], ['request:fail timeout', /超时/], ['request:fail Network is unreachable', /联网权限/], ]) { assert.match(helpers.networkFailureMessage({ errMsg }, 'https://im.bchongw.com', true), expected); } const message = helpers.networkFailureMessage({ errMsg: 'request:fail https://example.com/callback?token=secret' }, 'https://im.bchongw.com', true); assert.doesNotMatch(message, /token|secret/); });