257 lines
8.3 KiB
TypeScript
257 lines
8.3 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 { NoopLogger } from '@opentelemetry/core';
|
|
import { NodeTracerRegistry } from '@opentelemetry/node';
|
|
import {
|
|
InMemorySpanExporter,
|
|
SimpleSpanProcessor,
|
|
} from '@opentelemetry/tracing';
|
|
import {
|
|
SpanKind,
|
|
Attributes,
|
|
TimedEvent,
|
|
Span,
|
|
CanonicalCode,
|
|
Status,
|
|
} from '@opentelemetry/types';
|
|
import { plugin as pgPlugin, PostgresPlugin } from '@opentelemetry/plugin-pg';
|
|
import { plugin, PostgresPoolPlugin } from '../src';
|
|
import { AttributeNames } from '../src/enums';
|
|
import * as assert from 'assert';
|
|
import * as pg from 'pg';
|
|
import * as pgPool from 'pg-pool';
|
|
import * as testUtils from '@opentelemetry/test-utils';
|
|
|
|
const memoryExporter = new InMemorySpanExporter();
|
|
|
|
const CONFIG = {
|
|
user: process.env.POSTGRES_USER || 'postgres',
|
|
database: process.env.POSTGRES_DB || 'postgres',
|
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
port: process.env.POSTGRES_PORT
|
|
? parseInt(process.env.POSTGRES_PORT, 10)
|
|
: 54320,
|
|
maxClient: 1,
|
|
idleTimeoutMillis: 10000,
|
|
};
|
|
|
|
const DEFAULT_PGPOOL_ATTRIBUTES = {
|
|
[AttributeNames.COMPONENT]: PostgresPoolPlugin.COMPONENT,
|
|
[AttributeNames.DB_INSTANCE]: CONFIG.database,
|
|
[AttributeNames.DB_TYPE]: PostgresPoolPlugin.DB_TYPE,
|
|
[AttributeNames.PEER_HOSTNAME]: CONFIG.host,
|
|
[AttributeNames.PEER_ADDRESS]: `jdbc:postgresql://${CONFIG.host}:${CONFIG.port}/${CONFIG.database}`,
|
|
[AttributeNames.PEER_PORT]: CONFIG.port,
|
|
[AttributeNames.DB_USER]: CONFIG.user,
|
|
[AttributeNames.MAX_CLIENT]: CONFIG.maxClient,
|
|
[AttributeNames.IDLE_TIMEOUT_MILLIS]: CONFIG.idleTimeoutMillis,
|
|
};
|
|
|
|
const DEFAULT_PG_ATTRIBUTES = {
|
|
[AttributeNames.COMPONENT]: PostgresPlugin.COMPONENT,
|
|
[AttributeNames.DB_INSTANCE]: CONFIG.database,
|
|
[AttributeNames.DB_TYPE]: PostgresPlugin.DB_TYPE,
|
|
[AttributeNames.PEER_HOSTNAME]: CONFIG.host,
|
|
[AttributeNames.PEER_ADDRESS]: `jdbc:postgresql://${CONFIG.host}:${CONFIG.port}/${CONFIG.database}`,
|
|
[AttributeNames.PEER_PORT]: CONFIG.port,
|
|
[AttributeNames.DB_USER]: CONFIG.user,
|
|
};
|
|
|
|
const okStatus: Status = {
|
|
code: CanonicalCode.OK,
|
|
};
|
|
|
|
const runCallbackTest = (
|
|
parentSpan: Span,
|
|
attributes: Attributes,
|
|
events: TimedEvent[],
|
|
status: Status = okStatus,
|
|
spansLength = 1,
|
|
spansIndex = 0
|
|
) => {
|
|
const spans = memoryExporter.getFinishedSpans();
|
|
assert.strictEqual(spans.length, spansLength);
|
|
const pgSpan = spans[spansIndex];
|
|
testUtils.assertSpan(pgSpan, SpanKind.CLIENT, attributes, events, status);
|
|
testUtils.assertPropagation(pgSpan, parentSpan);
|
|
};
|
|
|
|
describe('pg-pool@2.x', () => {
|
|
let pool: pgPool<pg.Client>;
|
|
const registry = new NodeTracerRegistry();
|
|
const logger = new NoopLogger();
|
|
const testPostgres = process.env.TEST_POSTGRES; // For CI: assumes local postgres db is already available
|
|
const testPostgresLocally = process.env.TEST_POSTGRES_LOCAL; // For local: spins up local postgres db via docker
|
|
const shouldTest = testPostgres || testPostgresLocally; // Skips these tests if false (default)
|
|
|
|
before(function(done) {
|
|
if (!shouldTest) {
|
|
// this.skip() workaround
|
|
// https://github.com/mochajs/mocha/issues/2683#issuecomment-375629901
|
|
this.test!.parent!.pending = true;
|
|
this.skip();
|
|
}
|
|
pool = new pgPool(CONFIG);
|
|
registry.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
|
|
if (testPostgresLocally) {
|
|
testUtils.startDocker('postgres');
|
|
}
|
|
done();
|
|
});
|
|
|
|
after(function(done) {
|
|
if (testPostgresLocally) {
|
|
testUtils.cleanUpDocker('postgres');
|
|
}
|
|
pool.end(() => {
|
|
done();
|
|
});
|
|
});
|
|
|
|
beforeEach(function() {
|
|
plugin.enable(pgPool, registry, logger);
|
|
pgPlugin.enable(pg, registry, logger);
|
|
});
|
|
|
|
afterEach(() => {
|
|
memoryExporter.reset();
|
|
plugin.disable();
|
|
pgPlugin.disable();
|
|
});
|
|
|
|
it('should return a plugin', () => {
|
|
assert.ok(plugin instanceof PostgresPoolPlugin);
|
|
});
|
|
|
|
it('should have correct moduleName', () => {
|
|
assert.strictEqual(plugin.moduleName, 'pg-pool');
|
|
});
|
|
|
|
describe('#pool.connect()', () => {
|
|
// promise - checkout a client
|
|
it('should intercept pool.connect()', async () => {
|
|
const pgPoolattributes = {
|
|
...DEFAULT_PGPOOL_ATTRIBUTES,
|
|
};
|
|
const pgAttributes = {
|
|
...DEFAULT_PG_ATTRIBUTES,
|
|
[AttributeNames.DB_STATEMENT]: 'SELECT NOW()',
|
|
};
|
|
const events: TimedEvent[] = [];
|
|
const span = registry.getTracer('test-pg-pool').startSpan('test span');
|
|
await registry.getTracer('test-pg-pool').withSpan(span, async () => {
|
|
const client = await pool.connect();
|
|
runCallbackTest(span, pgPoolattributes, events, okStatus, 1, 0);
|
|
assert.ok(client, 'pool.connect() returns a promise');
|
|
try {
|
|
await client.query('SELECT NOW()');
|
|
runCallbackTest(span, pgAttributes, events, okStatus, 2, 1);
|
|
} catch (e) {
|
|
throw e;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
});
|
|
});
|
|
|
|
// callback - checkout a client
|
|
it('should not return a promise if callback is provided', done => {
|
|
const pgPoolattributes = {
|
|
...DEFAULT_PGPOOL_ATTRIBUTES,
|
|
};
|
|
const pgAttributes = {
|
|
...DEFAULT_PG_ATTRIBUTES,
|
|
[AttributeNames.DB_STATEMENT]: 'SELECT NOW()',
|
|
};
|
|
const events: TimedEvent[] = [];
|
|
const parentSpan = registry
|
|
.getTracer('test-pg-pool')
|
|
.startSpan('test span');
|
|
registry.getTracer('test-pg-pool').withSpan(parentSpan, () => {
|
|
const resNoPromise = pool.connect((err, client, release) => {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
release();
|
|
assert.ok(client);
|
|
runCallbackTest(parentSpan, pgPoolattributes, events, okStatus, 1, 0);
|
|
client.query('SELECT NOW()', (err, ret) => {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
assert.ok(ret);
|
|
runCallbackTest(parentSpan, pgAttributes, events, okStatus, 2, 1);
|
|
done();
|
|
});
|
|
});
|
|
assert.strictEqual(resNoPromise, undefined, 'No promise is returned');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#pool.query()', () => {
|
|
// promise
|
|
it('should call patched client.query()', async () => {
|
|
const pgPoolattributes = {
|
|
...DEFAULT_PGPOOL_ATTRIBUTES,
|
|
};
|
|
const pgAttributes = {
|
|
...DEFAULT_PG_ATTRIBUTES,
|
|
[AttributeNames.DB_STATEMENT]: 'SELECT NOW()',
|
|
};
|
|
const events: TimedEvent[] = [];
|
|
const span = registry.getTracer('test-pg-pool').startSpan('test span');
|
|
await registry.getTracer('test-pg-pool').withSpan(span, async () => {
|
|
try {
|
|
const result = await pool.query('SELECT NOW()');
|
|
runCallbackTest(span, pgPoolattributes, events, okStatus, 2, 0);
|
|
runCallbackTest(span, pgAttributes, events, okStatus, 2, 1);
|
|
assert.ok(result, 'pool.query() returns a promise');
|
|
} catch (e) {
|
|
throw e;
|
|
}
|
|
});
|
|
});
|
|
|
|
// callback
|
|
it('should not return a promise if callback is provided', done => {
|
|
const pgPoolattributes = {
|
|
...DEFAULT_PGPOOL_ATTRIBUTES,
|
|
};
|
|
const pgAttributes = {
|
|
...DEFAULT_PG_ATTRIBUTES,
|
|
[AttributeNames.DB_STATEMENT]: 'SELECT NOW()',
|
|
};
|
|
const events: TimedEvent[] = [];
|
|
const parentSpan = registry
|
|
.getTracer('test-pg-pool')
|
|
.startSpan('test span');
|
|
registry.getTracer('test-pg-pool').withSpan(parentSpan, () => {
|
|
const resNoPromise = pool.query('SELECT NOW()', (err, result) => {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
runCallbackTest(parentSpan, pgPoolattributes, events, okStatus, 2, 0);
|
|
runCallbackTest(parentSpan, pgAttributes, events, okStatus, 2, 1);
|
|
done();
|
|
});
|
|
assert.strictEqual(resNoPromise, undefined, 'No promise is returned');
|
|
});
|
|
});
|
|
});
|
|
});
|