Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a090170
Work on initial scaffold.
kinyoklion May 19, 2022
0b470ea
Update index exports.
kinyoklion May 19, 2022
93fbac6
Add an extra blank line.
kinyoklion May 19, 2022
8be08da
Attempt using mixin for event emitter.
kinyoklion May 23, 2022
5302683
Add implementation for emitting events for big segment status.
kinyoklion May 23, 2022
263fccd
Add documentation.
kinyoklion May 23, 2022
831b964
Fix lint and interface hierarchy.
kinyoklion May 23, 2022
3b1ece5
Start implementation.
kinyoklion May 23, 2022
cec9858
Progress on option handling.
kinyoklion May 23, 2022
954a135
Add application tags support.
kinyoklion May 23, 2022
1a0dffc
Un-generalize tags.
kinyoklion May 23, 2022
a141aeb
Fix tag formatting.
kinyoklion May 23, 2022
40d27e3
Comments.
kinyoklion May 23, 2022
f429571
Add internal to validated options.
kinyoklion May 23, 2022
5792122
Add tests for type checks. Finish implementing type checks.
kinyoklion May 24, 2022
b93ed50
Add remaining validator tests.
kinyoklion May 24, 2022
82599e2
Improve coverage collection. Start implementing application tags tests.
kinyoklion May 24, 2022
ef67115
Add test logger and finish tag tests.
kinyoklion May 24, 2022
cd8357a
Start testing the Configuration class.
kinyoklion May 24, 2022
7ea76b5
Fix validation. Start on individual option tests.
kinyoklion May 24, 2022
82788fd
Add remaining config tests.
kinyoklion May 24, 2022
9740584
Make sure to check resolves in the logger.
kinyoklion May 24, 2022
b573151
Implement check for service endpoints consistency. Improve testing me…
kinyoklion May 25, 2022
4283204
Merge branch 'main' into rlamb/sc-154352/options-parsing
kinyoklion May 25, 2022
9080329
Remove unintended file.
kinyoklion May 25, 2022
5e0d9b1
Update server-sdk-common/src/options/Configuration.ts
kinyoklion May 25, 2022
79dd5d6
Refactor expectMessages.
kinyoklion May 25, 2022
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
8 changes: 7 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module.exports = {
transform: {'^.+\\.ts?$': 'ts-jest'},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some updates to make coverage more useful.

testMatch: ["**/__tests__/**/*test.ts?(x)"],
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverageFrom: [
"platform-node/src/**/*.ts",
"sdk-common/src/**/*.ts",
"server-sdk-common/src/**/*.ts"
]
};
1 change: 1 addition & 0 deletions platform-node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"declaration": true,
"declarationMap": true, // enables importers to jump to source
"resolveJsonModule": true,
"stripInternal": true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to support @internal. So we can implement things and keep their details out of our exported types.

},
}
1 change: 1 addition & 0 deletions sdk-common/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"sourceMap": true,
"declaration": true,
"declarationMap": true, // enables importers to jump to source
"stripInternal": true
}
}
105 changes: 105 additions & 0 deletions server-sdk-common/__tests__/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { LDLogger } from '../src';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a logger implementation for tests. Helps to validate some common scenarios.


// TODO: Move this to sdk-common when implementing logging.
export enum LogLevel {
Debug,
Info,
Warn,
Error,
}

type ExpectedMessage = { level: LogLevel, matches: RegExp };

