Skip to content

Commit 311fbdb

Browse files
committed
Adding unit test cases
1 parent 11077d8 commit 311fbdb

File tree

2 files changed

+1037
-1
lines changed

2 files changed

+1037
-1
lines changed

packages/remote-config/test/api.test.ts

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
import { expect } from 'chai';
1919
import {
20+
ConfigUpdateObserver,
2021
ensureInitialized,
2122
fetchAndActivate,
2223
FetchResponse,
2324
getRemoteConfig,
24-
getString
25+
getString,
26+
onConfigUpdate
2527
} from '../src';
2628
import '../test/setup';
2729
import {
@@ -34,6 +36,8 @@ import * as sinon from 'sinon';
3436
import { Component, ComponentType } from '@firebase/component';
3537
import { FirebaseInstallations } from '@firebase/installations-types';
3638
import { openDatabase, APP_NAMESPACE_STORE } from '../src/storage/storage';
39+
import { ERROR_FACTORY, ErrorCode } from '../src/errors';
40+
import { RemoteConfig as RemoteConfigImpl } from '../src/remote_config';
3741

3842
const fakeFirebaseConfig = {
3943
apiKey: 'api-key',
@@ -151,4 +155,127 @@ describe('Remote Config API', () => {
151155
await ensureInitialized(rc);
152156
expect(getString(rc, 'foobar')).to.equal('hello world');
153157
});
158+
159+
describe('onConfigUpdate', () => {
160+
let capturedObserver: ConfigUpdateObserver | undefined;
161+
let rc: RemoteConfigImpl;
162+
let addObserverStub: sinon.SinonStub;
163+
let removeObserverStub: sinon.SinonStub;
164+
165+
beforeEach(() => {
166+
rc = getRemoteConfig(app) as RemoteConfigImpl;
167+
168+
addObserverStub = sinon
169+
.stub(rc._realtimeHandler, 'addObserver')
170+
.resolves();
171+
removeObserverStub = sinon
172+
.stub(rc._realtimeHandler, 'removeObserver')
173+
.resolves();
174+
175+
addObserverStub.callsFake(async (observer: ConfigUpdateObserver) => {
176+
capturedObserver = observer;
177+
});
178+
});
179+
180+
afterEach(() => {
181+
capturedObserver = undefined;
182+
addObserverStub.restore();
183+
removeObserverStub.restore();
184+
});
185+
186+
it('should call addObserver on the internal realtimeHandler', async () => {
187+
const mockObserver = {
188+
next: sinon.stub(),
189+
error: sinon.stub(),
190+
complete: sinon.stub()
191+
};
192+
await onConfigUpdate(rc, mockObserver);
193+
194+
expect(addObserverStub).to.have.been.calledOnce;
195+
expect(addObserverStub).to.have.been.calledWith(mockObserver);
196+
});
197+
198+
it('should return an unsubscribe function', async () => {
199+
const mockObserver = {
200+
next: sinon.stub(),
201+
error: sinon.stub(),
202+
complete: sinon.stub()
203+
};
204+
const unsubscribe = await onConfigUpdate(rc, mockObserver);
205+
206+
expect(unsubscribe).to.be.a('function');
207+
});
208+
209+
it('returned unsubscribe function should call removeObserver', async () => {
210+
const mockObserver = {
211+
next: sinon.stub(),
212+
error: sinon.stub(),
213+
complete: sinon.stub()
214+
};
215+
const unsubscribe = await onConfigUpdate(rc, mockObserver);
216+
217+
unsubscribe();
218+
219+
expect(removeObserverStub).to.have.been.calledOnce;
220+
expect(removeObserverStub).to.have.been.calledWith(mockObserver);
221+
});
222+
223+
it('observer.next should be called when realtimeHandler propagates an update', async () => {
224+
const mockObserver = {
225+
next: sinon.stub(),
226+
error: sinon.stub(),
227+
complete: sinon.stub()
228+
};
229+
await onConfigUpdate(rc, mockObserver);
230+
231+
if (capturedObserver && capturedObserver.next) {
232+
const mockConfigUpdate = { getUpdatedKeys: () => new Set(['new_key']) };
233+
capturedObserver.next(mockConfigUpdate);
234+
} else {
235+
expect.fail('Observer was not captured or next method is missing.');
236+
}
237+
238+
expect(mockObserver.next).to.have.been.calledOnce;
239+
expect(mockObserver.next).to.have.been.calledWithMatch({
240+
getUpdatedKeys: sinon.match.func
241+
});
242+
expect(
243+
mockObserver.next.getCall(0).args[0].getUpdatedKeys()
244+
).to.deep.equal(new Set(['new_key']));
245+
});
246+
247+
it('observer.error should be called when realtimeHandler propagates an error', async () => {
248+
const mockObserver = {
249+
next: sinon.stub(),
250+
error: sinon.stub(),
251+
complete: sinon.stub()
252+
};
253+
await onConfigUpdate(rc, mockObserver);
254+
255+
if (capturedObserver && capturedObserver.error) {
256+
const expectedOriginalErrorMessage = 'Realtime stream error';
257+
const mockError = ERROR_FACTORY.create(
258+
ErrorCode.CONFIG_UPDATE_STREAM_ERROR,
259+
{
260+
originalErrorMessage: expectedOriginalErrorMessage
261+
}
262+
);
263+
capturedObserver.error(mockError);
264+
} else {
265+
expect.fail('Observer was not captured or error method is missing.');
266+
}
267+
268+
expect(mockObserver.error).to.have.been.calledOnce;
269+
const receivedError = mockObserver.error.getCall(0).args[0];
270+
271+
expect(receivedError.message).to.equal(
272+
'Remote Config: The stream was not able to connect to the backend. (remoteconfig/stream-error).'
273+
);
274+
expect(receivedError).to.have.nested.property(
275+
'customData.originalErrorMessage',
276+
'Realtime stream error'
277+
);
278+
expect((receivedError as any).code).to.equal('remoteconfig/stream-error'); // Updated to expect the full prefixed code
279+
});
280+
});
154281
});

0 commit comments

Comments
 (0)