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