Skip to content

Commit 5ba1895

Browse files
authored
Merge branch 'main' into engraver-auto-version-upgrade/minorpatch/npm/0-1788160968
2 parents d4f79c4 + a02a46d commit 5ba1895

85 files changed

Lines changed: 22134 additions & 389 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/integration_tests_local.yml‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ name: local integration tests
44
# against the real AWS Lambda base images + the Runtime Interface Emulator
55
# (RIE). No AWS account or credentials required.
66
#
7-
# One job per Node major version; each job builds and tests both container
8-
# variants (cjs, esm), which share the same ~1 GB base image, so each job
9-
# pulls it exactly once. If public.ecr.aws anonymous pull limits ever bite,
10-
# the fix is to mirror the base images to GHCR (same pattern dd-trace-js
11-
# uses in its mirror-image.yml workflow).
7+
# One job per Node major version; each job builds the container/layer fixture
8+
# images and runs every case (default: all of them) against that runtime.
9+
# The fixtures share the same ~1 GB base image, so each job pulls it exactly
10+
# once. If public.ecr.aws anonymous pull limits ever bite, the fix is to
11+
# mirror the base images to GHCR (same pattern dd-trace-js uses in its
12+
# mirror-image.yml workflow).
1213

1314
on:
1415
pull_request:

‎integration_tests/container/cjs/Dockerfile‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
ARG NODE_VERSION=22
22
FROM public.ecr.aws/lambda/nodejs:${NODE_VERSION}
33

