opentelemetry-js/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts

314 lines
10 KiB
TypeScript

/*!
* Copyright 2019, OpenTelemetry Authors
*
* 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
*
* https://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 * as assert from 'assert';
import * as sinon from 'sinon';
import * as url from 'url';
import { CanonicalCode, SpanKind } from '@opentelemetry/types';
import { NoopScopeManager } from '@opentelemetry/scope-base';
import { IgnoreMatcher } from '../../src/types';
import * as utils from '../../src/utils';
import * as http from 'http';
import { Span, BasicTracerRegistry } from '@opentelemetry/tracing';
import { AttributeNames } from '../../src';
import { NoopLogger } from '@opentelemetry/core';
describe('Utility', () => {
describe('parseResponseStatus()', () => {
it('should return UNKNOWN code by default', () => {
const status = utils.parseResponseStatus(
(undefined as unknown) as number
);
assert.deepStrictEqual(status, { code: CanonicalCode.UNKNOWN });
});
it('should return OK for Success HTTP status code', () => {
for (let index = 200; index < 400; index++) {
const status = utils.parseResponseStatus(index);
assert.deepStrictEqual(status, { code: CanonicalCode.OK });
}
});
it('should not return OK for Bad HTTP status code', () => {
for (let index = 400; index <= 504; index++) {
const status = utils.parseResponseStatus(index);
assert.notStrictEqual(status.code, CanonicalCode.OK);
}
});
});
describe('hasExpectHeader()', () => {
it('should throw if no option', () => {
try {
utils.hasExpectHeader('' as http.RequestOptions);
assert.fail();
} catch (ignore) {}
});
it('should not throw if no headers', () => {
const result = utils.hasExpectHeader({} as http.RequestOptions);
assert.strictEqual(result, false);
});
it('should return true on Expect (no case sensitive)', () => {
for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) {
const result = utils.hasExpectHeader({
headers,
} as http.RequestOptions);
assert.strictEqual(result, true);
}
});
});
describe('getRequestInfo()', () => {
it('should get options object', () => {
const webUrl = 'http://u:p@google.fr/aPath?qu=ry';
const urlParsed = url.parse(webUrl);
const urlParsedWithoutPathname = {
...urlParsed,
pathname: undefined,
};
const whatWgUrl = new url.URL(webUrl);
for (const param of [
webUrl,
urlParsed,
urlParsedWithoutPathname,
whatWgUrl,
]) {
const result = utils.getRequestInfo(param);
assert.strictEqual(result.optionsParsed.hostname, 'google.fr');
assert.strictEqual(result.optionsParsed.protocol, 'http:');
assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry');
assert.strictEqual(result.pathname, '/aPath');
assert.strictEqual(result.origin, 'http://google.fr');
}
});
});
describe('satisfiesPattern()', () => {
it('string pattern', () => {
const answer1 = utils.satisfiesPattern('/test/1', '/test/1');
assert.strictEqual(answer1, true);
const answer2 = utils.satisfiesPattern('/test/1', '/test/11');
assert.strictEqual(answer2, false);
});
it('regex pattern', () => {
const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i);
assert.strictEqual(answer1, true);
const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/);
assert.strictEqual(answer2, false);
});
it('should throw if type is unknown', () => {
try {
utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher);
assert.fail();
} catch (error) {
assert.strictEqual(error instanceof TypeError, true);
}
});
it('function pattern', () => {
const answer1 = utils.satisfiesPattern(
'/test/home',
(url: string) => url === '/test/home'
);
assert.strictEqual(answer1, true);
const answer2 = utils.satisfiesPattern(
'/test/home',
(url: string) => url !== '/test/home'
);
assert.strictEqual(answer2, false);
});
});
describe('isIgnored()', () => {
let satisfiesPatternStub: sinon.SinonSpy<[string, IgnoreMatcher], boolean>;
beforeEach(() => {
satisfiesPatternStub = sinon.spy(utils, 'satisfiesPattern');
});
afterEach(() => {
satisfiesPatternStub.restore();
});
it('should call isSatisfyPattern, n match', () => {
const answer1 = utils.isIgnored('/test/1', ['/test/11']);
assert.strictEqual(answer1, false);
assert.strictEqual(
(utils.satisfiesPattern as sinon.SinonSpy).callCount,
1
);
});
it('should call isSatisfyPattern, match for function', () => {
satisfiesPatternStub.restore();
const answer1 = utils.isIgnored('/test/1', [
url => url.endsWith('/test/1'),
]);
assert.strictEqual(answer1, true);
});
it('should not re-throw when function throws an exception', () => {
satisfiesPatternStub.restore();
const log = new NoopLogger();
const onException = (e: Error) => {
log.error('error', e);
};
for (const callback of [undefined, onException]) {
assert.doesNotThrow(() =>
utils.isIgnored(
'/test/1',
[
url => {
throw new Error('test');
},
],
callback
)
);
}
});
it('should call onException when function throws an exception', () => {
satisfiesPatternStub.restore();
const onException = sinon.spy();
assert.doesNotThrow(() =>
utils.isIgnored(
'/test/1',
[
url => {
throw new Error('test');
},
],
onException
)
);
assert.strictEqual((onException as sinon.SinonSpy).callCount, 1);
});
it('should not call isSatisfyPattern', () => {
utils.isIgnored('/test/1', []);
assert.strictEqual(
(utils.satisfiesPattern as sinon.SinonSpy).callCount,
0
);
});
it('should return false on empty list', () => {
const answer1 = utils.isIgnored('/test/1', []);
assert.strictEqual(answer1, false);
});
it('should not throw and return false when list is undefined', () => {
const answer2 = utils.isIgnored('/test/1', undefined);
assert.strictEqual(answer2, false);
});
});
describe('getAbsoluteUrl()', () => {
it('should return absolute url with localhost', () => {
const path = '/test/1';
const result = utils.getAbsoluteUrl(url.parse(path), {});
assert.strictEqual(result, `http://localhost${path}`);
});
it('should return absolute url', () => {
const absUrl = 'http://www.google/test/1?query=1';
const result = utils.getAbsoluteUrl(url.parse(absUrl), {});
assert.strictEqual(result, absUrl);
});
it('should return default url', () => {
const result = utils.getAbsoluteUrl(null, {});
assert.strictEqual(result, 'http://localhost/');
});
it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => {
const result = utils.getAbsoluteUrl(
{ path: '/helloworld', port: 8080 },
{}
);
assert.strictEqual(result, 'http://localhost:8080/helloworld');
});
});
describe('setSpanWithError()', () => {
it('should have error attributes', () => {
const errorMessage = 'test error';
for (const obj of [undefined, { statusCode: 400 }]) {
const span = new Span(
new BasicTracerRegistry({
scopeManager: new NoopScopeManager(),
}).getTracer('default'),
'test',
{ spanId: '', traceId: '' },
SpanKind.INTERNAL
);
/* tslint:disable-next-line:no-any */
utils.setSpanWithError(span, new Error(errorMessage), obj as any);
const attributes = span.toReadableSpan().attributes;
assert.strictEqual(
attributes[AttributeNames.HTTP_ERROR_MESSAGE],
errorMessage
);
assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]);
}
});
});
describe('isOpenTelemetryRequest()', () => {
[
{},
{ headers: {} },
url.parse('http://url.com'),
{ headers: { [utils.OT_REQUEST_HEADER]: 0 } },
{ headers: { [utils.OT_REQUEST_HEADER]: false } },
].forEach(options => {
it(`should return false with the following value: ${JSON.stringify(
options
)}`, () => {
/* tslint:disable-next-line:no-any */
assert.strictEqual(utils.isOpenTelemetryRequest(options as any), false);
});
});
for (const options of [
{ headers: { [utils.OT_REQUEST_HEADER]: 1 } },
{ headers: { [utils.OT_REQUEST_HEADER]: true } },
]) {
it(`should return true with the following value: ${JSON.stringify(
options
)}`, () => {
/* tslint:disable-next-line:no-any */
assert.strictEqual(utils.isOpenTelemetryRequest(options as any), true);
});
}
});
describe('isValidOptionsType()', () => {
['', false, true, 1, 0, []].forEach(options => {
it(`should return false with the following value: ${JSON.stringify(
options
)}`, () => {
assert.strictEqual(utils.isValidOptionsType(options), false);
});
});
for (const options of ['url', url.parse('http://url.com'), {}]) {
it(`should return true with the following value: ${JSON.stringify(
options
)}`, () => {
assert.strictEqual(utils.isValidOptionsType(options), true);
});
}
});
});