Skip to content

Commit

Permalink
Issue #63: completed first version of context sourcing test set.
Browse files Browse the repository at this point in the history
  • Loading branch information
fgm committed Sep 5, 2018
1 parent f86cf0e commit 433914e
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 41 deletions.
164 changes: 130 additions & 34 deletions __tests__/unit/contextSourcingTest.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,206 @@
import ClientLogger from "../../src/ClientLogger";
import {
DETAILS_KEY, HOST_KEY,
IContext, SOURCE_KEY, TS_KEY,
IContext, ITimestamps, ITimestampsHash, SOURCE_KEY, TS_KEY,
} from "../../src/IContext";
import {ILogger} from "../../src/ILogger";
import { Levels } from "../../src/LogLevel";
import ServerLogger from "../../src/ServerLogger";

// Unit tests run on NodeJS, so we have access to its packages.
import { hostname } from "os";
import {ISender} from "../../src/Senders/ISender";
import {IStrategy} from "../../src/Strategies/IStrategy";

let result;
type StrategyFactory = (sender?: ISender) => IStrategy;

const emptyStrategy = () => ({
const newEmptyStrategy: StrategyFactory = () => ({
customizeLogger: () => [],
selectSenders: () => [],
});

const TestSender = new class {
class TestSender {
public result: IContext = {};

public send(level, message, context): void {
result = { level, message, context };
this.result = { level, message, context };
}
}();
}

const logStrategy = {
...emptyStrategy(),
selectSenders: () => [TestSender],
};
const newLogStrategy: StrategyFactory = (sender: TestSender) => ({
...newEmptyStrategy(),
selectSenders: () => [sender],
});

function testContextSourcing(): void {
function newClientLogger(): ILogger {
const cl: ILogger = new ClientLogger(logStrategy);
function newClientLogger(sender: ISender): ILogger {
const cl: ILogger = new ClientLogger(newLogStrategy(sender));
return cl;
}

function newServerLogger(): ILogger {
const sl: ILogger = new ServerLogger(logStrategy);
function newServerLogger(sender: ISender): ServerLogger {
const sl: ServerLogger = new ServerLogger(newLogStrategy(sender));
return sl;
}

test.only("Client logging, pure", () => {
const cl = newClientLogger();
test("Client logging, pure, no processor", () => {
const sender: TestSender = new TestSender();
const cl = newClientLogger(sender);
// Constant strings are immutable.
const message = "some message";
const side = "client";

// Const objects are not immutable: catch details being overwritten.
const details = { a: "A" };
const expectedDetails = { ...details };

const t1 = +new Date();
const t1: number = +new Date();
cl.log(Levels.WARNING, message, details);
const t2 = +new Date();
const t2: number = +new Date();

const expected = {
context: {
[DETAILS_KEY]: details,
// HOST_KEY: not on client logger.
[SOURCE_KEY]: "client",
// HOST_KEY: not on client contexts.
[SOURCE_KEY]: side,
// TS_KEY: Cannot test content just with toMatchObject.
},
level: Levels.WARNING,
message,
};
expect(t2).toBeGreaterThanOrEqual(t1);
const result = sender.result;
expect(typeof result).toBe("object");
expect(result).not.toHaveProperty(HOST_KEY);
expect(result).toMatchObject(expected);
expect(result).toHaveProperty(TS_KEY);
expect(typeof result[TS_KEY]).toBe("object");
expect(result[TS_KEY]).toHaveProperty("log");
expect(result[TS_KEY].log).toBeGreaterThanOrEqual(t1);
expect(result[TS_KEY].log).toBeLessThanOrEqual(t2);

const actualContext = result.context;
// No side properties without processors.
expect(actualContext).not.toHaveProperty(side);

expect(actualContext).toHaveProperty(TS_KEY);
const ts: ITimestampsHash = actualContext[TS_KEY];
expect(typeof ts).toBe("object");
expect(ts).toHaveProperty(side);
expect(ts[side]).toHaveProperty("log");
expect(ts[side].log).toBeGreaterThanOrEqual(t1);
expect(ts[side].log).toBeLessThanOrEqual(t2);
});

test.only("Server logging, pure", () => {
const sl = newServerLogger();
test("Server logging, pure, no processor", () => {
const sender: TestSender = new TestSender();
const sl = newServerLogger(sender);

// Constant strings are immutable.
const message = "some message";
const host = hostname();
const side = "server";

// Const objects are not immutable: catch details being overwritten.
const details = { a: "A" };
const expectedDetails = { ...details };

const t1 = +new Date();
const t1: number = +new Date();
sl.log(Levels.WARNING, message, details);
const t2 = +new Date();
const t2: number = +new Date();

const expected = {
context: {
[DETAILS_KEY]: expectedDetails,
[HOST_KEY]: host,
[SOURCE_KEY]: side,
// TS_KEY: Cannot test content just with toMatchObject.
},
level: Levels.WARNING,
message,
};
expect(t2).toBeGreaterThanOrEqual(t1);
const result = sender.result;
expect(typeof result).toBe("object");
expect(result).toMatchObject(expected);

const actualContext = result.context;
expect(actualContext).toHaveProperty(TS_KEY);
const ts: ITimestampsHash = actualContext[TS_KEY];
expect(typeof ts).toBe("object");
expect(ts).toHaveProperty(side);
expect(ts[side]).toHaveProperty("log");
expect(ts[side].log).toBeGreaterThanOrEqual(t1);
expect(ts[side].log).toBeLessThanOrEqual(t2);
});

test("Server logging from client log, no processor", () => {
const sender: TestSender = new TestSender();
const sl = newServerLogger(sender);

// Constant strings are immutable.
const message = "some message";
const host = hostname();

// Const objects are not immutable: catch details being overwritten.
const details = { a: "A" };
const expectedDetails = { ...details };

const clientContext: IContext = {
processorObject: { foo: "bar" },
processorScalar: "baz",
};
const expectedClientContext = { ...clientContext };

const side: string = "client";

// Go back in time to ensure sequencing t1 < client log < client send < t2.
const t1: number = +new Date() - 3;

const initialContext = {
[DETAILS_KEY]: expectedDetails,
// HOST_KEY: not on client contexts.
[TS_KEY]: {
[side]: {
log: t1 + 1,
send: t1 + 2,
} as ITimestamps,
// No "server" yet.
} as ITimestampsHash,
[SOURCE_KEY]: side,
[side]: clientContext,
};

sl.logExtended(Levels.WARNING, message, initialContext, "client");
const t2: number = +new Date();

const expected = {
context: {
[DETAILS_KEY]: expectedDetails,
[HOST_KEY]: host,
[SOURCE_KEY]: "server",
[SOURCE_KEY]: side,
// TS_KEY: Cannot test content just with toMatchObject.
[side]: expectedClientContext,
// No "server" content without a server processor.
},
level: Levels.WARNING,
message,
};

expect(t2).toBeGreaterThanOrEqual(t1);
const result = sender.result;

expect(typeof result).toBe("object");
expect(result).toMatchObject(expected);
expect(result).toHaveProperty(TS_KEY);

const actualContext = result.context;
// No side property without a processor.
expect(actualContext).not.toHaveProperty("server");

expect(actualContext).toHaveProperty(TS_KEY);
const ts: ITimestampsHash = actualContext[TS_KEY];
expect(typeof result[TS_KEY]).toBe("object");
expect(result[TS_KEY]).toHaveProperty("log");
expect(result[TS_KEY].log).toBeGreaterThanOrEqual(t1);
expect(result[TS_KEY].log).toBeLessThanOrEqual(t2);
expect(typeof ts).toBe("object");
expect(ts).toHaveProperty(side);
expect(ts).toHaveProperty("server");
expect(ts.server).toHaveProperty("log");
expect(ts.server.log).toBeGreaterThanOrEqual(t1);
expect(ts.server.log).toBeLessThanOrEqual(t2);
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/IContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface ITimestamps {
}

interface ITimestampsHash {
[key: string]: ITimestamps;
[side: string]: ITimestamps;
}

interface IContext {
Expand All @@ -31,5 +31,6 @@ export {
TS_KEY,
IDetails,
IContext,
ITimestamps,
ITimestampsHash,
};
9 changes: 3 additions & 6 deletions src/ServerLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,19 +265,16 @@ class ServerLogger extends Logger implements ILogger {
* The event level.
* @param message
* The event message.
* @param details
* The details submitted with the message: any additional data added to
* the message by the upstream (client/cordova) log() caller().
* @param context
* The context added to the details by upstream processors.
* @param source
* The upstream sender type.
*
* @throws InvalidArgumentException
*/
public logExtended(level: LogLevel.Levels, message: string, details: {}, context: IContext, source: string): void {
public logExtended(level: LogLevel.Levels, message: string, context: IContext, source: string): void {
this.validateLevel(level);
this.send(this.strategy, level, message, context3);
this.send(this.strategy, level, message, context);
}

/**
Expand All @@ -293,7 +290,7 @@ class ServerLogger extends Logger implements ILogger {
* @returns {void}
*/
public logMethod({ level = LogLevel.INFO, message = "", context = {} }) {
this.logExtended(level, message, {}, context, ClientLogger.side);
this.logExtended(level, message, context, ClientLogger.side);
}

/**
Expand Down

0 comments on commit 433914e

Please sign in to comment.