Skip to content

Commit d128026

Browse files
test(otel): add Azure durable instrumentation unit coverage
Add helper, handler, and enablement specs for the OTel Azure Functions and Durable Functions instrumentation, plus AzureWebJobsStorage tracking in the test agent for orchestration store tests. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9fe549d commit d128026

5 files changed

Lines changed: 1248 additions & 0 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
'use strict'
2+
3+
const assert = require('node:assert/strict')
4+
const os = require('os')
5+
const path = require('path')
6+
7+
const { describe, it } = require('mocha')
8+
9+
const {
10+
buildSpanParentContext,
11+
buildSpanParentContextAsync,
12+
carrierFromTraceContext,
13+
extractContext,
14+
getInstanceId,
15+
getInvocationContext,
16+
runWithTraceContext,
17+
} = require('../../src/helpers/azure-trace-context')
18+
19+
describe('azure-trace-context', () => {
20+
describe('carrierFromTraceContext', () => {
21+
it('returns null when traceContext is missing', () => {
22+
assert.equal(carrierFromTraceContext(undefined), null)
23+
})
24+
25+
it('maps traceParent and traceState to W3C carrier keys', () => {
26+
assert.deepEqual(
27+
carrierFromTraceContext({
28+
traceParent: '00-abc-def-01',
29+
traceState: 'dd=s:1',
30+
}),
31+
{
32+
traceparent: '00-abc-def-01',
33+
tracestate: 'dd=s:1',
34+
},
35+
)
36+
})
37+
38+
it('returns null when no W3C fields are present', () => {
39+
assert.equal(carrierFromTraceContext({}), null)
40+
})
41+
})
42+
43+
describe('getInvocationContext', () => {
44+
it('reads HTTP invocation context from the second argument', () => {
45+
const ctx = { traceContext: { traceParent: '00-a-b-01' } }
46+
assert.equal(getInvocationContext([{}, ctx], 'http'), ctx)
47+
})
48+
49+
it('reads durable orchestration context from the first argument', () => {
50+
const ctx = { df: { isReplaying: false } }
51+
assert.equal(getInvocationContext([ctx], 'durable-orchestration'), ctx)
52+
})
53+
54+
it('reads durable activity context from any argument with traceContext', () => {
55+
const ctx = { traceContext: { traceParent: '00-a-b-01' } }
56+
assert.equal(getInvocationContext([ctx], 'durable-activity'), ctx)
57+
assert.equal(getInvocationContext(['input', ctx], 'durable-activity'), ctx)
58+
})
59+
})
60+
61+
describe('getInstanceId', () => {
62+
it('reads the durable instance id from traceContext attributes', () => {
63+
assert.equal(getInstanceId({
64+
traceContext: {
65+
attributes: {
66+
'durabletask.task.instance_id': 'abc123',
67+
},
68+
},
69+
}), 'abc123')
70+
})
71+
72+
it('falls back to the legacy DurableFunctionsInstanceId attribute', () => {
73+
assert.equal(getInstanceId({
74+
traceContext: {
75+
attributes: {
76+
DurableFunctionsInstanceId: 'legacy-id',
77+
},
78+
},
79+
}), 'legacy-id')
80+
})
81+
})
82+
83+
describe('buildSpanParentContext', () => {
84+
it('extracts HTTP parent context from the invocation context', () => {
85+
const ctx = { traceContext: { traceParent: '00-00000000000000000000000000000001-0000000000000004-01' } }
86+
const parentContext = buildSpanParentContext([{}, ctx], 'http')
87+
assert.ok(parentContext)
88+
})
89+
90+
it('reads generic orchestration context from the second argument', () => {
91+
const ctx = { traceContext: { traceParent: '00-a-b-01' } }
92+
assert.equal(getInvocationContext([{}, ctx], 'orchestration-generic'), ctx)
93+
})
94+
95+
it('parents activity spans to the in-flight orchestration span', () => {
96+
const api = require('@opentelemetry/api')
97+
const {
98+
registerOrchestrationSpan,
99+
unregisterOrchestrationSpan,
100+
} = require('../../src/helpers/otel-orchestration-registry')
101+
102+
const orchestrationSpan = {
103+
spanContext () {
104+
return {
105+
traceId: '00000000000000000000000000000001',
106+
spanId: '0000000000000002',
107+
traceFlags: 1,
108+
}
109+
},
110+
}
111+
112+
registerOrchestrationSpan('abc123', orchestrationSpan)
113+
114+
const activityContext = {
115+
traceContext: {
116+
traceParent: '00-00000000000000000000000000000001-0000000000000003-00',
117+
attributes: {
118+
'durabletask.task.instance_id': 'abc123',
119+
},
120+
},
121+
}
122+
123+
const parentContext = buildSpanParentContext(['input', activityContext], 'durable-activity')
124+
const span = api.trace.getSpan(parentContext)
125+
126+
assert.equal(span, orchestrationSpan)
127+
unregisterOrchestrationSpan('abc123')
128+
})
129+
})
130+
131+
describe('buildSpanParentContextAsync', () => {
132+
it('parents async activity spans to orchestration metadata from the store', async () => {
133+
const storeDir = path.join(
134+
os.tmpdir(),
135+
`dd-orch-async-${Date.now()}-${Math.random().toString(16).slice(2)}`,
136+
)
137+
process.env.DD_TRACE_AZURE_ORCHESTRATION_STORE_DIR = storeDir
138+
139+
const { publishOrchestrationMetaSync } = require('../../src/helpers/otel-orchestration-store')
140+
publishOrchestrationMetaSync('async-inst', {
141+
traceId: '00000000000000000000000000000001',
142+
spanId: '0000000000000002',
143+
startTime: Date.now(),
144+
status: 'open',
145+
})
146+
147+
const activityContext = {
148+
traceContext: {
149+
traceParent: '00-00000000000000000000000000000001-0000000000000003-01',
150+
attributes: {
151+
'durabletask.task.instance_id': 'async-inst',
152+
},
153+
},
154+
}
155+
156+
const parentContext = await buildSpanParentContextAsync(
157+
['input', activityContext],
158+
'durable-activity',
159+
)
160+
assert.ok(parentContext)
161+
})
162+
})
163+
164+
describe('runWithTraceContext', () => {
165+
it('runs the callback when traceContext is missing', () => {
166+
assert.equal(runWithTraceContext(undefined, () => 42), 42)
167+
})
168+
169+
it('runs the callback when traceContext is present', () => {
170+
const result = runWithTraceContext(
171+
{ traceParent: '00-00000000000000000000000000000000-0000000000000000-01' },
172+
() => 'ok',
173+
)
174+
assert.equal(result, 'ok')
175+
})
176+
})
177+
178+
describe('extractContext', () => {
179+
it('returns root context when traceContext is missing', () => {
180+
const root = extractContext(undefined)
181+
assert.ok(root)
182+
})
183+
})
184+
})

0 commit comments

Comments
 (0)