/** @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(); }); });