Skip to content

Commit 0914d82

Browse files
committed
Add tests and cleanup
1 parent 809e1cd commit 0914d82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1552
-306
lines changed

packages/sdk/fastly/README.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,62 @@
1-
# fastly
1+
# LaunchDarkly SDK for Fastly
2+
3+
The LaunchDarkly SDK for Fastly is designed primarily for use in [Fastly Compute Platform](https://www.fastly.com/documentation/guides/compute/). It follows the server-side LaunchDarkly model for multi-user contexts. It is not intended for use in desktop and embedded systems applications.
4+
5+
# ⛔️⛔️⛔️⛔️
6+
7+
> [!CAUTION]
8+
> This library is an alpha version and should not be considered ready for production use while this message is visible.
9+
10+
# ☝️☝️☝️☝️☝️☝️
11+
12+
## Install
13+
14+
```shell
15+
# npm
16+
npm i @launchdarkly/fastly-server-sdk
17+
18+
# yarn
19+
yarn add @launchdarkly/fastly-server-sdk
20+
```
21+
22+
## Quickstart
23+
24+
See the full [example app](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/fastly/example).
25+
26+
## Developing this SDK
27+
28+
```shell
29+
# at js-core repo root
30+
yarn && yarn build && cd packages/sdk/fastly
31+
32+
# run tests
33+
yarn test
34+
```
35+
36+
## Verifying SDK build provenance with the SLSA framework
37+
38+
LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. To learn more, see the [provenance guide](PROVENANCE.md).
39+
40+
## About LaunchDarkly
41+
42+
- LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
43+
- Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
44+
- Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
45+
- Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
46+
- Grant access to certain features based on user attributes, like payment plan (eg: users on the 'gold' plan get access to more features than users in the 'silver' plan).
47+
- Disable parts of your application to facilitate maintenance, without taking everything offline.
48+
- LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Read [our documentation](https://docs.launchdarkly.com/sdk) for a complete list.
49+
- Explore LaunchDarkly
50+
- [launchdarkly.com](https://www.launchdarkly.com/ 'LaunchDarkly Main Website') for more information
51+
- [docs.launchdarkly.com](https://docs.launchdarkly.com/ 'LaunchDarkly Documentation') for our documentation and SDK reference guides
52+
- [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/ 'LaunchDarkly API Documentation') for our API documentation
53+
- [blog.launchdarkly.com](https://blog.launchdarkly.com/ 'LaunchDarkly Blog Documentation') for the latest product updates
54+
55+
[sdk-fastly-ci-badge]: https://github.com/launchdarkly/js-core/actions/workflows/fastly.yml/badge.svg
56+
[sdk-fastly-ci]: https://github.com/launchdarkly/js-core/actions/workflows/fastly.yml
57+
[sdk-fastly-npm-badge]: https://img.shields.io/npm/v/@launchdarkly/fastly-server-sdk.svg?style=flat-square
58+
[sdk-fastly-npm-link]: https://www.npmjs.com/package/@launchdarkly/fastly-server-sdk
59+
[sdk-fastly-ghp-badge]: https://img.shields.io/static/v1?label=GitHub+Pages&message=API+reference&color=00add8
60+
[sdk-fastly-ghp-link]: https://launchdarkly.github.io/js-core/packages/sdk/fastly/docs/
61+
[sdk-fastly-dm-badge]: https://img.shields.io/npm/dm/@launchdarkly/fastly-server-sdk.svg?style=flat-square
62+
[sdk-fastly-dt-badge]: https://img.shields.io/npm/dt/@launchdarkly/fastly-server-sdk.svg?style=flat-square
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const KVStore = jest.fn().mockImplementation(() => ({
2+
get: jest.fn(),
3+
put: jest.fn(),
4+
delete: jest.fn(),
5+
getMulti: jest.fn(),
6+
putMulti: jest.fn(),
7+
deleteMulti: jest.fn(),
8+
}));
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { AsyncStoreFacade, LDFeatureStore } from '@launchdarkly/js-server-sdk-common';
2+
3+
import { EdgeFeatureStore } from '../../src/api/EdgeFeatureStore';
4+
import mockEdgeProvider from '../../src/utils/mockEdgeProvider';
5+
import * as testData from './testData.json';
6+
7+
describe('EdgeFeatureStore', () => {
8+
const sdkKey = 'sdkKey';
9+
const kvKey = `LD-Env-${sdkKey}`;
10+
const mockLogger = {
11+
error: jest.fn(),
12+
warn: jest.fn(),
13+
info: jest.fn(),
14+
debug: jest.fn(),
15+
};
16+
const mockGet = mockEdgeProvider.get as jest.Mock;
17+
let featureStore: LDFeatureStore;
18+
let asyncFeatureStore: AsyncStoreFacade;
19+
20+
beforeEach(() => {
21+
mockGet.mockImplementation(() => Promise.resolve(JSON.stringify(testData)));
22+
featureStore = new EdgeFeatureStore(mockEdgeProvider, sdkKey, 'MockEdgeProvider', mockLogger);
23+
asyncFeatureStore = new AsyncStoreFacade(featureStore);
24+
});
25+
26+
afterEach(() => {
27+
jest.resetAllMocks();
28+
});
29+
30+
describe('get', () => {
31+
test('get flag', async () => {
32+
const flag = await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1');
33+
34+
expect(mockGet).toHaveBeenCalledWith(kvKey);
35+
expect(flag).toMatchObject(testData.flags.testFlag1);
36+
});
37+
38+
test('invalid flag key', async () => {
39+
const flag = await asyncFeatureStore.get({ namespace: 'features' }, 'invalid');
40+
41+
expect(flag).toBeUndefined();
42+
});
43+
44+
test('get segment', async () => {
45+
const segment = await asyncFeatureStore.get({ namespace: 'segments' }, 'testSegment1');
46+
47+
expect(mockGet).toHaveBeenCalledWith(kvKey);
48+
expect(segment).toMatchObject(testData.segments.testSegment1);
49+
});
50+
51+
test('invalid segment key', async () => {
52+
const segment = await asyncFeatureStore.get({ namespace: 'segments' }, 'invalid');
53+
54+
expect(segment).toBeUndefined();
55+
});
56+
57+
test('invalid kv key', async () => {
58+
mockGet.mockImplementation(() => Promise.resolve(null));
59+
const flag = await asyncFeatureStore.get({ namespace: 'features' }, 'testFlag1');
60+
61+
expect(flag).toBeNull();
62+
});
63+
});
64+
65+
describe('all', () => {
66+
test('all flags', async () => {
67+
const flags = await asyncFeatureStore.all({ namespace: 'features' });
68+
69+
expect(mockGet).toHaveBeenCalledWith(kvKey);
70+
expect(flags).toMatchObject(testData.flags);
71+
});
72+
73+
test('all segments', async () => {
74+
const segment = await asyncFeatureStore.all({ namespace: 'segments' });
75+
76+
expect(mockGet).toHaveBeenCalledWith(kvKey);
77+
expect(segment).toMatchObject(testData.segments);
78+
});
79+
80+
test('invalid DataKind', async () => {
81+
const flag = await asyncFeatureStore.all({ namespace: 'InvalidDataKind' });
82+
83+
expect(flag).toEqual({});
84+
});
85+
86+
test('invalid kv key', async () => {
87+
mockGet.mockImplementation(() => Promise.resolve(null));
88+
const segment = await asyncFeatureStore.all({ namespace: 'segments' });
89+
90+
expect(segment).toEqual({});
91+
});
92+
});
93+
94+
describe('initialized', () => {
95+
test('is initialized', async () => {
96+
const isInitialized = await asyncFeatureStore.initialized();
97+
98+
expect(mockGet).toHaveBeenCalledWith(kvKey);
99+
expect(isInitialized).toBeTruthy();
100+
});
101+
102+
test('not initialized', async () => {
103+
mockGet.mockImplementation(() => Promise.resolve(null));
104+
const isInitialized = await asyncFeatureStore.initialized();
105+
106+
expect(mockGet).toHaveBeenCalledWith(kvKey);
107+
expect(isInitialized).toBeFalsy();
108+
});
109+
});
110+
111+
describe('init & getDescription', () => {
112+
test('init', (done) => {
113+
const cb = jest.fn(() => {
114+
done();
115+
});
116+
featureStore.init(testData, cb);
117+
});
118+
119+
test('getDescription', async () => {
120+
const description = featureStore.getDescription?.();
121+
122+
expect(description).toEqual('MockEdgeProvider');
123+
});
124+
});
125+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { internal } from '@launchdarkly/js-server-sdk-common';
2+
3+
import LDClient from '../../src/api/LDClient';
4+
import { createBasicPlatform } from '../createBasicPlatform';
5+
6+
jest.mock('@launchdarkly/js-sdk-common', () => {
7+
const actual = jest.requireActual('@launchdarkly/js-sdk-common');
8+
return {
9+
...actual,
10+
...{
11+
internal: {
12+
...actual.internal,
13+
DiagnosticsManager: jest.fn(),
14+
EventProcessor: jest.fn(),
15+
},
16+
},
17+
};
18+
});
19+
20+
const mockEventProcessor = internal.EventProcessor as jest.Mock;
21+
describe('Edge LDClient', () => {
22+
it('uses clientSideID endpoints', async () => {
23+
const client = new LDClient('client-side-id', createBasicPlatform().info, {
24+
sendEvents: true,
25+
});
26+
await client.waitForInitialization({ timeout: 10 });
27+
const passedConfig = mockEventProcessor.mock.calls[0][0];
28+
29+
expect(passedConfig).toMatchObject({
30+
sendEvents: true,
31+
serviceEndpoints: {
32+
includeAuthorizationHeader: false,
33+
analyticsEventPath: '/events/bulk/client-side-id',
34+
diagnosticEventPath: '/events/diagnostic/client-side-id',
35+
events: 'https://events.launchdarkly.com',
36+
polling: 'https://sdk.launchdarkly.com',
37+
streaming: 'https://stream.launchdarkly.com',
38+
},
39+
});
40+
});
41+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { BasicLogger } from '@launchdarkly/js-server-sdk-common';
2+
3+
import createOptions, { defaultOptions } from '../../src/api/createOptions';
4+
5+
describe('createOptions', () => {
6+
test('default options', () => {
7+
expect(createOptions({})).toEqual(defaultOptions);
8+
});
9+
10+
test('override logger', () => {
11+
const logger = new BasicLogger({ name: 'test' });
12+
expect(createOptions({ logger })).toEqual({
13+
...defaultOptions,
14+
logger,
15+
});
16+
});
17+
});

0 commit comments

Comments
 (0)