Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/instrumentation/span.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Span.prototype._encode = function (cb) {
var payload = {
id: self.id,
transaction_id: self.transaction.id,
parent_id: self.transaction.id,
trace_id: self.transaction.traceId,
timestamp: self.transaction.timestamp,
name: self.name,
Expand Down
9 changes: 6 additions & 3 deletions test/instrumentation/span.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ test('#_encode() - ended unnamed', function myTest1 (t) {
span.end()
span._encode(function (err, payload) {
t.error(err)
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration', 'stacktrace'])
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'parent_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration', 'stacktrace'])
t.ok(/^[\da-f]{16}$/.test(payload.id))
t.ok(/^[\da-f]{16}$/.test(payload.transaction_id))
t.ok(/^[\da-f]{16}$/.test(payload.parent_id))
t.ok(/^[\da-f]{32}$/.test(payload.trace_id))
t.equal(payload.transaction_id, trans.id)
t.equal(payload.timestamp, trans.timestamp)
Expand All @@ -132,9 +133,10 @@ test('#_encode() - ended named', function myTest2 (t) {
span.end()
span._encode(function (err, payload) {
t.error(err)
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration', 'stacktrace'])
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'parent_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration', 'stacktrace'])
t.ok(/^[\da-f]{16}$/.test(payload.id))
t.ok(/^[\da-f]{16}$/.test(payload.transaction_id))
t.ok(/^[\da-f]{16}$/.test(payload.parent_id))
t.ok(/^[\da-f]{32}$/.test(payload.trace_id))
t.equal(payload.transaction_id, trans.id)
t.equal(payload.timestamp, trans.timestamp)
Expand All @@ -157,9 +159,10 @@ test('#_encode() - disabled stack traces', function (t) {
span.end()
span._encode(function (err, payload) {
t.error(err)
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration'])
t.deepEqual(Object.keys(payload), ['id', 'transaction_id', 'parent_id', 'trace_id', 'timestamp', 'name', 'type', 'start', 'duration'])
t.ok(/^[\da-f]{16}$/.test(payload.id))
t.ok(/^[\da-f]{16}$/.test(payload.transaction_id))
t.ok(/^[\da-f]{16}$/.test(payload.parent_id))
t.ok(/^[\da-f]{32}$/.test(payload.trace_id))
t.equal(payload.transaction_id, trans.id)
t.equal(payload.timestamp, trans.timestamp)
Expand Down
33 changes: 23 additions & 10 deletions test/integration/api-schema/_utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const exec = require('child_process').exec
const fs = require('fs')
const tmpdir = require('os').tmpdir
const join = require('path').join

Expand All @@ -10,21 +11,33 @@ const thunky = require('thunky')

const schemaDir = thunky(function (cb) {
const dir = join(tmpdir(), '.schemacache')
const script = join(__dirname, 'download-json-schemas.sh')
const cmd = `"${script}" "${dir}"`
console.log('downloading schemas from GitHub to %s...', dir)
exec(cmd, function (err) {
if (err) return cb(err)
cb(null, dir)
fs.stat(dir, function (err) {
if (!err) return cb(null, dir)

const script = join(__dirname, 'download-json-schemas.sh')
const cmd = `"${script}" "${dir}"`
console.log('downloading schemas from GitHub to %s...', dir)
exec(cmd, function (err) {
if (err) return cb(err)
cb(null, dir)
})
})
})

exports.transactionsValidator = thunky(function (cb) {
loadSchema(join('transactions', 'payload.json'), cb)
exports.metadataValidator = thunky(function (cb) {
loadSchema('metadata.json', cb)
})

exports.transactionValidator = thunky(function (cb) {
loadSchema(join('transactions', 'v2_transaction.json'), cb)
})

exports.spanValidator = thunky(function (cb) {
loadSchema(join('spans', 'v2_span.json'), cb)
})

exports.errorsValidator = thunky(function (cb) {
loadSchema(join('errors', 'payload.json'), cb)
exports.errorValidator = thunky(function (cb) {
loadSchema(join('errors', 'v2_error.json'), cb)
})

function loadSchema (relativePath, cb) {
Expand Down
244 changes: 162 additions & 82 deletions test/integration/api-schema/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,148 @@ if (require('os').platform() === 'win32') {
process.exit()
}

const getPort = require('get-port')
const http = require('http')
const zlib = require('zlib')

const afterAll = require('after-all-results')
const ndjson = require('ndjson')
const test = require('tape')

const utils = require('./_utils')
const Agent = require('../../_agent')

const next = afterAll(function (err, validators) {
if (err) throw err

const [validateMetadata, validateTransaction, validateSpan, validateError] = validators

test('metadata schema failure', function (t) {
t.equal(validateMetadata({}), false)
t.deepEqual(validateMetadata.errors, [
{ field: 'data.service', message: 'is required', value: {}, type: [ 'object' ], schemaPath: [] }
])
t.equal(validateMetadata({ service: {} }), false)
t.deepEqual(validateMetadata.errors, [
{ field: 'data.service.agent', message: 'is required', value: {}, type: 'object', schemaPath: [ 'properties', 'service' ] },
{ field: 'data.service.name', message: 'is required', value: {}, type: 'object', schemaPath: [ 'properties', 'service' ] }
])
t.end()
})

getPort().then(function (port) {
const agent = require('../../../').start({
serviceName: 'test',
serverUrl: 'http://localhost:' + port,
captureExceptions: false
test('transaction schema failure', function (t) {
t.equal(validateTransaction({}), false)
t.deepEqual(validateTransaction.errors, [
{ field: 'data.duration', message: 'is required', value: {}, type: 'object', schemaPath: [ 'allOf', 0 ] },
{ field: 'data.type', message: 'is required', value: {}, type: 'object', schemaPath: [ 'allOf', 0 ] },
{ field: 'data.id', message: 'is required', value: {}, type: undefined, schemaPath: [ 'allOf', 1 ] },
{ field: 'data.trace_id', message: 'is required', value: {}, type: undefined, schemaPath: [ 'allOf', 1 ] },
{ field: 'data.span_count', message: 'is required', value: {}, type: undefined, schemaPath: [ 'allOf', 1 ] }
])
t.end()
})

const http = require('http')
const zlib = require('zlib')
const test = require('tape')
const utils = require('./_utils')

test('transactions schema failure', function (t) {
utils.transactionsValidator(function (err, validate) {
t.error(err)
t.notOk(validate({}))
t.deepEqual(validate.errors, [
{ field: 'data.service', message: 'is required', value: {}, type: 'object', schemaPath: [] },
{ field: 'data.transactions', message: 'is required', value: {}, type: 'object', schemaPath: [] }
])
t.end()
})
test('span schema failure', function (t) {
t.equal(validateSpan({}), false)
t.deepEqual(validateSpan.errors, [
{ field: 'data.duration', message: 'is required', value: {}, type: 'object', schemaPath: [] },
{ field: 'data.name', message: 'is required', value: {}, type: 'object', schemaPath: [] },
{ field: 'data.start', message: 'is required', value: {}, type: 'object', schemaPath: [] },
{ field: 'data.type', message: 'is required', value: {}, type: 'object', schemaPath: [] }
])
t.end()
})

test('errors schema failure', function (t) {
utils.errorsValidator(function (err, validate) {
t.error(err)
t.notOk(validate({}))
t.deepEqual(validate.errors, [
{ field: 'data.service', message: 'is required', value: {}, type: 'object', schemaPath: [] },
{ field: 'data.errors', message: 'is required', value: {}, type: 'object', schemaPath: [] }
])
t.end()
})
test('error schema failure', function (t) {
t.equal(validateError({}), false)
t.deepEqual(validateError.errors, [
{ field: 'data', message: 'no schemas match', value: {}, type: 'object', schemaPath: [ 'allOf', 0 ] }
])
t.equal(validateError({ exception: {} }), false)
t.deepEqual(validateError.errors, [
{ field: 'data.exception', message: 'no schemas match', value: {}, type: [ 'object', 'null' ], schemaPath: [ 'allOf', 0, 'properties', 'exception' ] }
])
t.equal(validateError({ log: {} }), false)
t.deepEqual(validateError.errors, [
{ field: 'data.log.message', message: 'is required', value: {}, type: [ 'object', 'null' ], schemaPath: [ 'allOf', 0, 'properties', 'log' ] }
])
t.end()
})

test('POST /transactions', function (t) {
test('metadata + transaction schema', function (t) {
t.plan(7)

utils.transactionsValidator(function (err, validate) {
t.error(err)
let agent
const validators = [validateMetadata, validateTransaction]

const server = http.createServer(function (req, res) {
t.equal(req.method, 'POST', 'server should recieve a POST request')
t.equal(req.url, '/intake/v2/events', 'server should recieve request to correct endpoint')

req
.pipe(zlib.createGunzip())
.pipe(ndjson.parse())
.on('data', function (data) {
const type = Object.keys(data)[0]
const validate = validators.shift()
t.equal(validate(data[type]), true, type + ' should be valid')
t.equal(validate.errors, null, type + ' should not have any validation errors')
})
.on('end', function () {
res.end()
server.close()
agent.destroy()
t.end()
})
})

const server = http.createServer(function (req, res) {
t.equal(req.method, 'POST')
t.equal(req.url, '/v1/transactions')
server.listen(function () {
agent = newAgent(server)
agent.startTransaction('name1', 'type1')
agent.endTransaction()
agent.flush(function (err) {
t.error(err, 'flush should not result in an error')
})
})
})

const buffers = []
const gunzip = zlib.createGunzip()
const unzipped = req.pipe(gunzip)
test('metadata + span schema', function (t) {
t.plan(7)

unzipped.on('data', buffers.push.bind(buffers))
unzipped.on('end', function () {
let agent
const validators = [validateMetadata, validateSpan]

const server = http.createServer(function (req, res) {
t.equal(req.method, 'POST', 'server should recieve a POST request')
t.equal(req.url, '/intake/v2/events', 'server should recieve request to correct endpoint')

req
.pipe(zlib.createGunzip())
.pipe(ndjson.parse())
.on('data', function (data) {
const type = Object.keys(data)[0]
const validate = validators.shift()
t.equal(validate(data[type]), true, type + ' should be valid')
t.equal(validate.errors, null, type + ' should not have any validation errors')
})
.on('end', function () {
res.end()
server.close()
const data = JSON.parse(Buffer.concat(buffers))
t.equal(data.transactions.length, 1, 'expect 1 transaction to be sent')
const valid = validate(data)
t.equal(validate.errors, null, 'should not have any validation errors')
t.equal(valid, true, 'should be valid')
agent.destroy()
t.end()
})
})
})

server.listen(port, function () {
agent.startTransaction('name1', 'type1')
const span = agent.startSpan('name1', 'type1')
span.end()
agent.endTransaction()
server.listen(function () {
agent = newAgent(server)
agent.startTransaction()
const span = agent.startSpan('name1', 'type1')
span.end()
// Collecting the span stack trace is an async process. Wait a little before flushing
setTimeout(function () {
agent.flush(function (err) {
server.close()
t.error(err)
t.error(err, 'flush should not result in an error')
})
})
}, 250)
})
})

Expand All @@ -87,41 +155,53 @@ getPort().then(function (port) {
'just a string'
]
errors.forEach(function (error, index) {
test('POST /errors - ' + index, function (t) {
test('metadata + error schema - ' + index, function (t) {
t.plan(7)

utils.errorsValidator(function (err, validate) {
t.error(err)

const server = http.createServer(function (req, res) {
t.equal(req.method, 'POST')
t.equal(req.url, '/v1/errors')
let agent
const validators = [validateMetadata, validateError]

const buffers = []
const gunzip = zlib.createGunzip()
const unzipped = req.pipe(gunzip)

unzipped.on('data', buffers.push.bind(buffers))
unzipped.on('end', function () {
const server = http.createServer(function (req, res) {
t.equal(req.method, 'POST', 'server should recieve a POST request')
t.equal(req.url, '/intake/v2/events', 'server should recieve request to correct endpoint')

req
.pipe(zlib.createGunzip())
.pipe(ndjson.parse())
.on('data', function (data) {
const type = Object.keys(data)[0]
const validate = validators.shift()
t.equal(validate(data[type]), true, type + ' should be valid')
t.equal(validate.errors, null, type + ' should not have any validation errors')
})
.on('end', function () {
res.end()
server.close()
const data = JSON.parse(Buffer.concat(buffers))
t.equal(data.errors.length, 1, 'expect 1 error to be sent')
const valid = validate(data)
t.equal(validate.errors, null, 'should not have any validation errors')
t.equal(valid, true, 'should be valid')
agent.destroy()
t.end()
})
})
})

server.listen(port, function () {
agent.captureError(error, function (err) {
server.close()
t.error(err)
})
server.listen(function () {
agent = newAgent(server)
agent.captureError(error, function (err) {
t.error(err, 'captureError should not result in an error')
})
})
})
})
}, function (err) {
throw err
})

utils.metadataValidator(next())
utils.transactionValidator(next())
utils.spanValidator(next())
utils.errorValidator(next())

function newAgent (server) {
return new Agent().start({
serviceName: 'test',
serverUrl: 'http://localhost:' + server.address().port,
captureExceptions: false,
disableInstrumentations: ['http']
})
}
Loading