1491b5e357
- engine/ (DagWalker, sessions, IndexedDbPlanStore) deleted; server owns the graph - trigger/callback/counter carry PendingEffect; GET /sdk/v1/scenarios/pending polled (30s jittered heartbeat paused on hidden tab + wait-deadline timers) - client.effects public API unchanged (done/buy/dismiss/end/claim/battlepass/quest) - planStateStore removed; loginEvent kept; reconcile() makes server the source of truth - generated models regenerated; skills/README/CHANGELOG updated
79 lines
2.8 KiB
TypeScript
79 lines
2.8 KiB
TypeScript
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 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: NonNullable<RemoteConfig['valueType']>, 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: { 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);
|
|
});
|
|
});
|