4-
COPY package.json handler.js ${LAMBDA_TASK_ROOT}/
4+
# handler.js is the default redirect-mode fixture (DD_LAMBDA_HANDLER=handler.handle).
5+
# The remaining *.js files are the manual-wrap fixture handlers ported from the
6+
# AWS suite (throw-error, status-code-500s, send-metrics, process-input,
7+
# http-requests) plus extractor.js for the DD_TRACE_EXTRACTOR case.
8+
COPY package.json *.js ${LAMBDA_TASK_ROOT}/
59
COPY datadog-lambda-js-local.tgz /tmp/datadog-lambda-js-local.tgz
610
RUN cd ${LAMBDA_TASK_ROOT} \
711
&& npm install --omit=dev \
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Callback-style handler — (event, context, callback) instead of async —
2+
// manually wrapped with datadog(). The migration spike broke exactly this
3+
// seam: the new host's tracePromise wrapper replaced promisifiedHandler's
4+
// call site, so callback handlers returned null to API Gateway. handler.spec
5+
// pins the unit behavior; this case pins the same path end to end through
6+
// the RIE invoke. The setTimeout makes the completion genuinely asynchronous
7+
// so the wrapper cannot mistake it for a sync return.
8+
const { datadog } = require("datadog-lambda-js");
9+
10+
function handle(event, context, callback) {
11+
setTimeout(() => {
12+
callback(null, { message: "hello, dog!" });
13+
}, 10);
14+
}
15+
16+
module.exports.handle = datadog(handle);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Custom trace-context extractor fixture, loaded via
2+
// DD_TRACE_EXTRACTOR=extractor.extract (handler-style "module.function"
3+
// string, resolved by the shim's user-function loader).
4+
//
5+
// Returns a fixed context on every event: the extracted parent and the
6+
// `_dd.parent_source` span tag must show up in the trace, which the golden
7+
// asserts deterministically (ids are normalized to XXXX; the tag is not).
8+
module.exports.extract = async (event, context) => {
9+
return {
10+
traceId: "1234567890123456789",
11+
parentId: "9876543210987654321",
12+
sampleMode: 1,
13+
source: "event",
14+
};
15+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// fetch variant of http-requests.js. On Node 18+ the global fetch is
2+
// undici, which dd-trace instruments through its undici plugin — a different
3+
// injection path than the http/https plugin that patches axios in
4+
// http-requests.js. Both paths must keep injecting the x-datadog-* trace
5+
// context; this case pins the fetch one.
6+
//
7+
// Like http-requests.js this entry point is UNWRAPPED and runs through the
8+
// npm redirect entry (dist/handler.handler) in the cjs-fetch-requests case,
9+
// so dd-trace initializes before the user handler loads and the tracer's
10+
// undici plugin does the injection. The mock echoes the request headers and
11+
// this handler logs them, so the golden shows the injected downstream trace
12+
// context (x-datadog-trace-id / x-datadog-parent-id, normalized to XXXX).
13+
const { sendDistributionMetric } = require("datadog-lambda-js");
14+
15+
const urls = (process.env.MOCK_HTTP_URLS ||
16+
"https://ip-ranges.datadoghq.com,https://ip-ranges.datadoghq.eu"
17+
).split(",");
18+
19+
async function handle(event, context) {
20+
const responsePayload = { message: "hello, dog!" };
21+
22+
sendDistributionMetric("serverless.integration_test.execution", 1, "function:fetch-request");
23+
24+
for (let index = 0; index < urls.length; index++) {
25+
const response = await fetch(urls[index]);
26+
const body = await response.json();
27+
console.log(`mock-http saw headers for ${urls[index]}: ${JSON.stringify(body.headers)}`);
28+
}
29+
30+
console.log(`Snapshot test fetch requests successfully made to URLs: ${urls}`);
31+
32+
return responsePayload;
33+
}
34+
35+
module.exports.handle = handle;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Manual-wrap companion to http-requests.js, run as the manual-http-requests
2+
// case — the exact shape of the AWS suite's http-requests function
3+
// (manual datadog() wrap, no userland dd-trace init).
4+
//
5+
// With no tracer initialized, TraceListener falls back to the library's own
6+
// patchHttp (src/trace/patch-http.ts), which wraps http/https and logs
7+
// "HTTP GET <url> TraceHeaders: [...]" per request. On real Lambda the
8+
// injected headers come from the platform's pass-through _X_AMZN_TRACE_ID;
9+
// under RIE that variable cannot be emulated (the RIC owns it), so the
10+
// golden pins the empty-context form (TraceHeaders: []) plus the mock echo
11+
// of the exact header set sent. The path under test — patchHttp wiring —
12+
// fails this golden the moment it stops wrapping or logging; the
13+
// context-dependent header values are pinned by the patch-http unit tests
14+
// and the AWS suite.
15+
const { datadog } = require("datadog-lambda-js");
16+
const { handle } = require("./http-requests");
17+
18+
module.exports.handle = datadog(handle);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Ported from integration_tests/http-requests.js with one seam adaptation:
2+
// the AWS suite hits the real ip-ranges.datadoghq.com/.eu endpoints; the
3+
// local harness is hermetic, so URLs come from MOCK_HTTP_URLS (set by run.sh
4+
// to the mock-http container on the case's docker network). The default keeps
5+
// this file runnable outside the harness.
6+
//
7+
// This entry point is UNWRAPPED and runs through the npm redirect entry
8+
// (dist/handler.handler) in the cjs-http-requests case. Redirect mode
9+
// initializes dd-trace first, so the tracer's http plugin patches axios and
10+
// the mock echo below shows the injected x-datadog-* headers in the golden.
11+
// The manual-wrap variant of the AWS suite's function — which exercises the
12+
// library's own patchHttp fallback instead — lives in http-requests-manual.js
13+
// and runs as the manual-http-requests case.
14+
//
15+
// The mock echoes the request headers it received, and this handler logs them,
16+
// so both goldens show the injected downstream trace context
17+
// (x-datadog-trace-id / x-datadog-parent-id, normalized to XXXX).
18+
const { sendDistributionMetric } = require("datadog-lambda-js");
19+
const axios = require("axios");
20+
21+
const urls = (process.env.MOCK_HTTP_URLS ||
22+
"https://ip-ranges.datadoghq.com,https://ip-ranges.datadoghq.eu"
23+
).split(",");
24+
25+
async function handle(event, context) {
26+
const responsePayload = { message: "hello, dog!" };
27+
28+
sendDistributionMetric("serverless.integration_test.execution", 1, "function:http-request");
29+
30+
for (let index = 0; index < urls.length; index++) {
31+
const response = await axios({ url: urls[index], method: "get" });
32+
console.log(`mock-http saw headers for ${urls[index]}: ${JSON.stringify(response.data.headers)}`);
33+
}
34+
35+
console.log(`Snapshot test http requests successfully made to URLs: ${urls}`);
36+
37+
return responsePayload;
38+
}
39+
40+
module.exports.handle = handle;

‎integration_tests/container/cjs/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "1.0.0",
44
"private": true,
55
"dependencies": {
6+
"axios": "1.18.1",
67
"dd-trace": "5.105.0"
78
}
89
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const tracer = require("dd-trace").init();
2+
3+
const { datadog, sendDistributionMetric } = require("datadog-lambda-js");
4+
5+
// Minimal example of tracing to reduce flakiness
6+
async function handle(event, context) {
7+
const span = tracer.scope().active();
8+
9+
const responsePayload = { message: "hello, dog!" };
10+
11+
sendDistributionMetric("serverless.integration_test.execution", 1, "function:process-input-traced");
12+
13+
const { recordIds, eventType } = getRecordIds(event);
14+
15+
if (recordIds) {
16+
responsePayload.recordIds = recordIds;
17+
}
18+
19+
if (eventType) {
20+
responsePayload.eventType = eventType;
21+
} else {
22+
const requestId = getAPIGatewayRequestId(event);
23+
24+
if (requestId) {
25+
responsePayload.eventType = "APIGateway";
26+
27+
if (span) {
28+
span.setTag("api_gateway_request_id", requestId);
29+
}
30+
}
31+
}
32+
33+
if (span) {
34+
span.setTag("event_type", responsePayload.eventType);
35+
}
36+
37+
return responsePayload;
38+
}
39+
40+
const getRecordIds = tracer.wrap("getRecordIds", (event) => {
41+
const recordIds = [];
42+
let eventType = null;
43+
if (event.Records) {
44+
event.Records.forEach((record) => {
45+
if (record.messageId) {
46+
eventType = "SQS";
47+
recordIds.push(record.messageId);
48+
}
49+
if (record.Sns) {
50+
eventType = "SNS";
51+
recordIds.push(record.Sns.MessageId);
52+
}
53+
if (record.s3) {
54+
eventType = "S3";
55+
const bucketName = record.s3.bucket.name;
56+
const objectKey = record.s3.object.key;
57+
recordIds.push(`${bucketName}/${objectKey}`);
58+
}
59+
});
60+
}
61+
62+
if (eventType) {
63+
const span = tracer.scope().active();
64+
65+
if (span) {
66+
span.setTag("record_event_type", eventType);
67+
span.setTag("record_ids", recordIds.join());
68+
}
69+
}
70+
71+
return { recordIds, eventType };
72+
});
73+
74+
const getAPIGatewayRequestId = tracer.wrap("getAPIGatewayRequestId", (event) => {
75+
let requestId;
76+
77+
if (event.requestContext) {
78+
requestId = event.requestContext.requestId;
79+
80+
const span = tracer.scope().active();
81+
82+
if (span) {
83+
span.setTag("api_gateway_request_id", requestId);
84+
span.setTag("event_type", "APIGateway");
85+
}
86+
}
87+
88+
return requestId;
89+
});
90+
91+
module.exports.handle = datadog(handle);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const { datadog, sendDistributionMetric } = require("datadog-lambda-js");
2+
3+
sendDistributionMetric(
4+
"serverless.integration_test.outside_handler",
5+
1,
6+
"tagkey:tagvalue",
7+
`eventsource:outside_handler`,
8+
);
9+
10+
async function handle(event, context) {
11+
const responsePayload = { message: "hello, dog!" };
12+
13+
if (event.requestContext) {
14+
responsePayload.eventType = "APIGateway";
15+
responsePayload.requestId = event.requestContext.requestId;
16+
}
17+
18+
if (event.Records) {
19+
responsePayload.recordIds = [];
20+
21+
event.Records.forEach((record) => {
22+
if (record.messageId) {
23+
responsePayload.eventType = "SQS";
24+
responsePayload.recordIds.push(record.messageId);
25+
sendDistributionMetric(
26+
"serverless.integration_test.records_processed",
27+
1,
28+
"tagkey:tagvalue",
29+
`eventsource:${responsePayload.eventType}`,
30+
);
31+
}
32+
if (record.Sns) {
33+
responsePayload.eventType = "SNS";
34+
responsePayload.recordIds.push(record.Sns.MessageId);
35+
sendDistributionMetric(
36+
"serverless.integration_test.records_processed",
37+
1,
38+
"tagkey:tagvalue",
39+
`eventsource:${responsePayload.eventType}`,
40+
);
41+
}
42+
});
43+
}
44+
45+
sendDistributionMetric(
46+
"serverless.integration_test.execution",
47+
1,
48+
"tagkey:tagvalue",
49+
`eventsource:${responsePayload.eventType}`,
50+
);
51+
52+
console.log(`Processed ${responsePayload.eventType} request`);
53+
54+
return responsePayload;
55+
}
56+
57+
module.exports.handle = datadog(handle);

0 commit comments

Comments
 (0)