8701f43f75
- 删除渲染层传输层:services/clientApi.ts、clientHttp 的 fetchClientHttp 与超时封装整体退场,账户、认证、素材直传、错误上报与游戏分发发布改走 Rust typed command。 - 新增宿主封装:accountHost、editorAssetHost、platformSessionRetry、agentRuntimeErrorDetail 与 features/app-shell/workspacePreferences;渲染层不再读写 access token / refresh cookie,localStorage 只剩测试态旧键迁移 shim。 - 修复 DirectProject 失败文案 v2 映射与「查看详情」按需读取,画布标签面板高度夹紧、框选每帧重算、文档预览入口与场景标题栏 key、资源筛选面板样式。 - 补齐与调整回归:accountHost、agentRuntimeErrorDetail、clientAuthHost、clientHttp、platformSession、platformSessionRetry、resourceFilterPanelStyle、directThreadChatSubscription 等定向用例,并清理测试 harness 中无调用方的残留。
135 lines
4.8 KiB
TypeScript
135 lines
4.8 KiB
TypeScript
// @vitest-environment node
|
||
/**
|
||
* 客户端服务器选择的边界。
|
||
*
|
||
* 正式包只跟随构建渠道(`VITE_AGC_PLATFORM_CHANNEL`),自定义地址只在本地 Vite debug 可用;
|
||
* 归一化必须拒绝把凭据、路径、查询、哈希或明文远程地址带进登录请求——这些值随后会作为
|
||
* origin 交给 Rust 与后端,渲染层这一层只负责即时反馈与内存投影。
|
||
*/
|
||
import { beforeEach, expect, test, vi } from 'vitest';
|
||
|
||
const { invoke, resolveTauriInvoke } = vi.hoisted(() => {
|
||
const invokeMock = vi.fn();
|
||
return {
|
||
invoke: invokeMock,
|
||
resolveTauriInvoke: vi.fn(() => invokeMock),
|
||
};
|
||
});
|
||
|
||
vi.mock('../src/app/tauri', () => ({ resolveTauriInvoke }));
|
||
|
||
async function loadClientHttp() {
|
||
return import('../src/services/clientHttp');
|
||
}
|
||
|
||
beforeEach(() => {
|
||
vi.resetModules();
|
||
vi.unstubAllEnvs();
|
||
invoke.mockReset();
|
||
resolveTauriInvoke.mockReturnValue(invoke);
|
||
});
|
||
|
||
test('非法服务器地址按原因逐条拒绝', async () => {
|
||
const { normalizeClientServerBaseUrl } = await loadClientHttp();
|
||
|
||
expect(() => normalizeClientServerBaseUrl('dev.genarrative.world')).toThrow(
|
||
'服务器地址无效',
|
||
);
|
||
expect(() => normalizeClientServerBaseUrl('ftp://example.com')).toThrow(
|
||
'服务器地址必须是纯 HTTP(S) 地址',
|
||
);
|
||
expect(() =>
|
||
normalizeClientServerBaseUrl('https://user:pass@example.com'),
|
||
).toThrow('服务器地址必须是纯 HTTP(S) 地址');
|
||
expect(() => normalizeClientServerBaseUrl('https://example.com/api')).toThrow(
|
||
'服务器地址必须是纯 HTTP(S) 地址',
|
||
);
|
||
expect(() =>
|
||
normalizeClientServerBaseUrl('https://example.com/?token=1'),
|
||
).toThrow('服务器地址必须是纯 HTTP(S) 地址');
|
||
expect(() =>
|
||
normalizeClientServerBaseUrl('https://example.com/#frag'),
|
||
).toThrow('服务器地址必须是纯 HTTP(S) 地址');
|
||
expect(() => normalizeClientServerBaseUrl('http://example.com')).toThrow(
|
||
'非本机服务器必须使用 HTTPS',
|
||
);
|
||
});
|
||
|
||
test('合法地址去掉尾部斜杠并保留回环端口', async () => {
|
||
const { normalizeClientServerBaseUrl } = await loadClientHttp();
|
||
|
||
expect(normalizeClientServerBaseUrl('https://dev.genarrative.world///')).toBe(
|
||
'https://dev.genarrative.world',
|
||
);
|
||
expect(normalizeClientServerBaseUrl(' http://127.0.0.1:10001/ ')).toBe(
|
||
'http://127.0.0.1:10001',
|
||
);
|
||
expect(normalizeClientServerBaseUrl('http://localhost:8080')).toBe(
|
||
'http://localhost:8080',
|
||
);
|
||
});
|
||
|
||
test('预设映射到渠道地址,自定义走归一化', async () => {
|
||
const mod = await loadClientHttp();
|
||
|
||
expect(
|
||
mod.getClientServerBaseUrl({ preset: 'release', customBaseUrl: '' }),
|
||
).toBe(mod.AGC_RELEASE_API_BASE_URL);
|
||
expect(mod.getClientServerBaseUrl({ preset: 'dev', customBaseUrl: '' })).toBe(
|
||
mod.AGC_DEVELOPMENT_API_BASE_URL,
|
||
);
|
||
expect(
|
||
mod.getClientServerBaseUrl({
|
||
preset: 'custom',
|
||
customBaseUrl: 'https://staging.example.com/',
|
||
}),
|
||
).toBe('https://staging.example.com');
|
||
expect(mod.isClientServerPreset('custom')).toBe(true);
|
||
expect(mod.isClientServerPreset('staging')).toBe(false);
|
||
});
|
||
|
||
test('开发态切换服务器会落盘并在内存中生效', async () => {
|
||
vi.stubEnv('DEV', true);
|
||
const mod = await loadClientHttp();
|
||
|
||
expect(mod.isClientServerSelectionEnabled()).toBe(true);
|
||
expect(
|
||
mod.setClientServerSelection({
|
||
preset: 'custom',
|
||
customBaseUrl: ' https://staging.example.com/ ',
|
||
}),
|
||
).toEqual({ preset: 'custom', customBaseUrl: 'https://staging.example.com' });
|
||
await Promise.resolve();
|
||
expect(invoke).toHaveBeenCalledWith('set_client_server_selection', {
|
||
preset: 'custom',
|
||
customBaseUrl: 'https://staging.example.com',
|
||
});
|
||
expect(mod.getClientServerBaseUrl()).toBe('https://staging.example.com');
|
||
});
|
||
|
||
test('开发态从 Rust 偏好恢复服务器选择', async () => {
|
||
vi.stubEnv('DEV', true);
|
||
const mod = await loadClientHttp();
|
||
invoke.mockResolvedValue({
|
||
clientServerSelection: {
|
||
preset: 'custom',
|
||
customBaseUrl: 'https://restored.example.com',
|
||
},
|
||
});
|
||
|
||
await expect(mod.hydrateClientServerSelection()).resolves.toEqual({
|
||
preset: 'custom',
|
||
customBaseUrl: 'https://restored.example.com',
|
||
});
|
||
expect(invoke).toHaveBeenCalledWith('read_workspace_preferences');
|
||
expect(mod.getClientServerSelection()).toEqual({
|
||
preset: 'custom',
|
||
customBaseUrl: 'https://restored.example.com',
|
||
});
|
||
});
|
||
|
||
// 「打包态不开放服务器切换」这条规则这里测不了:`import.meta.env.DEV` 在测试运行下恒为
|
||
// true,`vi.stubEnv('DEV', false)` 改不动它(Vite/Vitest 把它按构建期常量处理),因此
|
||
// `isClientServerSelectionEnabled()` 与 `getClientServerSelection()` 的打包分支只能在真实
|
||
// 发布构建里核对。本文件覆盖的是地址归一化、预设映射、开发态落盘与偏好恢复。
|