Skip to content

Commit

Permalink
Merge pull request newrelic#65 from michaelgoin/remove-aws-server-dep…
Browse files Browse the repository at this point in the history
…endency

Remove AWS servers as dependency for versioned tests
  • Loading branch information
astormnewrelic authored Jul 29, 2020
2 parents b7a5691 + 438f280 commit 9da37c3
Show file tree
Hide file tree
Showing 16 changed files with 573 additions and 289 deletions.
3 changes: 3 additions & 0 deletions merged/aws-sdk/tests/versioned/amazon-dax-client.tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

const tap = require('tap')
const utils = require('@newrelic/test-utilities')

const common = require('./common')
const { FAKE_CREDENTIALS } = require('./aws-server-stubs')

// This will not resolve / allow web requests. Even with real ones, requests
// have to execute within the same VPC as the DAX configuration. When adding DAX support,
Expand Down Expand Up @@ -37,6 +39,7 @@ tap.test('amazon-dax-client', (t) => {
const AmazonDaxClient = require('amazon-dax-client')

daxClient = new AmazonDaxClient({
credentials: FAKE_CREDENTIALS,
endpoints: DAX_ENDPOINTS,
maxRetries: 0 // fail fast
})
Expand Down
82 changes: 50 additions & 32 deletions merged/aws-sdk/tests/versioned/aws-sdk.tap.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,71 +9,89 @@ const tap = require('tap')
const utils = require('@newrelic/test-utilities')
utils.assert.extendTap(tap)

const { createEmptyResponseServer, FAKE_CREDENTIALS } = require('./aws-server-stubs')

tap.test('aws-sdk', (t) => {
t.autoend()

let helper = null
let AWS = null

let server = null
let endpoint = null

t.beforeEach((done) => {
helper = utils.TestAgent.makeInstrumented()
helper.registerInstrumentation({
moduleName: 'aws-sdk',
type: 'conglomerate',
onRequire: require('../../lib/instrumentation')
server = createEmptyResponseServer()
server.listen(0, () => {
helper = utils.TestAgent.makeInstrumented()
helper.registerInstrumentation({
moduleName: 'aws-sdk',
type: 'conglomerate',
onRequire: require('../../lib/instrumentation')
})
AWS = require('aws-sdk')
AWS.config.update({region: 'us-east-1'})

endpoint = `http://localhost:${server.address().port}`
done()
})
AWS = require('aws-sdk')
done()
})

t.afterEach((done) => {
server.close()
server = null

helper && helper.unload()
AWS = null
done()
})

t.test('should mark requests to be dt-disabled', (t) => {
const https = require('https')
sinon.spy(https, 'request')
t.test('should mark requests to be dt-disabled', {skip: true}, (t) => {
// http because we've changed endpoint to be http
const http = require('http')
sinon.spy(http, 'request')
t.tearDown(() => {
// `afterEach` runs before `tearDown`, so the sinon spy may have already
// been removed.
if (https.request.restore) {
https.request.restore()
if (http.request.restore) {
http.request.restore()
}
})

AWS.config.update({
region: 'region',
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'foobar'
})
})

const s3 = new AWS.S3({
apiVersion: '2006-03-01',
credentials: FAKE_CREDENTIALS,
endpoint: endpoint,
// allows using generic endpoint, instead of needing a
// bucket.endpoint server setup.
s3ForcePathStyle: true,
params: {Bucket: 'bucket'}
})
s3.listObjects({Delimiter: '/'}, () => {})
s3.listObjects({Delimiter: '/'}, (err) => {
t.error(err)

if (t.ok(https.request.calledOnce, 'should call http.request')) {
const args = https.request.getCall(0).args
const headers = args[0].headers
const symbols = Object.getOwnPropertySymbols(headers).filter((s) => {
return s.toString() === 'Symbol(Disable distributed tracing)'
})
t.equal(symbols.length, 1, 'should have disabled dt')
}
t.end()
if (t.ok(http.request.calledOnce, 'should call http.request')) {
const args = http.request.getCall(0).args
const headers = args[0].headers
const symbols = Object.getOwnPropertySymbols(headers).filter((s) => {
return s.toString() === 'Symbol(Disable distributed tracing)'
})
t.equal(symbols.length, 1, 'should have disabled dt')
}
t.end()
})
})

t.test('should maintain transaction state in promises', (t) => {
const service = new AWS.SES()
const service = new AWS.SES({
credentials: FAKE_CREDENTIALS,
endpoint: endpoint
})
helper.runInTransaction((tx) => {
service.cloneReceiptRuleSet({
OriginalRuleSetName: 'RuleSetToClone',
RuleSetName: 'RuleSetToCreate'
}).promise().catch(() => {
}).promise().then(() => {
t.transaction(tx)
tx.end()
ender()
Expand All @@ -85,7 +103,7 @@ tap.test('aws-sdk', (t) => {
service.cloneReceiptRuleSet({
OriginalRuleSetName: 'RuleSetToClone',
RuleSetName: 'RuleSetToCreate'
}).promise().catch(() => {
}).promise().then(() => {
t.transaction(tx)
tx.end()
ender()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
'use strict'

const http = require('http')

function createEmptyResponseServer() {
const server = http.createServer(function(req, res) {
if (
req.method === 'GET' ||
req.method === 'POST' ||
req.method === 'PUT' ||
req.method === 'HEAD' ||
req.method === 'DELETE'
) {
handlePost(req, res)
return
}

// sometimes the aws-sdk will obfuscate this error
// so logging out.
// eslint-disable-next-line no-console
console.log('Unhandled request method: ', req.method)

res.statusCode = 500
res.end('Unhandled request method')
})

return server
}

function handlePost(req, res) {
req.on('data', () => {})

req.on('end', () => {
// currently, some tests do not rely on real responses back.
// it is enough to return something valid to the client.
res.end()
})
}

module.exports = createEmptyResponseServer
21 changes: 21 additions & 0 deletions merged/aws-sdk/tests/versioned/aws-server-stubs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
'use strict'

const createSqsServer = require('./sqs-server')
const createEmptyResponseServer = require('./empty-response-server')

// Specific values are unimportant because we'll be hitting our
// custom servers. But they need to be populated.
const FAKE_CREDENTIALS = {
accessKeyId: 'FAKE ID',
secretAccessKey: 'FAKE KEY'
}

module.exports = {
createSqsServer,
createEmptyResponseServer,
FAKE_CREDENTIALS
}
181 changes: 181 additions & 0 deletions merged/aws-sdk/tests/versioned/aws-server-stubs/sqs-server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
'use strict'

const http = require('http')
const fs = require('fs')
const path = require('path')

function createSqsServer() {
const server = http.createServer(function(req, res) {
if (req.method === 'POST') {
handleSqsPost(req, res)
return
}

res.statusCode = 500
res.end()
})

return server
}

function handleSqsPost(req, res) {
let body = ''

req.on('data', chunk => {
body += chunk.toString()
})

req.on('end', () => {
const endpoint = `http://localhost:${req.connection.localPort}`
const parsed = parseBody(body)

const getDataFunction = createGetDataFromAction(endpoint, parsed)

getDataFunction((err, data) => {
if (err) {
res.statusCode = 500
// eslint-disable-next-line no-console
console.log(err)
}

res.end(data)
})
})
}

function createGetDataFromAction(endpoint, body) {
switch (body.Action) {
case 'CreateQueue':
return getCreateQueueResponse.bind(null, endpoint, body.QueueName)
case 'SendMessage':
return getSendMessageResponse.bind(null)
case 'SendMessageBatch':
return getSendMessageBatchResponse.bind(null)
case 'ReceiveMessage':
return getReceiveMessageResponse.bind(null)
default:
return function actionNotImplemented(callback) {
setImmediate(() => {
callback(new Error('Action not implemented'))
})
}
}
}

function parseBody(body) {
const parsed = Object.create(null)

const items = body.split('&')
items.forEach((item) => {
const [key, value] = item.split('=')
parsed[key] = value
})

return parsed
}

let createQueueResponse = null
function getCreateQueueResponse(endpoint, queueName, callback) {
if (createQueueResponse) {
const modifiedResponse = replaceQueueUrl(createQueueResponse, endpoint, queueName)

setImmediate(() => {
callback(null, modifiedResponse)
})
return
}

readFromXml('create-queue-response.xml', (err, data) => {
if (err) {
callback(err)
return
}

createQueueResponse = data
const modifiedResponse = replaceQueueUrl(createQueueResponse, endpoint, queueName)

callback(null, modifiedResponse)
})
}

function replaceQueueUrl(xml, endpoint, queueName) {
const modifiedResponse = xml.replace(
'<QueueUrl></QueueUrl>',
`<QueueUrl>${endpoint}/queue/${queueName}</QueueUrl>`
)

return modifiedResponse
}

let sendMessageResponse = null
function getSendMessageResponse(callback) {
if (sendMessageResponse) {
setImmediate(() => {
callback(null, sendMessageResponse)
})
return
}

readFromXml('send-message-response.xml', (err, data) => {
if (err) {
callback(err)
return
}

sendMessageResponse = data
callback(null, sendMessageResponse)
})
}

let sendMessageBatchResponse = null
function getSendMessageBatchResponse(callback) {
if (sendMessageBatchResponse) {
setImmediate(() => {
callback(null, sendMessageBatchResponse)
})
return
}

readFromXml('send-message-batch-response.xml', (err, data) => {
if (err) {
callback(err)
return
}

sendMessageBatchResponse = data
callback(null, sendMessageBatchResponse)
})
}

let receiveMessageResponse = null
function getReceiveMessageResponse(callback) {
if (receiveMessageResponse) {
setImmediate(() => {
callback(null, receiveMessageResponse)
})
return
}

readFromXml('receive-message-response.xml', (err, data) => {
if (err) {
callback(err)
return
}

receiveMessageResponse = data
callback(null, receiveMessageResponse)
})
}

function readFromXml(fileName, callback) {
const fullPath = path.join(__dirname, 'responses', fileName)
fs.readFile(fullPath, 'utf8', function(err, data) {
callback(err, data)
})
}

module.exports = createSqsServer
Loading

0 comments on commit 9da37c3

Please sign in to comment.