export default class TestLogger implements LDLogger {
private readonly messages: Record<LogLevel, string[]> = {
[LogLevel.Debug]: [],
[LogLevel.Info]: [],
[LogLevel.Warn]: [],
[LogLevel.Error]: [],
};

private callCount = 0;

private waiters: Array<() => void> = [];

timeout(timeoutMs: number): Promise<number> {
return new Promise<number>((resolve) => {
setTimeout(() => resolve(this.callCount), timeoutMs);
});
}

async waitForMessages(count: number, timeoutMs: number = 1000): Promise<number> {
return Promise.race([
new Promise<number>((resolve) => {
const waiter = () => {
if (this.callCount >= count) {
resolve(this.callCount);
}
};
waiter();
this.waiters.push(waiter);
}), this.timeout(timeoutMs)]);
}

/**
* Check received messages for expected messages.
*
* @param expectedMessages List of expected messages. If a message is expected
* more than once, then it should be included multiple times.
* @returns A list of messages that were not received.
*/
expectMessages(
expectedMessages: ExpectedMessage[],
): void {
const matched: Record<LogLevel, number[]> = {
[LogLevel.Debug]: [],
[LogLevel.Info]: [],
[LogLevel.Warn]: [],
[LogLevel.Error]: [],
};

expectedMessages.forEach((expectedMessage) => {
const received = this.messages[expectedMessage.level];
const index = received.findIndex(
(receivedMessage) => receivedMessage.match(expectedMessage.matches),
);
if (index < 0) {
throw new Error(`Did not find expected message: ${expectedMessage}`);
} else if (matched[expectedMessage.level].indexOf(index) >= 0) {
throw new Error(`Did not find expected message: ${expectedMessage}`);
} else {
matched[expectedMessage.level].push(index);
}
});
}

getCount() {
return this.callCount;
}

private checkResolves() {
this.waiters.forEach((waiter) => waiter());
}

private log(level: LogLevel, ...args: any[]) {
this.messages[level].push(args.join(' '));
this.callCount += 1;
this.checkResolves();
}

error(...args: any[]): void {
this.log(LogLevel.Error, args);
}

warn(...args: any[]): void {
this.log(LogLevel.Warn, args);
}

info(...args: any[]): void {
this.log(LogLevel.Info, args);
}

debug(...args: any[]): void {
this.log(LogLevel.Debug, args);
}
}
53 changes: 53 additions & 0 deletions server-sdk-common/__tests__/options/ApplicationTags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import ApplicationTags from '../../src/options/ApplicationTags';
import { ValidatedOptions } from '../../src/options/ValidatedOptions';
import TestLogger, { LogLevel } from '../Logger';

describe.each([
[
{ application: { id: 'is-valid', version: 'also-valid' }, logger: new TestLogger() },
'application-id/is-valid application-version/also-valid', [],
],
[{ application: { id: 'is-valid' }, logger: new TestLogger() }, 'application-id/is-valid', []],
[{ application: { version: 'also-valid' }, logger: new TestLogger() }, 'application-version/also-valid', []],
[{ application: {}, logger: new TestLogger() }, undefined, []],
[{ logger: new TestLogger() }, undefined, []],
[undefined, undefined, undefined],

// Above ones are 'valid' cases. Below are invalid.
[
{ application: { id: 'bad tag' }, logger: new TestLogger() },
undefined, [
{ level: LogLevel.Warn, matches: /Config option "application.id" must/ },
],
],
[
{ application: { id: 'bad tag', version: 'good-tag' }, logger: new TestLogger() },
'application-version/good-tag', [
{ level: LogLevel.Warn, matches: /Config option "application.id" must/ },
],
],
[
{ application: { id: 'bad tag', version: 'also bad' }, logger: new TestLogger() },
undefined, [
{ level: LogLevel.Warn, matches: /Config option "application.id" must/ },
{ level: LogLevel.Warn, matches: /Config option "application.version" must/ },
],
],
// Bad tags and no logger.
[
{ application: { id: 'bad tag', version: 'also bad' }, logger: undefined },
undefined, undefined,
],
])('given application tags configurations', (config, result, logs) => {
it('produces the correct tag values', () => {
const tags = new ApplicationTags(config as unknown as ValidatedOptions);
expect(tags.value).toEqual(result);
});

it('logs issues it encounters', () => {
expect(config?.logger?.getCount()).toEqual(logs?.length);
if (logs) {
config?.logger?.expectMessages(logs);
}
});
});
Loading