test(mobile): verify platform lifecycle clients

This commit is contained in:
Saravana Achu Mac 2026-05-05 13:02:14 -07:00
parent 4e4a25a179
commit 738fa5b894
2 changed files with 197 additions and 0 deletions

View 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');
});
});

View 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');
});
});