Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

refactor(lumberjack): replace with util fn #1213

Merged
merged 9 commits into from
Jan 11, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`attachRequestSpies requestSpy is called with object options 1`] = `
Url {
"auth": "user:password",
"hash": "#so-blue",
"host": "example.tld:8080",
"hostname": "example.tld",
"href": "https://user:password@example.tld:8080/somewhere?over=rainbow#so-blue",
"path": "/somewhere?over=rainbow",
"pathname": "/somewhere",
"port": "8080",
"protocol": "https:",
"query": "over=rainbow",
"search": "?over=rainbow",
"slashes": true,
}
`;

exports[`attachRequestSpies requestSpy is called with object options 2`] = `
Url {
"auth": "user:password",
"hash": "#so-blue",
"host": "example.tld:8080",
"hostname": "example.tld",
"href": "http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue",
"path": "/somewhere?over=rainbow",
"pathname": "/somewhere",
"port": "8080",
"protocol": "http:",
"query": "over=rainbow",
"search": "?over=rainbow",
"slashes": true,
}
`;

exports[`attachRequestSpies requestSpy is called with parsed options 1`] = `
Url {
"auth": "user:password",
"hash": "#so-blue",
"host": "example.tld:8080",
"hostname": "example.tld",
"href": "http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue",
"path": "/somewhere?over=rainbow",
"pathname": "/somewhere",
"port": "8080",
"protocol": "http:",
"query": "over=rainbow",
"search": "?over=rainbow",
"slashes": true,
}
`;

exports[`attachRequestSpies requestSpy is called with sparse object options 1`] = `
Url {
"auth": null,
"hash": null,
"host": "localhost",
"hostname": "localhost",
"href": "https://localhost/",
"path": "/",
"pathname": "/",
"port": null,
"protocol": "https:",
"query": null,
"search": null,
"slashes": true,
}
`;

exports[`attachRequestSpies requestSpy is called with sparse object options 2`] = `
Url {
"auth": null,
"hash": null,
"host": "localhost",
"hostname": "localhost",
"href": "http://localhost/",
"path": "/",
"pathname": "/",
"port": null,
"protocol": "http:",
"query": null,
"search": null,
"slashes": true,
}
`;

exports[`attachRequestSpies socketCloseSpy is called when the request socket closes 1`] = `
Url {
"auth": null,
"hash": null,
"host": "example.tld",
"hostname": "example.tld",
"href": "http://example.tld/",
"path": "/",
"pathname": "/",
"port": null,
"protocol": "http:",
"query": null,
"search": null,
"slashes": true,
}
`;

exports[`attachRequestSpies throws if requestSpy is not a function 1`] = `"requestSpy must be a function (was "undefined")"`;

exports[`attachRequestSpies throws if socketCloseSpy is provided but is not a function 1`] = `"socketCloseSpy must be function if provided (was "string")"`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`attachSpy throws if the method name is not a function on the object 1`] = `"method is not a function"`;

exports[`attachSpy throws if the spy is not a function 1`] = `"spy must be a function (was "string")"`;
148 changes: 148 additions & 0 deletions __tests__/server/utils/logging/attachRequestSpies.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright 2023 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express
* or implied. See the License for the specific language governing permissions and limitations
* under the License.
*/

import http from 'node:http';
import https from 'node:https';
import onFinished from 'on-finished';

import attachRequestSpies from '../../../../src/server/utils/logging/attachRequestSpies';
import attachSpy from '../../../../src/server/utils/logging/attachSpy';

jest.mock('http', () => ({ request: jest.fn() }));
jest.mock('https', () => ({ request: jest.fn() }));
jest.mock('on-finished');
jest.mock('../../../../src/server/utils/logging/attachSpy');

