mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Feature Flags] Set RUM transaction.outcome (#200576)
This commit is contained in:
parent
30e075a1b6
commit
f9287a9545
2 changed files with 70 additions and 6 deletions
|
@ -8,10 +8,10 @@
|
|||
*/
|
||||
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { apm } from '@elastic/apm-rum';
|
||||
import { Transaction, apm } from '@elastic/apm-rum';
|
||||
import { type Client, OpenFeature, type Provider } from '@openfeature/web-sdk';
|
||||
import { coreContextMock } from '@kbn/core-base-browser-mocks';
|
||||
import type { FeatureFlagsStart } from '@kbn/core-feature-flags-browser';
|
||||
import type { FeatureFlagsSetup, FeatureFlagsStart } from '@kbn/core-feature-flags-browser';
|
||||
import { injectedMetadataServiceMock } from '@kbn/core-injected-metadata-browser-mocks';
|
||||
import type { InternalInjectedMetadataSetup } from '@kbn/core-injected-metadata-browser-internal';
|
||||
import { FeatureFlagsService } from '..';
|
||||
|
@ -63,7 +63,7 @@ describe('FeatureFlagsService Browser', () => {
|
|||
test('awaits initialization in the start context', async () => {
|
||||
const { setProvider } = featureFlagsService.setup({ injectedMetadata });
|
||||
let externalResolve: Function = () => void 0;
|
||||
const spy = jest.spyOn(OpenFeature, 'setProviderAndWait').mockImplementation(async () => {
|
||||
const spy = jest.spyOn(OpenFeature, 'setProviderAndWait').mockImplementationOnce(async () => {
|
||||
await new Promise((resolve) => {
|
||||
externalResolve = resolve;
|
||||
});
|
||||
|
@ -80,7 +80,7 @@ describe('FeatureFlagsService Browser', () => {
|
|||
|
||||
test('do not hold for too long during initialization', async () => {
|
||||
const { setProvider } = featureFlagsService.setup({ injectedMetadata });
|
||||
const spy = jest.spyOn(OpenFeature, 'setProviderAndWait').mockImplementation(async () => {
|
||||
const spy = jest.spyOn(OpenFeature, 'setProviderAndWait').mockImplementationOnce(async () => {
|
||||
await new Promise(() => {}); // never resolves
|
||||
});
|
||||
const apmCaptureErrorSpy = jest.spyOn(apm, 'captureError');
|
||||
|
@ -95,6 +95,60 @@ describe('FeatureFlagsService Browser', () => {
|
|||
expect.stringContaining('The feature flags provider took too long to initialize.')
|
||||
);
|
||||
});
|
||||
|
||||
describe('APM instrumentation', () => {
|
||||
const fakeProvider = { metadata: { name: 'fake provider' } } as Provider;
|
||||
|
||||
let setProvider: FeatureFlagsSetup['setProvider'];
|
||||
let apmSpy: jest.SpyInstance<Transaction | undefined>;
|
||||
let setProviderSpy: jest.SpyInstance<Promise<void>>;
|
||||
|
||||
beforeEach(() => {
|
||||
const setup = featureFlagsService.setup({ injectedMetadata });
|
||||
setProvider = setup.setProvider;
|
||||
setProviderSpy = jest.spyOn(OpenFeature, 'setProviderAndWait');
|
||||
apmSpy = jest.spyOn(apm, 'startTransaction');
|
||||
});
|
||||
|
||||
test('starts an APM transaction to track the time it takes to set a provider', () => {
|
||||
expect.assertions(1);
|
||||
setProvider(fakeProvider);
|
||||
expect(apmSpy).toHaveBeenCalledWith('set-provider', 'feature-flags');
|
||||
});
|
||||
|
||||
test('APM transaction tracks success', async () => {
|
||||
expect.assertions(4);
|
||||
|
||||
setProviderSpy.mockResolvedValueOnce();
|
||||
setProvider(fakeProvider);
|
||||
|
||||
const transaction = apmSpy.mock.results[0].value;
|
||||
const endTransactionSpy = jest.spyOn(transaction, 'end');
|
||||
expect(transaction.outcome).toBeUndefined();
|
||||
expect(endTransactionSpy).toHaveBeenCalledTimes(0);
|
||||
await setProviderSpy.mock.results[0].value.catch(() => {});
|
||||
expect(transaction.outcome).toBe('success');
|
||||
expect(endTransactionSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('APM transaction tracks failures', async () => {
|
||||
expect.assertions(5);
|
||||
|
||||
const apmCaptureErrorSpy = jest.spyOn(apm, 'captureError');
|
||||
const error = new Error('Something went terribly wrong');
|
||||
setProviderSpy.mockRejectedValueOnce(error);
|
||||
setProvider(fakeProvider);
|
||||
|
||||
const transaction = apmSpy.mock.results[0].value;
|
||||
const endTransactionSpy = jest.spyOn(transaction, 'end');
|
||||
expect(transaction.outcome).toBeUndefined();
|
||||
expect(endTransactionSpy).toHaveBeenCalledTimes(0);
|
||||
await setProviderSpy.mock.results[0].value.catch(() => {});
|
||||
expect(apmCaptureErrorSpy).toHaveBeenCalledWith(error);
|
||||
expect(transaction.outcome).toBe('failure');
|
||||
expect(endTransactionSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('context handling', () => {
|
||||
|
|
|
@ -71,11 +71,21 @@ export class FeatureFlagsService {
|
|||
const transaction = apm.startTransaction('set-provider', 'feature-flags');
|
||||
this.isProviderReadyPromise = OpenFeature.setProviderAndWait(provider);
|
||||
this.isProviderReadyPromise
|
||||
.then(() => transaction?.end())
|
||||
.then(() => {
|
||||
if (transaction) {
|
||||
// @ts-expect-error RUM types are not correct
|
||||
transaction.outcome = 'success';
|
||||
transaction.end();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error(err);
|
||||
apm.captureError(err);
|
||||
transaction?.end();
|
||||
if (transaction) {
|
||||
// @ts-expect-error RUM types are not correct
|
||||
transaction.outcome = 'failure';
|
||||
transaction.end();
|
||||
}
|
||||
});
|
||||
},
|
||||
appendContext: (contextToAppend) => this.appendContext(contextToAppend),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue