Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/sdk-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
target_file: 'packages/shared/sdk-client/dist/esm/index.mjs'
package_name: '@launchdarkly/js-client-sdk-common'
pr_number: ${{ github.event.number }}
size_limit: 38000
size_limit: 39000
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ function makeFlagManager() {

function makeSourceFactoryProvider() {
return {
createInitializerFactory: jest.fn((_entry: any) => jest.fn()),
createSynchronizerSlot: jest.fn((_entry: any) => createSynchronizerSlot(jest.fn())),
createInitializerFactory: jest.fn((entry: any) =>
entry?.type === 'cache' ? { create: jest.fn(), isCache: true } : { create: jest.fn() },
),
createSynchronizerSlot: jest.fn((_entry: any) => createSynchronizerSlot({ create: jest.fn() })),
};
}

Expand Down Expand Up @@ -983,7 +985,7 @@ it('uses per-mode fdv1Fallback pollInterval from MODE_TABLE for background mode'
const dsConfig = capturedDataSourceConfigs[0];
const fdv1Slot = dsConfig.synchronizerSlots[dsConfig.synchronizerSlots.length - 1];
// Invoke the factory to trigger createFDv1PollingSynchronizer.
fdv1Slot.factory(() => undefined);
fdv1Slot.factory.create(() => undefined);

// The FDv1 fallback synchronizer should use background's default (3600s = 3600000ms).
expect(createFDv1PollingSynchronizer).toHaveBeenCalledWith(
Expand Down Expand Up @@ -1022,6 +1024,25 @@ it('resolves identify immediately when initial mode has no sources', async () =>
manager.close();
});

it('builds offline mode with a cache-marked initializer factory and no synchronizers', async () => {
// Verifies that the offline mode passes an isCache-marked factory to the
// data source. FDv2DataSource is responsible for recognizing this
// cache-only configuration and completing initialization on a cache miss.
const manager = createFDv2DataManagerBase(makeBaseConfig({ foregroundMode: 'offline' }));

const { resolve, reject } = await identifyManager(manager);

expect(resolve).toHaveBeenCalledTimes(1);
expect(reject).not.toHaveBeenCalled();

const dsConfig = capturedDataSourceConfigs[0];
expect(dsConfig.synchronizerSlots).toEqual([]);
expect(dsConfig.initializerFactories).toHaveLength(1);
expect(dsConfig.initializerFactories[0].isCache).toBe(true);

manager.close();
});

it('does not identify after close', async () => {
const manager = createFDv2DataManagerBase(makeBaseConfig());
manager.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ beforeEach(() => {
});
mockCreateStreamingInitializer.mockReturnValue({ close: jest.fn() });
mockCreateStreamingSynchronizer.mockReturnValue({ close: jest.fn() });
mockCreateCacheInitializerFactory.mockReturnValue(jest.fn());
mockCreateCacheInitializerFactory.mockReturnValue({ create: jest.fn(), isCache: true });
mockMakeFDv2Requestor.mockReturnValue({ poll: jest.fn() });
});

Expand All @@ -124,7 +124,7 @@ it('creates a PollingInitializer for a polling initializer entry', () => {

expect(factory).toBeDefined();
const selectorGetter = () => 'some-selector';
factory!(selectorGetter);
factory!.create(selectorGetter);
expect(mockCreatePollingInitializer).toHaveBeenCalledWith(
ctx.requestor,
ctx.logger,
Expand All @@ -141,7 +141,7 @@ it('creates a StreamingInitializer for a streaming initializer entry', () => {

expect(factory).toBeDefined();
const selectorGetter = () => 'some-selector';
factory!(selectorGetter);
factory!.create(selectorGetter);
expect(mockCreateStreamingBase).toHaveBeenCalledWith(
expect.objectContaining({
requests: ctx.requests,
Expand Down Expand Up @@ -169,6 +169,7 @@ it('creates a CacheInitializer for a cache initializer entry', () => {
logger: ctx.logger,
});
expect(factory).toBe(mockCreateCacheInitializerFactory.mock.results[0].value);
expect(factory!.isCache).toBe(true);
});

it('returns undefined for an unknown initializer entry type', () => {
Expand Down Expand Up @@ -196,7 +197,7 @@ it('creates a PollingSynchronizer slot for a polling synchronizer entry', () =>
// Invoke the factory that was passed to createSynchronizerSlot
const factoryArg = mockCreateSynchronizerSlot.mock.calls[0][0];
const selectorGetter = () => 'sel';
factoryArg(selectorGetter);
factoryArg.create(selectorGetter);
expect(mockCreatePollingSynchronizer).toHaveBeenCalledWith(
ctx.requestor,
ctx.logger,
Expand All @@ -218,7 +219,7 @@ it('creates a StreamingSynchronizer slot for a streaming synchronizer entry', ()
// Invoke the factory that was passed to createSynchronizerSlot
const factoryArg = mockCreateSynchronizerSlot.mock.calls[0][0];
const selectorGetter = () => 'sel';
factoryArg(selectorGetter);
factoryArg.create(selectorGetter);
expect(mockCreateStreamingBase).toHaveBeenCalledWith(
expect.objectContaining({
requests: ctx.requests,
Expand Down Expand Up @@ -255,7 +256,7 @@ it('creates a new requestor when polling entry has endpoint overrides', () => {
expect(factory).toBeDefined();

const selectorGetter = () => undefined;
factory!(selectorGetter);
factory!.create(selectorGetter);

expect(mockMakeFDv2Requestor).toHaveBeenCalledWith(
ctx.plainContextString,
Expand Down Expand Up @@ -288,7 +289,7 @@ it('uses per-entry pollInterval override for polling synchronizer', () => {

const factoryArg = mockCreateSynchronizerSlot.mock.calls[0][0];
const selectorGetter = () => undefined;
factoryArg(selectorGetter);
factoryArg.create(selectorGetter);

expect(mockCreatePollingSynchronizer).toHaveBeenCalledWith(
ctx.requestor,
Expand All @@ -307,7 +308,7 @@ it('uses per-entry initialReconnectDelay override for streaming initializer', ()

const factory = provider.createInitializerFactory(entry, ctx);
expect(factory).toBeDefined();
factory!(() => undefined);
factory!.create(() => undefined);

expect(mockCreateStreamingBase).toHaveBeenCalledWith(
expect.objectContaining({
Expand All @@ -328,7 +329,7 @@ it('ping handler uses the factory selector getter, not a stale reference', () =>

let currentSelector: string | undefined = 'selector-v1';
const selectorGetter = () => currentSelector;
factory!(selectorGetter);
factory!.create(selectorGetter);

// Extract the pingHandler from the createStreamingBase call
const streamingBaseArgs = mockCreateStreamingBase.mock.calls[0][0];
Expand All @@ -352,7 +353,7 @@ it('ping handler uses per-entry endpoint-overridden requestor', () => {

const factory = provider.createInitializerFactory(entry, ctx);
expect(factory).toBeDefined();
factory!(() => undefined);
factory!.create(() => undefined);

// Extract the pingHandler from the createStreamingBase call
const streamingBaseArgs = mockCreateStreamingBase.mock.calls[0][0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function createInitializer(
context,
logger,
});
return factory(noSelector);
return factory.create(noSelector);
}

describe('CacheInitializer', () => {
Expand All @@ -51,6 +51,16 @@ describe('CacheInitializer', () => {
context = Context.fromLDContext({ kind: 'user', key: 'test-user' });
});

it('returns a factory marked as a cache initializer', () => {
const factory = createCacheInitializerFactory({
storage: makeMemoryStorage(),
crypto,
environmentNamespace: TEST_NAMESPACE,
context,
});
expect(factory.isCache).toBe(true);
});

it('returns a changeSet with cached flags when cache is present', async () => {
const storage = makeMemoryStorage();
const flags: Flags = {
Expand Down Expand Up @@ -234,7 +244,7 @@ describe('CacheInitializer', () => {
context,
});

const initializer = factory(selectorGetter);
const initializer = factory.create(selectorGetter);
const result = await initializer.run();

expect(result.type).toBe('changeSet');
Expand Down
Loading
Loading