describe('attachRequestSpies', () => {
beforeEach(() => {
attachSpy.mockClear();
});

it('throws if requestSpy is not a function', () => {
expect(attachRequestSpies).toThrowErrorMatchingSnapshot();
});

it('does not throw if socketCloseSpy is not provided', () => {
expect(() => attachRequestSpies(() => {})).not.toThrow();
});

it('throws if socketCloseSpy is provided but is not a function', () => {
expect(() => attachRequestSpies(() => {}, 'apples')).toThrowErrorMatchingSnapshot();
});

it('attaches http and https spies', () => {
attachRequestSpies(jest.fn());
expect(attachSpy).toHaveBeenCalledTimes(2);
expect(attachSpy.mock.calls[0][0]).toEqual(https);
expect(attachSpy.mock.calls[0][1]).toBe('request');
expect(attachSpy.mock.calls[1][0]).toEqual(http);
expect(attachSpy.mock.calls[1][1]).toBe('request');
expect(typeof attachSpy.mock.calls[0][2]).toBe('function');
expect(typeof attachSpy.mock.calls[1][2]).toBe('function');
});

describe('requestSpy', () => {
it('is called with clientRequest', () => {
const requestSpy = jest.fn();
attachRequestSpies(requestSpy);
expect(attachSpy).toHaveBeenCalledTimes(2);

const httpsSpy = attachSpy.mock.calls[0][2];
const httpSpy = attachSpy.mock.calls[1][2];

const callOriginal = jest.fn(() => 'client request object');

httpsSpy(['http://example.tld'], callOriginal);
Copy link
Member

Choose a reason for hiding this comment

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

Can we call from http directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we could, these tests rely on attachSpy being mocked so they will all need a rewrite.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

httpSpy(['http://example.tld'], callOriginal);

expect(requestSpy).toHaveBeenCalledTimes(2);
expect(requestSpy.mock.calls[0][0]).toBe('client request object');
expect(requestSpy.mock.calls[1][0]).toBe('client request object');
});

it('is called with object options', () => {
const requestSpy = jest.fn();
attachRequestSpies(requestSpy);
expect(attachSpy).toHaveBeenCalledTimes(2);
const httpsSpy = attachSpy.mock.calls[0][2];
const httpSpy = attachSpy.mock.calls[1][2];
const callOriginal = jest.fn(() => 'client request object');
httpsSpy([{
protocol: 'https',
hostname: 'example.tld',
port: 8080,
method: 'GET',
path: '/somewhere?over=rainbow#so-blue',
auth: 'user:password',
}], callOriginal);

httpSpy([{
protocol: 'http',
hostname: 'example.tld',
port: 8080,
method: 'GET',
path: '/somewhere?over=rainbow#so-blue',
auth: 'user:password',
}], callOriginal);

expect(requestSpy).toHaveBeenCalledTimes(2);
expect(requestSpy.mock.calls[0][1]).toMatchSnapshot();
expect(requestSpy.mock.calls[1][1]).toMatchSnapshot();
});

it('is called with sparse object options', () => {
const requestSpy = jest.fn();
attachRequestSpies(requestSpy);
expect(attachSpy).toHaveBeenCalledTimes(2);
const httpsSpy = attachSpy.mock.calls[0][2];
const httpSpy = attachSpy.mock.calls[1][2];

const callOriginal = jest.fn(() => 'client request object');
httpsSpy([{ method: 'GET' }], callOriginal);
httpSpy([{ method: 'GET' }], callOriginal);
expect(requestSpy).toHaveBeenCalledTimes(2);
expect(requestSpy.mock.calls[0][1]).toMatchSnapshot();
expect(requestSpy.mock.calls[1][1]).toMatchSnapshot();
});

it('is called with parsed options', () => {
const requestSpy = jest.fn();
attachRequestSpies(requestSpy);
expect(attachSpy).toHaveBeenCalledTimes(2);
const spy = attachSpy.mock.calls[0][2];
const callOriginal = jest.fn(() => 'client request object');
spy(['http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue'], callOriginal);
expect(requestSpy).toHaveBeenCalledTimes(1);
expect(requestSpy.mock.calls[0][1]).toMatchSnapshot();
});
});

describe('socketCloseSpy', () => {
it('is called when the request socket closes', () => {
onFinished.mockClear();
const socketCloseSpy = jest.fn();
attachRequestSpies(jest.fn(), socketCloseSpy);
expect(attachSpy).toHaveBeenCalledTimes(2);
const spy = attachSpy.mock.calls[0][2];
const clientRequest = {};
const callOriginal = () => clientRequest;
spy(['http://example.tld'], callOriginal);
expect(onFinished).toHaveBeenCalledTimes(1);
onFinished.mock.calls[0][1]();
expect(socketCloseSpy).toHaveBeenCalledTimes(1);
expect(socketCloseSpy.mock.calls[0][0]).toBe(clientRequest);
expect(socketCloseSpy.mock.calls[0][1]).toMatchSnapshot();
});
});
});
84 changes: 84 additions & 0 deletions __tests__/server/utils/logging/attachSpy.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2023 American Express Travel Related Services Company, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express
* or implied. See the License for the specific language governing permissions and limitations
* under the License.
*/

import attachSpy from '../../../../src/server/utils/logging/attachSpy';

describe('attachSpy', () => {
it('throws if the method name is not a function on the object', () => {
expect(() => attachSpy({}, 'method', () => {})).toThrowErrorMatchingSnapshot();
});
it('throws if the spy is not a function', () => {
expect(() => attachSpy({ method: () => {} }, 'method', 'hello')).toThrowErrorMatchingSnapshot();
});
describe('monkeypatched method', () => {
const originalMethod = jest.fn(() => 'some return value');
const obj = {};

beforeEach(() => {
obj.method = originalMethod;
originalMethod.mockClear();
});

it('invokes the spy', () => {
const spy = jest.fn();
attachSpy(obj, 'method', spy);
expect(spy).not.toHaveBeenCalled();
obj.method();
expect(spy).toHaveBeenCalledTimes(1);
});

it('invokes the spy with args', () => {
const spy = jest.fn();
attachSpy(obj, 'method', spy);
obj.method('g', { h: 'i' }, 9);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0][0]).toEqual(['g', { h: 'i' }, 9]);
});

it('invokes the spy with callOriginal', () => {
const spy = jest.fn();
attachSpy(obj, 'method', spy);
obj.method();
expect(spy).toHaveBeenCalledTimes(1);
expect(typeof spy.mock.calls[0][1]).toEqual('function');
});

it('returns the original methods return value from callOriginal', () => {
expect.assertions(1);
attachSpy(obj, 'method', (args, callOriginal) => {
expect(callOriginal()).toEqual('some return value');
});
obj.method();
});

it('calls the original method when the spy does not', () => {
attachSpy(obj, 'method', () => {});
obj.method();
expect(originalMethod).toHaveBeenCalledTimes(1);
});

it('does not call the original method when the spy already had', () => {
attachSpy(obj, 'method', (args, callOriginal) => {
callOriginal();
});
obj.method();
expect(originalMethod).toHaveBeenCalledTimes(1);
});

it('returns the original methods return value to the caller of the monkeypatched method', () => {
attachSpy(obj, 'method', jest.fn());
expect(obj.method()).toEqual('some return value');
});
});
});
Loading
Loading