Files
opendance-web/web-server/test/access-log.spec.ts
T
zijunchiang 9f08d316d0 docs: 清除文档中的真实部署信息与易混淆 Unicode 字符
需求:公开仓库的说明文档不应暴露开发/部署期信息,且 Gitea 会对 README.txt
报"含模棱两可的 Unicode 字符"。

敏感信息(全部替换为占位符或文档示例值):
- 真实数据库实例:DB_HOST=192.168.150.10 / DB_USER=dpendance / DB_NAME=dpendance
  → <数据库主机> / opendance_app / opendance(web-server/.env.example、
  deploy/.env.example、contracts/database.md ×3 库、project.yaml、README.txt
  的建库 SQL、tasks/001、tasks/002)
- 内网网段 192.168.150.0/24 / 192.168.150.20 / 192.168.1.10
  → RFC 5737 文档网段 192.0.2.0/24 等(含测试夹具,行为不变)
- 真实 Gitea 域名 → https://<Gitea 地址>/<owner>/<repo>(systemd 单元、
  Windows/Android README 的交叉链接)
- 测试夹具里的真实口令字面量 → 虚构口令(仍覆盖特殊字符场景)
- 保留 /opt/opendance、/var/lib/opendance 等产品默认安装路径(非敏感)

README.txt 易混淆字符:
- 全角拉丁字母(I/K 等)与全角 ASCII 标点((),:;?)→ 半角 + 中文排版空格
- 正文里的乱码字节样例(模å…)→ 改为文字描述
- × → x,★ → *
- 注意:代码/测试/契约里的全角「IK」是 MMD 真实骨骼名,**必须保留**,
  已回滚误改(mmd-skeleton.ts、mmd-vmd.spec.ts、inspect-vmd.mjs、各契约)

验证:168 项单测通过(含 VMD 31 项,确认全角骨骼名未被破坏)。
2026-09-15 21:25:25 +08:00

114 lines
4.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { describeAccess, describeDualAccess, preferredUrl } from '../src/config/access-hint';
/**
* 启动日志里"该访问哪个地址"必须与真实绑定一致。
* 之前的写法直接把 bindHost 拼进 URL,绑定 0.0.0.0 时会打印
* http://0.0.0.0:3000——浏览器打不开,排障时会被误导。
*/
describe('启动日志的访问地址推导', () => {
it('绑定回环时只给回环地址,并明确标注仅本机可访问', () => {
for (const host of ['127.0.0.1', 'localhost', '::1']) {
const { listen, urls } = describeAccess(host, 3000);
expect(listen).toContain(host);
expect(listen).toContain('仅本机可访问');
expect(urls).toEqual([`http://${host}:3000`]);
}
});
it('绑定通配地址时给出回环 + 真实网卡地址,且绝不出现 0.0.0.0', () => {
for (const host of ['0.0.0.0', '::', '*', '']) {
const { listen, urls } = describeAccess(host, 3000);
expect(listen).toContain('全部网卡');
expect(urls[0]).toBe('http://127.0.0.1:3000');
expect(urls.every((url) => !url.includes('0.0.0.0'))).toBe(true);
// 至少要有回环地址可用
expect(urls.length).toBeGreaterThanOrEqual(1);
}
});
it('绑定具体网卡时只给该地址,并提示范围', () => {
const { listen, urls } = describeAccess('192.0.2.20', 8080);
expect(listen).toBe('192.0.2.20:8080(仅该地址可访问)');
expect(urls).toEqual(['http://192.0.2.20:8080']);
});
it('端口号被完整带进每个地址', () => {
expect(describeAccess('0.0.0.0', 3123).urls.every((url) => url.endsWith(':3123'))).toBe(true);
expect(describeAccess('10.0.0.1', 80).urls).toEqual(['http://10.0.0.1:80']);
});
});
/**
* 双端口模式下,启动日志必须分别说清"客户端调接口用哪个地址"和
* "浏览器看管理页面用哪个地址",否则客户端会把管理端口当成 API 端口。
*/
describe('双端口访问提示', () => {
it('分端口时分别给出软件 API 与管理端两行,并标明各自职责', () => {
const hint = describeDualAccess({
apiHost: '0.0.0.0',
apiPort: 3111,
adminHost: '0.0.0.0',
adminPort: 3112,
});
expect(hint.sharedPort).toBe(false);
expect(hint.apiLine).toContain('软件 API 端口:');
expect(hint.apiLine).toContain(':3111');
expect(hint.apiLine).toContain('/api、/health、/static、/contracts');
expect(hint.adminLine).toContain('管理端端口:');
expect(hint.adminLine).toContain(':3112');
expect(hint.adminLine).toContain('浏览器访问');
expect(hint.adminLine).not.toContain('未分端口');
expect(hint.sharedNotice).toBe('');
});
it('绝不把 0.0.0.0 写进可访问地址', () => {
const hint = describeDualAccess({
apiHost: '0.0.0.0',
apiPort: 3000,
adminHost: '0.0.0.0',
adminPort: 3001,
});
expect(hint.apiLine).not.toContain('0.0.0.0');
expect(hint.adminLine).not.toContain('0.0.0.0');
expect(hint.api.urls[0]).toBe('http://127.0.0.1:3000');
});
it('ADMIN_PORT === PORT 时标记为未分端口并给出提示', () => {
const hint = describeDualAccess({
apiHost: '0.0.0.0',
apiPort: 3000,
adminHost: '0.0.0.0',
adminPort: 3000,
});
expect(hint.sharedPort).toBe(true);
expect(hint.sharedNotice).toContain('未分端口');
expect(hint.adminLine).toContain('未分端口');
expect(hint.adminLine).toContain(':3000');
});
it('绑定回环时保留"只能本机访问"的告警依据', () => {
const hint = describeDualAccess({
apiHost: '127.0.0.1',
apiPort: 3000,
adminHost: '127.0.0.1',
adminPort: 3001,
});
expect(hint.loopbackOnly).toBe(true);
expect(hint.apiLine).toContain('http://127.0.0.1:3000');
});
it('preferredUrl 优先挑真实网卡地址,没有才回落到回环地址', () => {
expect(
preferredUrl({
listen: '',
urls: ['http://127.0.0.1:3000', 'http://192.0.2.10:3000'],
loopbackOnly: false,
}),
).toBe('http://192.0.2.10:3000');
expect(
preferredUrl({ listen: '', urls: ['http://127.0.0.1:3000'], loopbackOnly: true }),
).toBe('http://127.0.0.1:3000');
});
});