import 'mocha'; import { stub, assert, spy } from 'sinon'; import { expect } from 'chai'; import * as Utils from '../src/utils'; import { SpanKind, SpanStatusCode } from '@opentelemetry/api'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; describe('elasticsearch utils', () => { const spanMock = { recordException: (err) => {}, setStatus: (obj) => {}, end: () => {}, setAttributes: (obj) => {}, }; context('defaultDbStatementSerializer', () => { it('should serialize', () => { const result = Utils.defaultDbStatementSerializer('operationName', { index: 'test' }, {}); expect(result).to.equal('{"params":{"index":"test"},"options":{}}'); }); }); context('onError', () => { it('should record error', () => { const recordExceptionStub = stub(spanMock, 'recordException'); const setStatusStub = stub(spanMock, 'setStatus'); const endStub = stub(spanMock, 'end'); const error = new Error('test error'); // @ts-ignore Utils.onError(spanMock, error); assert.calledOnce(recordExceptionStub); assert.calledWith(recordExceptionStub, error); assert.calledOnce(setStatusStub); assert.calledWith(setStatusStub, { code: SpanStatusCode.ERROR, message: error.message }); assert.calledOnce(endStub); recordExceptionStub.restore(); setStatusStub.restore(); endStub.restore(); }); }); context('onResponse', () => { it('should record response without responseHook', () => { const setAttributesStub = stub(spanMock, 'setAttributes'); const setStatusStub = stub(spanMock, 'setStatus'); const endStub = stub(spanMock, 'end'); // @ts-ignore Utils.onResponse(spanMock, { meta: { connection: { url: 'http://localhost' } } }); assert.calledOnce(setAttributesStub); assert.calledOnce(setStatusStub); assert.calledOnce(endStub); assert.calledWith(setStatusStub, { code: SpanStatusCode.OK }); setAttributesStub.restore(); setStatusStub.restore(); endStub.restore(); }); it('should record response with responseHook', () => { const setAttributesStub = stub(spanMock, 'setAttributes'); const setStatusStub = stub(spanMock, 'setStatus'); const endStub = stub(spanMock, 'end'); const responseHook = spy(); // @ts-ignore Utils.onResponse(spanMock, { meta: { connection: { url: 'http://localhost' } } }, responseHook); assert.calledOnce(setAttributesStub); assert.calledOnce(setStatusStub); assert.calledOnce(endStub); assert.calledWith(setStatusStub, { code: SpanStatusCode.OK }); expect(responseHook.called).to.be.true; setAttributesStub.restore(); setStatusStub.restore(); endStub.restore(); }); }); context('getNetAttributes', () => { const url = 'http://localhost:9200'; const attributes = Utils.getNetAttributes(url); it('should get hostname from url', () => { expect(attributes[SemanticAttributes.NET_PEER_NAME]).to.equal('localhost'); }); it('should get hostname from url', () => { expect(attributes[SemanticAttributes.NET_PEER_PORT]).to.equal('9200'); }); it('should set net.transport', () => { expect(attributes[SemanticAttributes.NET_TRANSPORT]).to.equal('IP.TCP'); }); }); context('getPort', () => { it('should get port', () => { const result = Utils.getPort('3030', 'http:'); expect(result).to.equal('3030'); }); it('should get port from http protocol', () => { const result = Utils.getPort('', 'http:'); expect(result).to.equal('80'); }); it('should get port from https protocol', () => { const result = Utils.getPort('', 'https:'); expect(result).to.equal('443'); }); }); context('normalizeArguments', () => { it('should normalize with callback only', () => { const callbackFunction = () => {}; // @ts-ignore const [params, options, callback] = Utils.normalizeArguments(callbackFunction); expect(params).to.be.empty; expect(options).to.be.empty; expect(callback).to.be.equal(callbackFunction); }); it('should normalize with params only', () => { // @ts-ignore const [params, options, callback] = Utils.normalizeArguments({ index: 'test' }); expect(params).to.deep.equal({ index: 'test' }); expect(options).to.be.undefined; expect(callback).to.be.undefined; }); }); context('getIndexName', () => { it('should accept index string', () => { const index = Utils.getIndexName({ index: 'test' }); expect(index).to.equal('test'); }); it('should accept index array', () => { const indexes = Utils.getIndexName({ index: ['index1', 'index2'] }); expect(indexes).to.equal('index1,index2'); }); it('should accept no index', () => { const undefinedParams = Utils.getIndexName(undefined); const emptyObject = Utils.getIndexName({}); expect(undefinedParams).to.be.undefined; expect(emptyObject).to.be.undefined; }); it('should ignore unexpected index', () => { const functionIndex = Utils.getIndexName({ index: () => {} }); const objectIndex = Utils.getIndexName({ index: {} }); expect(functionIndex).to.be.undefined; expect(objectIndex).to.be.undefined; }); }); context('startSpan', () => { const tracerMock = { startSpan: (name, options?, context?): any => {}, startActiveSpan: () => {}, }; it('should start span with client kind', () => { const startSpanStub = stub(tracerMock, 'startSpan'); Utils.startSpan({ tracer: tracerMock, attributes: { testAttribute: 'testValue' }, }); assert.calledOnce(startSpanStub); const [operation, options] = startSpanStub.getCall(0).args; expect(operation).to.equal('elasticsearch.request'); expect(options.kind).to.equal(SpanKind.CLIENT); expect(options.attributes[SemanticAttributes.DB_SYSTEM]).to.equal('elasticsearch'); expect(options.attributes.testAttribute).to.equal('testValue'); }); }); });