test(mobile): verify platform lifecycle clients
This commit is contained in:
parent
4e4a25a179
commit
738fa5b894
11
mobile/src/lib/app-metadata.test.ts
Normal file
11
mobile/src/lib/app-metadata.test.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { APP_PLATFORM, APP_VERSION, BUILD_NUMBER, OS_VERSION } from './app-metadata';
|
||||
|
||||
describe('mobile app metadata', () => {
|
||||
it('derives platform and release metadata for shared platform clients', () => {
|
||||
expect(APP_PLATFORM).toBe('ios');
|
||||
expect(APP_VERSION).toBe('0.1.0');
|
||||
expect(BUILD_NUMBER).toBe('1');
|
||||
expect(OS_VERSION).toBe('17.0');
|
||||
});
|
||||
});
|
||||
186
mobile/src/lib/platform.test.ts
Normal file
186
mobile/src/lib/platform.test.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { API_CONFIG, PRODUCT_ID } from '../api/config';
|
||||
import { mmkvStorage } from '../store/mmkv-storage';
|
||||
import { APP_VERSION, BUILD_NUMBER, OS_VERSION } from './app-metadata';
|
||||
|
||||
const {
|
||||
blobClientMock,
|
||||
createBlobClientMock,
|
||||
createFeatureFlagClientMock,
|
||||
createKillSwitchClientMock,
|
||||
createTelemetryClientMock,
|
||||
diagnosticsGetInstanceMock,
|
||||
featureFlagClientMock,
|
||||
getAccessTokenMock,
|
||||
killSwitchClientMock,
|
||||
telemetryClientMock,
|
||||
} = vi.hoisted(() => {
|
||||
const telemetryClient = {
|
||||
flush: vi.fn(),
|
||||
init: vi.fn(),
|
||||
trackEvent: vi.fn(),
|
||||
};
|
||||
const featureFlagClient = {
|
||||
getValue: vi.fn(),
|
||||
init: vi.fn(),
|
||||
isEnabled: vi.fn(),
|
||||
};
|
||||
const killSwitchClient = {
|
||||
check: vi.fn(),
|
||||
};
|
||||
const blobClient = {
|
||||
upload: vi.fn(),
|
||||
};
|
||||
|
||||
return {
|
||||
telemetryClientMock: telemetryClient,
|
||||
featureFlagClientMock: featureFlagClient,
|
||||
killSwitchClientMock: killSwitchClient,
|
||||
blobClientMock: blobClient,
|
||||
createTelemetryClientMock: vi.fn(() => telemetryClient),
|
||||
createFeatureFlagClientMock: vi.fn(() => featureFlagClient),
|
||||
createKillSwitchClientMock: vi.fn(() => killSwitchClient),
|
||||
createBlobClientMock: vi.fn(() => blobClient),
|
||||
diagnosticsGetInstanceMock: vi.fn(() => ({ capture: vi.fn() })),
|
||||
getAccessTokenMock: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@bytelyst/telemetry-client', () => ({
|
||||
createTelemetryClient: createTelemetryClientMock,
|
||||
}));
|
||||
|
||||
vi.mock('@bytelyst/feature-flag-client', () => ({
|
||||
createFeatureFlagClient: createFeatureFlagClientMock,
|
||||
}));
|
||||
|
||||
vi.mock('@bytelyst/kill-switch-client', () => ({
|
||||
createKillSwitchClient: createKillSwitchClientMock,
|
||||
}));
|
||||
|
||||
vi.mock('@bytelyst/blob-client', () => ({
|
||||
createBlobClient: createBlobClientMock,
|
||||
}));
|
||||
|
||||
vi.mock('@bytelyst/diagnostics-client', () => ({
|
||||
DiagnosticsClient: {
|
||||
getInstance: diagnosticsGetInstanceMock,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('./auth-helpers', () => ({
|
||||
getAccessToken: getAccessTokenMock,
|
||||
}));
|
||||
|
||||
async function loadPlatform() {
|
||||
vi.resetModules();
|
||||
return import('./platform');
|
||||
}
|
||||
|
||||
describe('mobile platform lifecycle clients', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mmkvStorage.removeItem(`${PRODUCT_ID}_install_id`);
|
||||
getAccessTokenMock.mockReturnValue('mobile-token');
|
||||
featureFlagClientMock.init.mockResolvedValue(undefined);
|
||||
featureFlagClientMock.isEnabled.mockReturnValue(true);
|
||||
featureFlagClientMock.getValue.mockImplementation((_key: string, fallback: unknown) => fallback);
|
||||
killSwitchClientMock.check.mockResolvedValue({ disabled: false, message: null });
|
||||
});
|
||||
|
||||
it('configures telemetry, flags, kill switch, and blob clients with product metadata', async () => {
|
||||
const platform = await loadPlatform();
|
||||
|
||||
expect(createTelemetryClientMock).toHaveBeenCalledWith({
|
||||
productId: PRODUCT_ID,
|
||||
baseUrl: API_CONFIG.platformBaseUrl,
|
||||
endpoint: '/telemetry/events',
|
||||
platform: 'mobile',
|
||||
channel: 'notelett_mobile',
|
||||
transport: 'fetch',
|
||||
appVersion: APP_VERSION,
|
||||
buildNumber: BUILD_NUMBER,
|
||||
releaseChannel: 'dev',
|
||||
osFamily: OS_VERSION,
|
||||
});
|
||||
expect(createFeatureFlagClientMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseUrl: API_CONFIG.platformBaseUrl,
|
||||
productId: PRODUCT_ID,
|
||||
platform: 'mobile',
|
||||
pollIntervalMs: 5 * 60 * 1000,
|
||||
getAccessToken: getAccessTokenMock,
|
||||
}),
|
||||
);
|
||||
expect(createKillSwitchClientMock).toHaveBeenCalledWith({
|
||||
baseUrl: API_CONFIG.platformBaseUrl,
|
||||
productId: PRODUCT_ID,
|
||||
platform: 'mobile',
|
||||
});
|
||||
expect(createBlobClientMock).toHaveBeenCalledWith({
|
||||
baseUrl: API_CONFIG.platformBaseUrl,
|
||||
productId: PRODUCT_ID,
|
||||
getAccessToken: getAccessTokenMock,
|
||||
});
|
||||
expect(platform.blobClient).toBe(blobClientMock);
|
||||
});
|
||||
|
||||
it('initializes telemetry and feature flags once', async () => {
|
||||
const { initPlatform } = await loadPlatform();
|
||||
|
||||
await initPlatform();
|
||||
await initPlatform();
|
||||
|
||||
expect(telemetryClientMock.init).toHaveBeenCalledTimes(1);
|
||||
expect(telemetryClientMock.trackEvent).toHaveBeenCalledWith('info', 'app_shell', 'mobile_app_initialized');
|
||||
expect(featureFlagClientMock.init).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('keeps platform initialization best-effort when feature flags fail', async () => {
|
||||
const { initPlatform } = await loadPlatform();
|
||||
featureFlagClientMock.init.mockRejectedValueOnce(new Error('flags down'));
|
||||
|
||||
await expect(initPlatform()).resolves.toBeUndefined();
|
||||
|
||||
expect(telemetryClientMock.init).toHaveBeenCalled();
|
||||
expect(telemetryClientMock.trackEvent).toHaveBeenCalledWith('info', 'app_shell', 'mobile_app_initialized');
|
||||
});
|
||||
|
||||
it('delegates feature checks, kill switch checks, and telemetry flushes', async () => {
|
||||
const { checkKillSwitch, flushTelemetry, getFeatureValue, isFeatureEnabled } = await loadPlatform();
|
||||
|
||||
expect(isFeatureEnabled('mobile.capture')).toBe(true);
|
||||
expect(getFeatureValue('mobile.limit', 10)).toBe(10);
|
||||
await expect(checkKillSwitch()).resolves.toEqual({ disabled: false, message: null });
|
||||
flushTelemetry();
|
||||
|
||||
expect(featureFlagClientMock.isEnabled).toHaveBeenCalledWith('mobile.capture');
|
||||
expect(featureFlagClientMock.getValue).toHaveBeenCalledWith('mobile.limit', 10);
|
||||
expect(killSwitchClientMock.check).toHaveBeenCalled();
|
||||
expect(telemetryClientMock.flush).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('configures diagnostics with install id, app metadata, and auth token access', async () => {
|
||||
const { getDiagnosticsClient } = await loadPlatform();
|
||||
const { mmkvStorage: platformStorage } = await import('../store/mmkv-storage');
|
||||
platformStorage.setItem(`${PRODUCT_ID}_install_id`, 'install-1');
|
||||
|
||||
getDiagnosticsClient();
|
||||
|
||||
expect(diagnosticsGetInstanceMock).toHaveBeenCalledWith({
|
||||
productId: PRODUCT_ID,
|
||||
serverUrl: API_CONFIG.platformBaseUrl,
|
||||
platform: 'mobile',
|
||||
channel: 'notelett_mobile',
|
||||
anonymousInstallId: 'install-1',
|
||||
osFamily: OS_VERSION,
|
||||
appVersion: APP_VERSION,
|
||||
buildNumber: BUILD_NUMBER,
|
||||
releaseChannel: 'dev',
|
||||
getAuthToken: expect.any(Function),
|
||||
});
|
||||
const calls = diagnosticsGetInstanceMock.mock.calls as unknown as Array<[{ getAuthToken: () => string }]>;
|
||||
const options = calls[0][0];
|
||||
expect(options.getAuthToken()).toBe('mobile-token');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user