diff --git a/integration-tests/helpers.js b/integration-tests/helpers.js index ae24451bea3..cd4ed9edf29 100644 --- a/integration-tests/helpers.js +++ b/integration-tests/helpers.js @@ -1,6 +1,8 @@ 'use strict' const { promisify } = require('util') +const { createHash } = require('crypto') +const uuid = require('crypto-randomuuid') const express = require('express') const bodyParser = require('body-parser') const msgpack = require('msgpack-lite') @@ -24,6 +26,7 @@ class FakeAgent extends EventEmitter { constructor (port = 0) { super() this.port = port + this._rc_files = [] } async start () { @@ -38,6 +41,69 @@ class FakeAgent extends EventEmitter { payload: msgpack.decode(req.body, { codec }) }) }) + app.post('/v0.7/config', (req, res) => { + const buffers = [] + req.on('data', buffers.push.bind(buffers)) + req.on('end', () => { + const { products } = JSON.parse(Buffer.concat(buffers).toString()).client + + const expires = (new Date(Date.now() + 1000 * 60 * 60 * 24)).toISOString() // in 24 hours + const clientID = uuid() // TODO: What is this? It isn't the runtime-id + + // Currently, only `opaque_backend_state` and `targets` are used by dd-trace-js in the object below + const targets = { + signatures: [], + signed: { + _type: 'targets', + custom: { + agent_refresh_interval: 5, + opaque_backend_state: '' + }, + expires, + spec_version: '1.0.0', + targets: {}, + version: 12345 + } + } + const opaqueBackendState = { + version: 2, + state: { file_hashes: { key: [] } } + } + const targetFiles = [] + const clientConfigs = [] + + const files = this._rc_files.filter(([product]) => products.includes(product)) + + for (const { orgId, product, id, name, config } of files) { + const path = `datadog/${orgId}/${product}/${id}/${name}` + const fileDigest = createHash('sha256').update(config).digest() + + opaqueBackendState.state.file_hashes.key.push(fileDigest.toString('base64')) + + targets.signed.targets[path] = { + custom: { + c: [clientID], + 'tracer-predicates': { tracer_predicates_v1: [{ clientID }] }, + v: 20 + }, + hashes: { sha256: fileDigest.toString('hex') }, + length: config.length + } + + targetFiles.push({ path, raw: base64(config) }) + clientConfigs.push(path) + } + + targets.signed.custom.opaque_backend_state = base64(opaqueBackendState) + + res.send(JSON.stringify({ + roots: [], // Not used by dd-trace-js currently + targets: base64(targets), + target_files: targetFiles, + client_configs: clientConfigs + })) + }) + }) app.post('/profiling/v1/input', upload.any(), (req, res) => { res.status(200).send() this.emit('message', { @@ -75,6 +141,30 @@ class FakeAgent extends EventEmitter { }) } + /** + * Remove any existing config added by calls to FakeAgent#addRemoteConfig. + */ + resetRemoteConfig () { + this._rc_files = [] + } + + /** + * Add a config object to be returned by the fake Remote Config endpoint. + * @param {Object} config - Object containing the Remote Config "file" and metadata + * @param {number} [config.orgId=2] - The Datadog organization ID + * @param {string} config.product - The Remote Config product name + * @param {string} config.id - The Remote Config config ID + * @param {string} [config.name] - The Remote Config "name". Defaults to the sha256 hash of `config.id` + * @param {Object} config.config - The Remote Config "file" object + */ + addRemoteConfig (config) { + config.orgId = config.orgId ?? 2 + config.name = config.name ?? crypto.createHash('sha256').update(config.id).digest('hex') + config.config = JSON.stringify(config.config) + + this._rc_files.push(config) + } + // **resolveAtFirstSuccess** - specific use case for Next.js (or any other future libraries) // where multiple payloads are generated, and only one is expected to have the proper span (ie next.request), // but it't not guaranteed to be the last one (so, expectedMessageCount would not be helpful). @@ -486,6 +576,11 @@ function sandboxCwd () { return sandbox.folder } +function base64 (strOrObj) { + const str = typeof strOrObj === 'string' ? strOrObj : JSON.stringify(strOrObj) + return Buffer.from(str).toString('base64') +} + module.exports = { FakeAgent, spawnProc,