e3682fd06f
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m31s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m33s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m40s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m21s
Project CI / AI game creator shell Rust crates (push) Successful in 3m11s
Project CI / Native shell tests (push) Successful in 16m15s
Project CI / Frontend tests (push) Successful in 14m57s
Project CI / Repository checks (push) Successful in 14m48s
Project CI / Backend tests (push) Successful in 20m7s
Project CI / AI game creator shell web tests (push) Successful in 6m20s
移除服务器选择并按来源隔离登录凭据 新增官网客户端下载入口和匿名平台聚合接口 根据发布清单自动展示Windows与macOS首装包 补齐Mac首装元数据和上传顺序校验 同步定向测试与下载发布规范
122 lines
4.3 KiB
TypeScript
122 lines
4.3 KiB
TypeScript
/** @vitest-environment jsdom */
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import {
|
|
getStoredAuthAccessToken as getApiAccessToken,
|
|
requestClientApi,
|
|
} from '../src/services/clientApi';
|
|
import {
|
|
clearStoredAuthAccessToken,
|
|
getStoredAuthAccessToken,
|
|
setStoredAuthAccessToken,
|
|
} from '../src/services/clientAuth';
|
|
import { AGC_DEVELOPMENT_API_BASE_URL } from '../src/services/clientHttp';
|
|
|
|
vi.mock('@tauri-apps/plugin-http', () => ({ fetch: vi.fn() }));
|
|
vi.mock('../src/services/errorReporting', () => ({
|
|
captureClientError: vi.fn(),
|
|
}));
|
|
|
|
const tokenKey = 'genarrative.auth.access-token.v1';
|
|
const originKey = 'genarrative.auth.access-token-origin.v1';
|
|
const selectionKey = 'genarrative.client.server-selection.v1';
|
|
|
|
describe('AGC platform credential origin', () => {
|
|
afterEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
it('preserves a credential already marked as dev', () => {
|
|
window.localStorage.setItem(tokenKey, 'existing-dev-token');
|
|
window.localStorage.setItem(originKey, AGC_DEVELOPMENT_API_BASE_URL);
|
|
|
|
expect(getStoredAuthAccessToken()).toBe('existing-dev-token');
|
|
expect(getApiAccessToken()).toBe('existing-dev-token');
|
|
expect(window.localStorage.getItem(originKey)).toBe(
|
|
AGC_DEVELOPMENT_API_BASE_URL,
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
JSON.stringify({ preset: 'dev', customBaseUrl: '' }),
|
|
JSON.stringify({
|
|
preset: 'custom',
|
|
customBaseUrl: `${AGC_DEVELOPMENT_API_BASE_URL}/`,
|
|
}),
|
|
JSON.stringify({ preset: 'release', customBaseUrl: '' }),
|
|
JSON.stringify({ preset: 'custom', customBaseUrl: 'https://example.com' }),
|
|
JSON.stringify({
|
|
preset: 'custom',
|
|
customBaseUrl: 'http://localhost:8082',
|
|
}),
|
|
JSON.stringify({ preset: 'unknown', customBaseUrl: '' }),
|
|
'invalid-json',
|
|
'null',
|
|
])(
|
|
'never infers a legacy credential origin from a saved preference: %s',
|
|
async (selection) => {
|
|
window.localStorage.setItem(tokenKey, 'other-server-token');
|
|
window.localStorage.setItem(selectionKey, selection);
|
|
vi.stubEnv('MODE', 'production');
|
|
const fetchMock = vi
|
|
.spyOn(globalThis, 'fetch')
|
|
.mockResolvedValue(
|
|
new Response(JSON.stringify({ result: true }), { status: 200 }),
|
|
);
|
|
|
|
await requestClientApi('/api/profile/dashboard', {}, '读取失败');
|
|
|
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
const [url, init] = fetchMock.mock.calls[0];
|
|
expect(url).toBe(`${AGC_DEVELOPMENT_API_BASE_URL}/api/profile/dashboard`);
|
|
expect(new Headers(init?.headers).get('Authorization')).toBeNull();
|
|
expect(window.localStorage.getItem(tokenKey)).toBeNull();
|
|
},
|
|
);
|
|
|
|
it.each([true, false])(
|
|
'clears an unmarked credential without a preference (development=%s)',
|
|
(development) => {
|
|
// Vitest 0.34 stores stubbed env values as strings; use a falsy value for DEV=false.
|
|
vi.stubEnv('DEV', development ? 'true' : '');
|
|
window.localStorage.setItem(tokenKey, 'legacy-token');
|
|
|
|
expect(getStoredAuthAccessToken()).toBe('');
|
|
},
|
|
);
|
|
|
|
it('does not relabel a credential that already belongs to another origin', () => {
|
|
window.localStorage.setItem(tokenKey, 'other-origin-token');
|
|
window.localStorage.setItem(originKey, 'https://www.genarrative.world');
|
|
window.localStorage.setItem(
|
|
selectionKey,
|
|
JSON.stringify({ preset: 'dev' }),
|
|
);
|
|
|
|
expect(getApiAccessToken()).toBe('');
|
|
expect(window.localStorage.getItem(tokenKey)).toBeNull();
|
|
expect(window.localStorage.getItem(originKey)).toBeNull();
|
|
});
|
|
|
|
it('stores new dev credentials with their origin and ignores old preferences', () => {
|
|
vi.stubEnv('DEV', false);
|
|
setStoredAuthAccessToken('new-token');
|
|
window.localStorage.setItem(
|
|
selectionKey,
|
|
JSON.stringify({ preset: 'release' }),
|
|
);
|
|
|
|
expect(getApiAccessToken()).toBe('new-token');
|
|
expect(getStoredAuthAccessToken('https://www.genarrative.world')).toBe('');
|
|
expect(() =>
|
|
setStoredAuthAccessToken('wrong-token', 'https://example.com'),
|
|
).toThrow('固定的 dev 服务');
|
|
expect(getStoredAuthAccessToken()).toBe('new-token');
|
|
clearStoredAuthAccessToken();
|
|
expect(window.localStorage.getItem(tokenKey)).toBeNull();
|
|
expect(window.localStorage.getItem(originKey)).toBeNull();
|
|
});
|
|
});
|