@@ -0,0 +1,101 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { RudderClient } from '../../src/client/RudderClient.js';
|
||||
import { createFakeTokenStore } from '../helpers/FakeTokenStore.js';
|
||||
import { createFakeGateway, type FakeGateway } from '../helpers/fakeGateway.js';
|
||||
import { stubDeterministicUuid } from '../helpers/memoryPlanStore.js';
|
||||
import { plan } from '../helpers/plan.js';
|
||||
import type { RemoteConfig } from '../../src/generated/remote-config.js';
|
||||
|
||||
interface GameRemoteConfig extends Record<string, unknown> {
|
||||
max_energy: number;
|
||||
drop_rate: number;
|
||||
pvp_enabled: boolean;
|
||||
welcome_text: string;
|
||||
boss: { hp: number; name: string };
|
||||
legacy: string;
|
||||
missing_declared: string;
|
||||
}
|
||||
|
||||
function cfg(key: string, value: string, valueType: string, active = true): RemoteConfig {
|
||||
return { key, value, valueType, active };
|
||||
}
|
||||
|
||||
function makeClient(): RudderClient<GameRemoteConfig> {
|
||||
return new RudderClient<GameRemoteConfig>({
|
||||
baseUrl: 'https://api.test.rudder.build',
|
||||
projectKey: 'test-project',
|
||||
tokenStore: createFakeTokenStore(),
|
||||
runtime: { planStateStore: null, loginEvent: null },
|
||||
});
|
||||
}
|
||||
|
||||
describe('E2E: remote config', () => {
|
||||
let gateway: FakeGateway;
|
||||
let client: RudderClient<GameRemoteConfig>;
|
||||
|
||||
beforeEach(() => {
|
||||
stubDeterministicUuid();
|
||||
gateway = createFakeGateway({
|
||||
remoteConfigs: {
|
||||
max_energy: cfg('max_energy', '50', 'int'),
|
||||
drop_rate: cfg('drop_rate', '0.25', 'float'),
|
||||
pvp_enabled: cfg('pvp_enabled', 'true', 'bool'),
|
||||
welcome_text: cfg('welcome_text', 'Hello!', 'string'),
|
||||
boss: cfg('boss', '{"hp":1000,"name":"Drake"}', 'json'),
|
||||
legacy: cfg('legacy', 'old', 'string', false), // inactive — must be excluded
|
||||
},
|
||||
});
|
||||
gateway.install();
|
||||
client = makeClient();
|
||||
});
|
||||
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
it('loads configs and reads them back with type coercion', async () => {
|
||||
await client.auth.loginWithDevice();
|
||||
|
||||
expect(client.remoteConfig.status).toBe('ready');
|
||||
expect(client.remoteConfig.get('max_energy', 0)).toBe(50);
|
||||
expect(client.remoteConfig.get('drop_rate', 0)).toBeCloseTo(0.25);
|
||||
expect(client.remoteConfig.get('pvp_enabled', false)).toBe(true);
|
||||
expect(client.remoteConfig.get('welcome_text', '')).toBe('Hello!');
|
||||
expect(client.remoteConfig.get('boss', { hp: 0, name: '' })).toEqual({
|
||||
hp: 1000,
|
||||
name: 'Drake',
|
||||
});
|
||||
});
|
||||
|
||||
it('inactive configs are not exposed; missing keys fall back to default', async () => {
|
||||
await client.auth.loginWithDevice();
|
||||
|
||||
expect(client.remoteConfig.get('legacy', 'DEFAULT')).toBe('DEFAULT');
|
||||
expect(client.remoteConfig.get('missing_declared', 'fallback')).toBe('fallback');
|
||||
});
|
||||
|
||||
it('get() before login returns the default (no throw)', () => {
|
||||
expect(client.remoteConfig.status).toBe('idle');
|
||||
expect(client.remoteConfig.get('max_energy', 99)).toBe(99);
|
||||
});
|
||||
|
||||
it('a remote_config_override scenario node patches the live cache', async () => {
|
||||
client = new RudderClient<GameRemoteConfig>({
|
||||
baseUrl: 'https://api.test.rudder.build',
|
||||
projectKey: 'test-project',
|
||||
tokenStore: createFakeTokenStore(),
|
||||
runtime: { planStateStore: null },
|
||||
});
|
||||
gateway.onEvent(
|
||||
'player_login',
|
||||
plan('boost')
|
||||
.node('override', 'remote_config_override', {
|
||||
patches: [{ path: 'drop_rate', valueType: 'float', value: '0.9' }],
|
||||
})
|
||||
.build(),
|
||||
);
|
||||
|
||||
await client.auth.loginWithDevice();
|
||||
|
||||
// The override is reflected immediately, without a reload.
|
||||
expect(client.remoteConfig.get('drop_rate', 0)).toBeCloseTo(0.9);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user