test(platform): verify client propagation
This commit is contained in:
parent
3503ac86ad
commit
efa20979fc
47
mobile/src/api/client.test.ts
Normal file
47
mobile/src/api/client.test.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { API_CONFIG } from './config';
|
||||
import { getApiClient } from './client';
|
||||
|
||||
const { getAccessTokenMock } = vi.hoisted(() => ({
|
||||
getAccessTokenMock: vi.fn(),
|
||||
}));
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
|
||||
vi.mock('./auth', () => ({
|
||||
getAuthClient: () => ({
|
||||
getAccessToken: getAccessTokenMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
function jsonResponse(data: unknown, status = 200) {
|
||||
return {
|
||||
ok: status >= 200 && status < 300,
|
||||
status,
|
||||
statusText: status === 200 ? 'OK' : 'Error',
|
||||
json: () => Promise.resolve(data),
|
||||
};
|
||||
}
|
||||
|
||||
describe('mobile API client', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset();
|
||||
getAccessTokenMock.mockReset();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
});
|
||||
|
||||
it('propagates product identity, auth token, and request id through the shared API client', async () => {
|
||||
getAccessTokenMock.mockReturnValue('mobile-token');
|
||||
fetchMock.mockResolvedValue(jsonResponse({ ok: true }));
|
||||
|
||||
await getApiClient().fetch('/workspaces');
|
||||
|
||||
const [, init] = fetchMock.mock.calls[0] as [string, RequestInit];
|
||||
const headers = init.headers as Record<string, string>;
|
||||
|
||||
expect(headers['x-product-id']).toBe(API_CONFIG.productId);
|
||||
expect(headers.Authorization).toBe('Bearer mobile-token');
|
||||
expect(headers['x-request-id']).toBeTruthy();
|
||||
});
|
||||
});
|
||||
46
mobile/src/lib/platform-api.test.ts
Normal file
46
mobile/src/lib/platform-api.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { API_CONFIG, PRODUCT_ID } from '../api/config';
|
||||
|
||||
const { createPlatformClientMock, getMock, getAccessTokenMock } = vi.hoisted(() => ({
|
||||
createPlatformClientMock: vi.fn(),
|
||||
getMock: vi.fn(),
|
||||
getAccessTokenMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@bytelyst/platform-client', () => ({
|
||||
createPlatformClient: createPlatformClientMock,
|
||||
}));
|
||||
|
||||
vi.mock('./auth-helpers', () => ({
|
||||
getAccessToken: getAccessTokenMock,
|
||||
}));
|
||||
|
||||
import { getUserSettings } from './platform-api';
|
||||
|
||||
describe('mobile platform API wrapper', () => {
|
||||
beforeEach(() => {
|
||||
createPlatformClientMock.mockReset();
|
||||
getMock.mockReset();
|
||||
getAccessTokenMock.mockReset();
|
||||
getAccessTokenMock.mockReturnValue('mobile-platform-token');
|
||||
getMock.mockResolvedValue({ theme: 'dark' });
|
||||
createPlatformClientMock.mockReturnValue({
|
||||
get: getMock,
|
||||
put: vi.fn(),
|
||||
del: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it('configures the shared platform client with product identity and auth token access', async () => {
|
||||
await getUserSettings();
|
||||
|
||||
expect(createPlatformClientMock).toHaveBeenCalledWith({
|
||||
baseUrl: API_CONFIG.platformBaseUrl,
|
||||
productId: PRODUCT_ID,
|
||||
getAccessToken: getAccessTokenMock,
|
||||
});
|
||||
expect(createPlatformClientMock.mock.calls[0][0].getAccessToken()).toBe('mobile-platform-token');
|
||||
expect(getMock).toHaveBeenCalledWith('/settings');
|
||||
});
|
||||
});
|
||||
59
web/src/lib/api-helpers.test.ts
Normal file
59
web/src/lib/api-helpers.test.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createNotesApiClient, getAccessToken } from "@/lib/api-helpers";
|
||||
import { PRODUCT_ID } from "@/lib/product-config";
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
const storage = new Map<string, string>();
|
||||
const localStorageMock = {
|
||||
getItem: vi.fn((key: string) => storage.get(key) ?? null),
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
storage.set(key, value);
|
||||
}),
|
||||
clear: vi.fn(() => {
|
||||
storage.clear();
|
||||
}),
|
||||
};
|
||||
|
||||
function jsonResponse(data: unknown, status = 200) {
|
||||
return {
|
||||
ok: status >= 200 && status < 300,
|
||||
status,
|
||||
statusText: status === 200 ? "OK" : "Error",
|
||||
json: () => Promise.resolve(data),
|
||||
};
|
||||
}
|
||||
|
||||
describe("api-helpers", () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
storage.clear();
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
configurable: true,
|
||||
value: localStorageMock,
|
||||
});
|
||||
vi.stubGlobal("localStorage", localStorageMock);
|
||||
});
|
||||
|
||||
it("reads the product-scoped access token from localStorage", () => {
|
||||
localStorage.setItem(`${PRODUCT_ID}_access_token`, "web-token");
|
||||
|
||||
expect(getAccessToken()).toBe("web-token");
|
||||
});
|
||||
|
||||
it("propagates product identity, auth token, and request id through the shared API client", async () => {
|
||||
localStorage.setItem(`${PRODUCT_ID}_access_token`, "web-token");
|
||||
fetchMock.mockResolvedValue(jsonResponse({ ok: true }));
|
||||
|
||||
const api = createNotesApiClient();
|
||||
await api.fetch("/workspaces");
|
||||
|
||||
const [, init] = fetchMock.mock.calls[0] as [string, RequestInit];
|
||||
const headers = init.headers as Record<string, string>;
|
||||
|
||||
expect(headers["x-product-id"]).toBe(PRODUCT_ID);
|
||||
expect(headers.Authorization).toBe("Bearer web-token");
|
||||
expect(headers["x-request-id"]).toBeTruthy();
|
||||
});
|
||||
});
|
||||
46
web/src/lib/platform.test.ts
Normal file
46
web/src/lib/platform.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { PLATFORM_SERVICE_URL, PRODUCT_ID } from "@/lib/product-config";
|
||||
|
||||
const { createPlatformClientMock, getMock, getAccessTokenMock } = vi.hoisted(() => ({
|
||||
createPlatformClientMock: vi.fn(),
|
||||
getMock: vi.fn(),
|
||||
getAccessTokenMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@bytelyst/platform-client", () => ({
|
||||
createPlatformClient: createPlatformClientMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/api-helpers", () => ({
|
||||
getAccessToken: getAccessTokenMock,
|
||||
}));
|
||||
|
||||
import { getUserSettings } from "@/lib/platform";
|
||||
|
||||
describe("platform client wrapper", () => {
|
||||
beforeEach(() => {
|
||||
createPlatformClientMock.mockReset();
|
||||
getMock.mockReset();
|
||||
getAccessTokenMock.mockReset();
|
||||
getAccessTokenMock.mockReturnValue("web-platform-token");
|
||||
getMock.mockResolvedValue({ theme: "dark" });
|
||||
createPlatformClientMock.mockReturnValue({
|
||||
get: getMock,
|
||||
put: vi.fn(),
|
||||
del: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("configures the shared platform client with product identity and auth token access", async () => {
|
||||
await getUserSettings();
|
||||
|
||||
expect(createPlatformClientMock).toHaveBeenCalledWith({
|
||||
baseUrl: PLATFORM_SERVICE_URL,
|
||||
productId: PRODUCT_ID,
|
||||
getAccessToken: getAccessTokenMock,
|
||||
});
|
||||
expect(createPlatformClientMock.mock.calls[0][0].getAccessToken()).toBe("web-platform-token");
|
||||
expect(getMock).toHaveBeenCalledWith("/settings");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user