feat: modules and resources domains, drop removed domains, regenerated models
This commit is contained in:
+41
-115
@@ -6,9 +6,30 @@ function jsonResponse(body: unknown): Response {
|
||||
return new Response(JSON.stringify(body), { status: 200 });
|
||||
}
|
||||
|
||||
function stubKernelFetch(
|
||||
extra?: (url: string) => Response | undefined,
|
||||
): ReturnType<typeof vi.fn> {
|
||||
return vi.fn().mockImplementation((url: string) => {
|
||||
const custom = extra?.(url);
|
||||
if (custom) return Promise.resolve(custom);
|
||||
if (String(url).includes('/sdk/v1/authorization/device')) {
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'access-token-123',
|
||||
refreshToken: 'refresh-token-456',
|
||||
}));
|
||||
}
|
||||
if (String(url).includes('/sdk/v1/catalog')) {
|
||||
return Promise.resolve(jsonResponse({ currencies: [] }));
|
||||
}
|
||||
if (String(url).includes('/sdk/v1/sync')) {
|
||||
return Promise.resolve(jsonResponse({}));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ player: null, wallets: [] }));
|
||||
});
|
||||
}
|
||||
|
||||
describe('AuthService', () => {
|
||||
beforeEach(() => {
|
||||
// Mock crypto.randomUUID for deterministic device ID.
|
||||
vi.stubGlobal('crypto', {
|
||||
randomUUID: () => '00000000-0000-4000-a000-000000000001',
|
||||
});
|
||||
@@ -17,20 +38,11 @@ describe('AuthService', () => {
|
||||
|
||||
it('loginWithDevice sends correct request body', async () => {
|
||||
const client = createTestClient();
|
||||
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'access-token-123',
|
||||
refreshToken: 'refresh-token-456',
|
||||
}));
|
||||
});
|
||||
const fetchMock = stubKernelFetch();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const response = await client.auth.loginWithDevice({ region: 'us', language: 'en' });
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(9);
|
||||
const [url, init] = fetchMock.mock.calls[0];
|
||||
expect(url).toContain('/sdk/v1/authorization/device');
|
||||
const body = JSON.parse(init.body);
|
||||
@@ -40,29 +52,24 @@ describe('AuthService', () => {
|
||||
expect(body.language).toBe('en');
|
||||
expect(response.accessToken).toBe('access-token-123');
|
||||
expect(response.refreshToken).toBe('refresh-token-456');
|
||||
expect(fetchMock.mock.calls.map(([url]) => new URL(String(url)).pathname)).toEqual([
|
||||
'/sdk/v1/authorization/device',
|
||||
'/sdk/v1/remote-configs',
|
||||
'/sdk/v1/player/information',
|
||||
'/sdk/v1/stores',
|
||||
'/sdk/v1/catalog',
|
||||
'/sdk/v1/scenarios/pending',
|
||||
'/sdk/v1/scenarios/trigger',
|
||||
'/sdk/v1/player/information',
|
||||
'/sdk/v1/sync',
|
||||
]);
|
||||
const paths = fetchMock.mock.calls.map(([callUrl]) => new URL(String(callUrl)).pathname);
|
||||
expect(paths[0]).toBe('/sdk/v1/authorization/device');
|
||||
expect(paths).toContain('/sdk/v1/player/information');
|
||||
expect(paths).toContain('/sdk/v1/catalog');
|
||||
expect(paths).toContain('/sdk/v1/sync');
|
||||
expect(paths).not.toContain('/sdk/v1/remote-configs');
|
||||
expect(paths).not.toContain('/sdk/v1/stores');
|
||||
expect(paths).not.toContain('/sdk/v1/scenarios/pending');
|
||||
expect(paths).not.toContain('/sdk/v1/scenarios/trigger');
|
||||
});
|
||||
|
||||
it('loginWithDevice saves tokens on success', async () => {
|
||||
const client = createTestClient();
|
||||
vi.stubGlobal('fetch', vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
vi.stubGlobal('fetch', stubKernelFetch((url) => {
|
||||
if (url.includes('/sdk/v1/authorization/device')) {
|
||||
return jsonResponse({ accessToken: 'at', refreshToken: 'rt' });
|
||||
}
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'at',
|
||||
refreshToken: 'rt',
|
||||
}));
|
||||
return undefined;
|
||||
}));
|
||||
|
||||
await client.auth.loginWithDevice();
|
||||
@@ -94,23 +101,16 @@ describe('AuthService', () => {
|
||||
|
||||
it('loginWithDevice sends token in Authorization header after login', async () => {
|
||||
const client = createTestClient();
|
||||
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
||||
const fetchMock = stubKernelFetch((url) => {
|
||||
if (url.includes('/sdk/v1/authorization/device')) {
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'at2',
|
||||
refreshToken: 'rt2',
|
||||
}));
|
||||
return jsonResponse({ accessToken: 'at2', refreshToken: 'rt2' });
|
||||
}
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ player: null, wallets: [] }));
|
||||
return undefined;
|
||||
});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await client.auth.loginWithDevice();
|
||||
|
||||
// Subsequent authenticated calls should include the token.
|
||||
const response = await client.player.reload();
|
||||
expect(response).toEqual({ player: null, wallets: [] });
|
||||
const playerCall = fetchMock.mock.calls.find(([url]) =>
|
||||
@@ -120,71 +120,9 @@ describe('AuthService', () => {
|
||||
expect(headers['Authorization']).toBe('Bearer at2');
|
||||
});
|
||||
|
||||
it('loginWithDevice only fires player_login from the client runtime', async () => {
|
||||
const client = createTestClient();
|
||||
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/authorization/device')) {
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'at-new',
|
||||
refreshToken: 'rt-new',
|
||||
}));
|
||||
}
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
if (url.includes('/sdk/v1/scenarios/trigger')) {
|
||||
return Promise.resolve(jsonResponse({ plans: [] }));
|
||||
}
|
||||
if (url.includes('/sdk/v1/stores')) {
|
||||
return Promise.resolve(jsonResponse({ stores: [], total: 0 }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ player: null, wallets: [] }));
|
||||
});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await client.auth.loginWithDevice();
|
||||
|
||||
const triggerBodies = fetchMock.mock.calls
|
||||
.filter(([url]) => String(url).includes('/sdk/v1/scenarios/trigger'))
|
||||
.map(([, init]) => JSON.parse(init.body));
|
||||
expect(triggerBodies.map((body) => body.event)).toEqual(['player_login']);
|
||||
});
|
||||
|
||||
it('loginWithDevice isolates post-login scenario trigger failures', async () => {
|
||||
const client = createTestClient();
|
||||
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
vi.stubGlobal('fetch', vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/authorization/device')) {
|
||||
return Promise.resolve(jsonResponse({
|
||||
accessToken: 'at',
|
||||
refreshToken: 'rt',
|
||||
}));
|
||||
}
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
if (url.includes('/sdk/v1/scenarios/trigger')) {
|
||||
return Promise.resolve(new Response('scenario failed', { status: 500, statusText: 'Internal Server Error' }));
|
||||
}
|
||||
if (url.includes('/sdk/v1/stores')) {
|
||||
return Promise.resolve(jsonResponse({ stores: [], total: 0 }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ player: null, wallets: [] }));
|
||||
}));
|
||||
|
||||
await expect(client.auth.loginWithDevice()).resolves.toMatchObject({ accessToken: 'at' });
|
||||
expect(warn).toHaveBeenCalled();
|
||||
warn.mockRestore();
|
||||
});
|
||||
|
||||
it('loginWithDevice defaults to region global and language en', async () => {
|
||||
const client = createTestClient();
|
||||
const fetchMock = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ accessToken: 'at', refreshToken: 'rt' }));
|
||||
});
|
||||
const fetchMock = stubKernelFetch();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await client.auth.loginWithDevice();
|
||||
@@ -203,21 +141,9 @@ describe('auth state', () => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
function stubLoginFlow(): void {
|
||||
vi.stubGlobal('fetch', vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes('/sdk/v1/remote-configs')) {
|
||||
return Promise.resolve(jsonResponse({ configs: {} }));
|
||||
}
|
||||
if (url.includes('/sdk/v1/scenarios/trigger')) {
|
||||
return Promise.resolve(jsonResponse({ plans: [] }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({ accessToken: 'at', refreshToken: 'rt' }));
|
||||
}));
|
||||
}
|
||||
|
||||
it('onAuthStateChange fires immediately, then on login and logout', async () => {
|
||||
const client = createTestClient();
|
||||
stubLoginFlow();
|
||||
vi.stubGlobal('fetch', stubKernelFetch());
|
||||
|
||||
const states: string[] = [];
|
||||
const unsubscribe = client.auth.onAuthStateChange((state) => states.push(state));
|
||||
|
||||
Reference in New Issue
Block a user